Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Release

on:
push:
tags:
- "v*"

jobs:
test:
name: Test
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Swift 6.2
uses: swift-actions/setup-swift@v2
with:
swift-version: "6.2"

- name: Run tests
run: swift test -Xswiftc -strict-concurrency=minimal

build-arm64:
name: Build (arm64)
needs: test
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Swift 6.2
uses: swift-actions/setup-swift@v2
with:
swift-version: "6.2"

- name: Build release binaries
run: scripts/build-release.sh --arch arm64 --version "${GITHUB_REF_NAME}" --dist-root dist

- name: Upload arm64 artifacts
uses: actions/upload-artifact@v4
with:
name: dist-arm64
path: dist/arm64
if-no-files-found: error

build-x86_64:
name: Build (x86_64)
needs: test
runs-on: macos-15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Swift 6.2
uses: swift-actions/setup-swift@v2
with:
swift-version: "6.2"

- name: Build release binaries
run: scripts/build-release.sh --arch x86_64 --version "${GITHUB_REF_NAME}" --dist-root dist

Comment on lines +47 to +62
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow builds the x86_64 slice on macos-13, but this repo’s Package.swift declares // swift-tools-version: 6.2 and platforms: [.macOS(.v15)]. Unless the job installs/selects a Swift 6.2 / Xcode 16 toolchain, swift build on macos-13 is likely to fail due to an incompatible default toolchain/SDK. Consider selecting an explicit Xcode version in the workflow (e.g., via xcode-select/a setup action) or adjusting the runner strategy so the x86_64 job uses a runner with a compatible toolchain.

Copilot uses AI. Check for mistakes.
- name: Upload x86_64 artifacts
uses: actions/upload-artifact@v4
with:
name: dist-x86_64
path: dist/x86_64
if-no-files-found: error

release:
name: Package and Publish
needs:
- build-arm64
- build-x86_64
runs-on: macos-15
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Swift 6.2
uses: swift-actions/setup-swift@v2
with:
swift-version: "6.2"

- name: Download arm64 artifacts
uses: actions/download-artifact@v4
with:
name: dist-arm64
path: dist/arm64

- name: Download x86_64 artifacts
uses: actions/download-artifact@v4
with:
name: dist-x86_64
path: dist/x86_64

- name: Package release archives
run: scripts/package-universal.sh --dist-root dist --output-dir release

- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
release/*.tar.gz
release/SHA256SUMS.txt
generate_release_notes: true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ DerivedData/
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
.codex/tmp/
/dist
/release
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,34 @@ See [Architecture](Docs/architecture.md) for the process overview.
swift run -c release xcode-mcp-proxy-install
```

### Install from GitHub Releases

Each release tag (`v*`) publishes:

- `xcode-mcp-proxy.tar.gz` (universal binary)
- `xcode-mcp-proxy-darwin-arm64.tar.gz`
- `xcode-mcp-proxy-darwin-x86_64.tar.gz`
- `SHA256SUMS.txt`

Example:

```bash
VERSION=v0.1.0
BASE_URL="https://github.com/<owner>/XcodeMCPKit/releases/download/${VERSION}"
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The download URL uses a literal <owner> placeholder, so the example commands will fail as written. Replace this with the real repository owner/org (or use a placeholder pattern that matches GitHub’s actual URL structure, e.g. <OWNER>/<REPO>), and ensure the repo name matches the actual GitHub repo slug users should install from.

Suggested change
BASE_URL="https://github.com/<owner>/XcodeMCPKit/releases/download/${VERSION}"
BASE_URL="https://github.com/<OWNER>/XcodeMCPKit/releases/download/${VERSION}"

Copilot uses AI. Check for mistakes.

ARCHIVE="xcode-mcp-proxy.tar.gz" # or: xcode-mcp-proxy-darwin-arm64.tar.gz / xcode-mcp-proxy-darwin-x86_64.tar.gz
curl -fL -O "${BASE_URL}/${ARCHIVE}"
curl -fL -O "${BASE_URL}/SHA256SUMS.txt"
shasum -a 256 -c SHA256SUMS.txt

tar -xzf "${ARCHIVE}"
mkdir -p "${HOME}/.local/bin"
cp bin/* "${HOME}/.local/bin/"
chmod +x "${HOME}/.local/bin/xcode-mcp-proxy" \
"${HOME}/.local/bin/xcode-mcp-proxy-server" \
"${HOME}/.local/bin/xcode-mcp-proxy-install"
```

Replace `xcrun mcpbridge` with one of the following:

### Codex
Expand Down
117 changes: 117 additions & 0 deletions scripts/build-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<'EOF'
Usage: scripts/build-release.sh --arch <arm64|x86_64> --version <tag> [--dist-root <dir>]

Builds release binaries and stages them under:
<dist-root>/<arch>/bin/
EOF
}

arch=""
version=""
dist_root="dist"

while [[ $# -gt 0 ]]; do
case "$1" in
--arch)
arch="${2:-}"
shift 2
;;
--version)
version="${2:-}"
shift 2
;;
--dist-root)
dist_root="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage
exit 1
;;
esac
done

if [[ -z "$arch" ]]; then
echo "--arch is required." >&2
usage
exit 1
fi

if [[ -z "$version" ]]; then
echo "--version is required." >&2
usage
exit 1
fi

case "$arch" in
arm64|x86_64) ;;
*)
echo "Unsupported arch: $arch (expected arm64 or x86_64)" >&2
exit 1
;;
esac

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [[ "$dist_root" = /* ]]; then
dist_base="$dist_root"
else
dist_base="$repo_root/$dist_root"
fi

out_dir="$dist_base/$arch"
bin_out="$out_dir/bin"
products=(
"xcode-mcp-proxy"
"xcode-mcp-proxy-server"
"xcode-mcp-proxy-install"
)

pushd "$repo_root" >/dev/null

for product in "${products[@]}"; do
swift build -c release \
-Xswiftc -strict-concurrency=minimal \
--arch "$arch" \
--product "$product"
done
Comment on lines +55 to +85
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--arch is only used to choose the output directory/manifest and is not used to control or validate the architecture produced by swift build. This can silently stage binaries that don’t match the requested arch (e.g., if run on an unexpected runner). Consider either inferring arch from uname -m, passing an explicit build option (like --arch/--triple if supported in your SwiftPM version), and/or validating the produced binaries (e.g., lipo -info/file) and failing if they don’t match.

Copilot uses AI. Check for mistakes.

bin_path="$(swift build -c release --arch "$arch" --show-bin-path)"
rm -rf "$out_dir"
mkdir -p "$bin_out"

for product in "${products[@]}"; do
source_path="$bin_path/$product"
if [[ ! -f "$source_path" ]]; then
source_path="$(find "$repo_root/.build" -type f -path "*/release/$product" | head -n 1 || true)"
fi
if [[ -z "$source_path" || ! -f "$source_path" ]]; then
echo "Failed to locate built binary: $product" >&2
exit 1
fi

