Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3908854
feat(runtime): Add portable resource root resolution
cameroncooke Feb 9, 2026
2c772a5
build(axe): Enforce signature checks in bundling
cameroncooke Feb 9, 2026
961a40d
build(portable): Add macOS portable packaging scripts
cameroncooke Feb 9, 2026
7d14c35
ci(release): Add macOS portable packaging pipeline
cameroncooke Feb 9, 2026
058b6f8
ci(homebrew): Automate tap formula updates
cameroncooke Feb 9, 2026
8365d56
fix(portable): Correct arch runtime packaging and brew install layout
cameroncooke Feb 9, 2026
41c4667
fix(build): Guard import.meta usage for CJS Smithery builds
cameroncooke Feb 9, 2026
228ecb4
docs(migration): Fix invalid CLI command reference
cameroncooke Feb 9, 2026
c5e0b21
fix(docs-check): Validate CLI refs only in code examples
cameroncooke Feb 9, 2026
d3e3431
ci(release): Allow isolated Homebrew tap updates on workflow_dispatch
cameroncooke Feb 9, 2026
4ba33a2
ci(release): Use env guard for Homebrew tap token checks
cameroncooke Feb 9, 2026
189af39
ci(release): Add npm tag for workflow_dispatch dry-run publish
cameroncooke Feb 9, 2026
96e4d1d
ci(release): Use macos-14 for both portable arch builds
cameroncooke Feb 9, 2026
255795f
ci(release): Handle artifact roots and empty tap bootstrap
cameroncooke Feb 9, 2026
355606f
ci(release): Build universal package from tar archives
cameroncooke Feb 9, 2026
dce9961
ci(release): Host test Homebrew artifacts from tap repo
cameroncooke Feb 10, 2026
0e3e3d7
fix(portable): Resolve symlinked bin path for Homebrew installs
cameroncooke Feb 10, 2026
6ab2959
build(portable): Prefer AXe Homebrew archive for bundling
cameroncooke Feb 10, 2026
6d60791
build(portable): Skip Gatekeeper check for unsigned AXe archive
cameroncooke Feb 10, 2026
05c78bc
build(portable): Allow unsigned AXe Homebrew assets
cameroncooke Feb 10, 2026
d49b724
build(axe): Remove machine-specific local AXe path
cameroncooke Feb 10, 2026
04c53f0
ci(release): Automate Homebrew deploys for tag releases
cameroncooke Feb 10, 2026
22d3d92
ci(portable): Skip execution checks for cross-arch artifacts
cameroncooke Feb 10, 2026
8a147fd
docs: Unify installation sections and update for v2.0.0 stable
cameroncooke Feb 10, 2026
2f7208d
chore(distribution): Remove Smithery integration and references
cameroncooke Feb 10, 2026
c537138
ci: Remove stale Smithery check from CI workflow
cameroncooke Feb 10, 2026
c9bd012
fix(release): Address PR review findings for portable packaging
cameroncooke Feb 10, 2026
5619bbd
fix(portable): Resolve symlinked Homebrew bin launch paths
cameroncooke Feb 10, 2026
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
5 changes: 1 addition & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,12 @@ jobs:
- name: Bundle AXe artifacts
run: npm run bundle:axe

- name: Build (Smithery)
- name: Build
run: npm run build

- name: Validate docs CLI command references
run: npm run docs:check

- name: Verify Smithery bundle
run: npm run verify:smithery-bundle

- name: Lint
run: npm run lint

Expand Down
260 changes: 235 additions & 25 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
release:
runs-on: macos-latest
environment: production
outputs:
version: ${{ steps.get_version.outputs.VERSION }}

steps:
- name: Checkout code
Expand Down Expand Up @@ -48,7 +50,7 @@ jobs:
- name: Bundle AXe artifacts
run: npm run bundle:axe

- name: Build Smithery bundle
- name: Build package
run: npm run build

- name: Run tests
Expand All @@ -72,6 +74,22 @@ jobs:
# For tag-based releases, package.json was already updated by release script
fi