target_path="$bin_out/$product"
cp "$source_path" "$target_path"
chmod +x "$target_path"
if command -v codesign >/dev/null 2>&1; then
codesign --remove-signature "$target_path" >/dev/null 2>&1 || true
fi
done

cat > "$out_dir/manifest.txt" <<EOF
version=$version
arch=$arch
built_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
EOF

popd >/dev/null

echo "Staged release binaries at: $out_dir"
129 changes: 129 additions & 0 deletions scripts/package-universal.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
cat <<'EOF'
Usage: scripts/package-universal.sh [--dist-root <dir>] [--output-dir <dir>]

Requires staged binaries from:
<dist-root>/arm64/bin/
<dist-root>/x86_64/bin/

Outputs:
<output-dir>/xcode-mcp-proxy.tar.gz
<output-dir>/xcode-mcp-proxy-darwin-arm64.tar.gz
<output-dir>/xcode-mcp-proxy-darwin-x86_64.tar.gz
<output-dir>/SHA256SUMS.txt
EOF
}

dist_root="dist"
output_dir="release"

while [[ $# -gt 0 ]]; do
case "$1" in
--dist-root)
dist_root="${2:-}"
shift 2
;;
--output-dir)
output_dir="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage
exit 1
;;
esac
done

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [[ "$dist_root" = /* ]]; then
dist_base="$dist_root"
else
dist_base="$repo_root/$dist_root"
fi
if [[ "$output_dir" = /* ]]; then
output_base="$output_dir"
else
output_base="$repo_root/$output_dir"
fi

arm_bin="$dist_base/arm64/bin"
x86_bin="$dist_base/x86_64/bin"
for path in "$arm_bin" "$x86_bin"; do
if [[ ! -d "$path" ]]; then
echo "Missing staged directory: $path" >&2
exit 1
fi
done

tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT

mkdir -p "$output_base"

universal_archive="$output_base/xcode-mcp-proxy.tar.gz"
arm_archive="$output_base/xcode-mcp-proxy-darwin-arm64.tar.gz"
x86_archive="$output_base/xcode-mcp-proxy-darwin-x86_64.tar.gz"
find "$output_base" -maxdepth 1 -type f -name 'xcode-mcp-proxy*.tar.gz' -delete
rm -f "$output_base/SHA256SUMS.txt"

products=(
"xcode-mcp-proxy"
"xcode-mcp-proxy-server"
"xcode-mcp-proxy-install"
)

for product in "${products[@]}"; do
arm_product="$arm_bin/$product"
x86_product="$x86_bin/$product"
if [[ ! -f "$arm_product" ]]; then
echo "Missing staged binary: $arm_product" >&2
exit 1
fi
if [[ ! -f "$x86_product" ]]; then
echo "Missing staged binary: $x86_product" >&2
exit 1
fi
done

mkdir -p "$tmp_dir/universal/bin"
for product in "${products[@]}"; do
target="$tmp_dir/universal/bin/$product"
lipo -create -output "$target" "$arm_bin/$product" "$x86_bin/$product"
chmod +x "$target"
if command -v codesign >/dev/null 2>&1; then
codesign --remove-signature "$target" >/dev/null 2>&1 || true
fi
done

cp -R "$tmp_dir/universal/bin" "$tmp_dir/bin"
tar -C "$tmp_dir" -czf "$universal_archive" bin
rm -rf "$tmp_dir/bin"

cp -R "$arm_bin" "$tmp_dir/bin"
tar -C "$tmp_dir" -czf "$arm_archive" bin
rm -rf "$tmp_dir/bin"

cp -R "$x86_bin" "$tmp_dir/bin"
tar -C "$tmp_dir" -czf "$x86_archive" bin
rm -rf "$tmp_dir/bin"

(
cd "$output_base"
shasum -a 256 \
xcode-mcp-proxy.tar.gz \
xcode-mcp-proxy-darwin-arm64.tar.gz \
xcode-mcp-proxy-darwin-x86_64.tar.gz > SHA256SUMS.txt
)

echo "Created release package: $universal_archive"
echo "Created release package: $arm_archive"
echo "Created release package: $x86_archive"
echo "Created checksum file: $output_base/SHA256SUMS.txt"