- name: Resolve npm tag from version
id: resolve_npm_tag
run: |
VERSION="${{ steps.get_version.outputs.VERSION }}"
if [[ "$VERSION" == *"-beta"* ]]; then
NPM_TAG="beta"
elif [[ "$VERSION" == *"-alpha"* ]]; then
NPM_TAG="alpha"
elif [[ "$VERSION" == *"-rc"* ]]; then
NPM_TAG="rc"
else
NPM_TAG="latest"
fi
echo "NPM_TAG=$NPM_TAG" >> "$GITHUB_OUTPUT"
echo "Resolved npm tag: $NPM_TAG"

- name: Generate GitHub release notes (production releases only)
if: github.event_name == 'push'
run: |
Expand All @@ -86,7 +104,7 @@ jobs:
if: github.event_name == 'workflow_dispatch'
run: |
echo "🧪 Testing package creation (dry run)"
npm publish --dry-run --access public
npm publish --dry-run --access public --tag "${{ steps.resolve_npm_tag.outputs.NPM_TAG }}"

- name: Publish to NPM (production releases only)
if: github.event_name == 'push'
Expand All @@ -97,32 +115,10 @@ jobs:
echo "✅ xcodebuildmcp@$VERSION already on NPM. Skipping publish."
exit 0
fi
# Determine the appropriate npm tag based on version
if [[ "$VERSION" == *"-beta"* ]]; then
NPM_TAG="beta"
elif [[ "$VERSION" == *"-alpha"* ]]; then
NPM_TAG="alpha"
elif [[ "$VERSION" == *"-rc"* ]]; then
NPM_TAG="rc"
else
# For stable releases, explicitly use latest tag
NPM_TAG="latest"
fi
NPM_TAG="${{ steps.resolve_npm_tag.outputs.NPM_TAG }}"
echo "📦 Publishing to NPM with tag: $NPM_TAG"
npm publish --access public --tag "$NPM_TAG"

- name: Deploy to Smithery (production releases only)
if: github.event_name == 'push'
continue-on-error: true
env:
SMITHERY_TOKEN: ${{ secrets.SMITHERY_TOKEN }}
run: |
if [ -z "$SMITHERY_TOKEN" ]; then
echo "Missing SMITHERY_TOKEN secret for Smithery deploy."
exit 1
fi
npx smithery deploy --transport stdio

- name: Create GitHub Release (production releases only)
if: github.event_name == 'push'
uses: softprops/action-gh-release@v1
Expand Down Expand Up @@ -214,3 +210,217 @@ jobs:
delay=$((delay*2))
done
echo "✅ MCP Registry publish succeeded."

build_and_package_macos:
needs: release
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
runner: macos-14
- arch: x64
runner: macos-14
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Install dependencies
run: npm ci --ignore-scripts

- name: Package portable artifact
run: |
npm run package:macos -- --arch "${{ matrix.arch }}" --version "${{ needs.release.outputs.version }}"

- name: Verify portable artifact
run: |
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz"

- name: Upload arch artifact
uses: actions/upload-artifact@v4
with:
name: portable-${{ matrix.arch }}
path: |
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz.sha256

build_universal_and_verify:
needs: [release, build_and_package_macos]
runs-on: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Install dependencies
run: npm ci --ignore-scripts

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

- name: Download x64 artifact
uses: actions/download-artifact@v4
with:
name: portable-x64
path: dist/portable/x64

- name: Expand per-arch archives
id: expand_archives
run: |
VERSION="${{ needs.release.outputs.version }}"
ARM64_TGZ="dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz"
X64_TGZ="dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz"
ARM64_ROOT="dist/portable/unpacked/arm64/xcodebuildmcp-${VERSION}-darwin-arm64"
X64_ROOT="dist/portable/unpacked/x64/xcodebuildmcp-${VERSION}-darwin-x64"
mkdir -p dist/portable/unpacked/arm64 dist/portable/unpacked/x64
tar -xzf "$ARM64_TGZ" -C dist/portable/unpacked/arm64
tar -xzf "$X64_TGZ" -C dist/portable/unpacked/x64
echo "ARM64_ROOT=$ARM64_ROOT" >> "$GITHUB_OUTPUT"
echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT"

- name: Build universal portable artifact
run: |
npm run package:macos:universal -- \
--version "${{ needs.release.outputs.version }}" \
--arm64-root "${{ steps.expand_archives.outputs.ARM64_ROOT }}" \
--x64-root "${{ steps.expand_archives.outputs.X64_ROOT }}"

- name: Verify universal portable artifact
run: |
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz"

- name: Upload universal artifact
uses: actions/upload-artifact@v4
with:
name: portable-universal
path: |
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz
dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256

publish_portable_assets:
if: github.event_name == 'push'
needs: [release, build_and_package_macos, build_universal_and_verify]
runs-on: ubuntu-latest
steps:
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: portable-arm64
path: dist/portable/arm64

- name: Download x64 artifact
uses: actions/download-artifact@v4
with:
name: portable-x64
path: dist/portable/x64

- name: Download universal artifact
uses: actions/download-artifact@v4
with:
name: portable-universal
path: dist/portable/universal

- name: Upload portable assets to GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "v${{ needs.release.outputs.version }}" \
dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz \
dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz.sha256 \
dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz \
dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz.sha256 \
dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz \
dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 \
--clobber

update_homebrew_tap:
if: github.event_name == 'push'
needs: [release, build_and_package_macos, publish_portable_assets]
runs-on: ubuntu-latest
env:
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24'

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

- name: Download x64 artifact
uses: actions/download-artifact@v4
with:
name: portable-x64
path: dist/portable/x64

- name: Skip when tap token is unavailable
if: env.HOMEBREW_TAP_TOKEN == ''
run: echo "HOMEBREW_TAP_TOKEN is not set; skipping Homebrew tap update."

- name: Generate formula
if: env.HOMEBREW_TAP_TOKEN != ''
run: |
VERSION="${{ needs.release.outputs.version }}"
FORMULA_BASE_URL="https://github.com/cameroncooke/XcodeBuildMCP/releases/download/v${VERSION}"
ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)"
X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)"
npm run homebrew:formula -- \
--version "$VERSION" \
--arm64-sha "$ARM64_SHA" \
--x64-sha "$X64_SHA" \
--base-url "$FORMULA_BASE_URL" \
--out dist/homebrew/Formula/xcodebuildmcp.rb

- name: Update tap repository
if: env.HOMEBREW_TAP_TOKEN != ''
env:
GH_TOKEN: ${{ env.HOMEBREW_TAP_TOKEN }}
run: |
VERSION="${{ needs.release.outputs.version }}"
DEFAULT_BRANCH="main"
gh repo clone cameroncooke/homebrew-xcodebuildmcp tap-repo
mkdir -p tap-repo/Formula
cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb
cd tap-repo
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
DETECTED_BRANCH="$(gh repo view cameroncooke/homebrew-xcodebuildmcp --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null || true)"
if [ -n "$DETECTED_BRANCH" ]; then
DEFAULT_BRANCH="$DETECTED_BRANCH"
fi
if ! git rev-parse --verify HEAD >/dev/null 2>&1; then
echo "Tap repo has no commits; bootstrapping ${DEFAULT_BRANCH}."
git checkout -b "$DEFAULT_BRANCH"
else
git checkout "$DEFAULT_BRANCH"
git pull --ff-only origin "$DEFAULT_BRANCH"
fi
git add Formula/xcodebuildmcp.rb
if git diff --cached --quiet; then
echo "Formula already up to date; nothing to push."
exit 0
fi
git commit -m "xcodebuildmcp ${VERSION}"
git push origin "$DEFAULT_BRANCH"
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ buildServer.json

# Bundled AXe artifacts (generated during build)
bundled/
.smithery/

/.mcpregistry_github_token
/.mcpregistry_registry_token
Expand Down
Loading
Loading