Skip to content
Merged
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
123 changes: 82 additions & 41 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Test version (e.g., 1.9.1-test)'
description: 'Test version (e.g., 1.9.1-beta or 1.9.1-beta.1)'
required: true
type: string

Expand Down Expand Up @@ -58,26 +58,47 @@ jobs:

- name: Get version from tag or input
id: get_version
env:
GH_EVENT_NAME: ${{ github.event_name }}
GH_INPUT_VERSION: ${{ github.event.inputs.version }}
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "IS_TEST=true" >> $GITHUB_OUTPUT
set -euo pipefail

if [ "$GH_EVENT_NAME" = "workflow_dispatch" ]; then
VERSION="$GH_INPUT_VERSION"
IS_TEST=true
else
VERSION=${GITHUB_REF#refs/tags/v}
IS_TEST=false
fi

# Validate version format before using it in later steps.
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "Invalid version format: $VERSION" >&2
exit 1
fi

{
echo "VERSION<<EOF"
echo "$VERSION"
echo "EOF"
} >> "$GITHUB_OUTPUT"
echo "IS_TEST=$IS_TEST" >> "$GITHUB_OUTPUT"

if [ "$IS_TEST" = "true" ]; then
echo "📝 Test version: $VERSION"
# Update package.json version for test releases only
npm version $VERSION --no-git-tag-version
npm version "$VERSION" --no-git-tag-version
else
VERSION=${GITHUB_REF#refs/tags/v}
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "IS_TEST=false" >> $GITHUB_OUTPUT
echo "🚀 Release version: $VERSION"
# For tag-based releases, package.json was already updated by release script
fi

- name: Resolve npm tag from version
id: resolve_npm_tag
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
VERSION="${{ steps.get_version.outputs.VERSION }}"
if [[ "$VERSION" == *"-beta"* ]]; then
NPM_TAG="beta"
elif [[ "$VERSION" == *"-alpha"* ]]; then
Expand All @@ -92,30 +113,35 @@ jobs:

- name: Generate GitHub release notes (production releases only)
if: github.event_name == 'push'
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
node scripts/generate-github-release-notes.mjs \
--version "${{ steps.get_version.outputs.VERSION }}" \
--version "$VERSION" \
--out github-release-body.md

- name: Create package
run: npm pack

- name: Test publish (dry run for manual triggers)
if: github.event_name == 'workflow_dispatch'
env:
NPM_TAG: ${{ steps.resolve_npm_tag.outputs.NPM_TAG }}
run: |
echo "🧪 Testing package creation (dry run)"
npm publish --dry-run --access public --tag "${{ steps.resolve_npm_tag.outputs.NPM_TAG }}"
npm publish --dry-run --access public --tag "$NPM_TAG"

- name: Publish to NPM (production releases only)
if: github.event_name == 'push'
env:
VERSION: ${{ steps.get_version.outputs.VERSION }}
NPM_TAG: ${{ steps.resolve_npm_tag.outputs.NPM_TAG }}
run: |
VERSION="${{ steps.get_version.outputs.VERSION }}"
# Skip if this exact version is already published (idempotent reruns)
if npm view xcodebuildmcp@"$VERSION" version >/dev/null 2>&1; then
echo "✅ xcodebuildmcp@$VERSION already on NPM. Skipping publish."
exit 0
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"

Expand All @@ -132,14 +158,17 @@ jobs:
prerelease: false

- name: Summary
env:
IS_TEST: ${{ steps.get_version.outputs.IS_TEST }}
VERSION: ${{ steps.get_version.outputs.VERSION }}
run: |
if [ "${{ steps.get_version.outputs.IS_TEST }}" = "true" ]; then
echo "🧪 Test completed for version: ${{ steps.get_version.outputs.VERSION }}"
if [ "$IS_TEST" = "true" ]; then
echo "🧪 Test completed for version: $VERSION"
echo "Ready for production release!"
else
echo "🎉 Production release completed!"
echo "Version: ${{ steps.get_version.outputs.VERSION }}"
echo "📦 NPM: https://www.npmjs.com/package/xcodebuildmcp/v/${{ steps.get_version.outputs.VERSION }}"
echo "Version: $VERSION"
echo "📦 NPM: https://www.npmjs.com/package/xcodebuildmcp/v/$VERSION"
echo "📚 MCP Registry: publish attempted in separate job (mcp_registry)"
fi

Expand All @@ -149,18 +178,15 @@ jobs:
runs-on: ubuntu-latest
env:
MCP_DNS_PRIVATE_KEY: ${{ secrets.MCP_DNS_PRIVATE_KEY }}
VERSION: ${{ needs.release.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get version from tag
id: get_version_mcp
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "🚢 MCP publish for version: $VERSION"
- name: Release version context
run: echo "🚢 MCP publish for version: $VERSION"

- name: Missing secret — skip MCP publish
if: env.MCP_DNS_PRIVATE_KEY == ''
Expand Down Expand Up @@ -235,12 +261,16 @@ jobs:
run: npm ci --ignore-scripts

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

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

- name: Upload arch artifact
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -280,8 +310,9 @@ jobs:

- name: Expand per-arch archives
id: expand_archives
env:
VERSION: ${{ needs.release.outputs.version }}
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"
Expand All @@ -293,15 +324,19 @@ jobs:
echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT"

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

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

- name: Upload universal artifact
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -338,16 +373,18 @@ jobs:
- name: Upload portable assets to GitHub Release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.release.outputs.version }}
REPOSITORY: ${{ github.repository }}
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 \
gh release upload "v${VERSION}" \
dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz \
dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256 \
dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz \
dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256 \
dist/portable/universal/xcodebuildmcp-${VERSION}-darwin-universal.tar.gz \
dist/portable/universal/xcodebuildmcp-${VERSION}-darwin-universal.tar.gz.sha256 \
--clobber \
--repo "${{ github.repository }}"
--repo "$REPOSITORY"

update_homebrew_tap:
if: github.event_name == 'push'
Expand Down Expand Up @@ -382,9 +419,11 @@ jobs:

- name: Generate formula
if: env.HOMEBREW_TAP_DEPLOY_KEY != ''
env:
VERSION: ${{ needs.release.outputs.version }}
REPOSITORY: ${{ github.repository }}
run: |
VERSION="${{ needs.release.outputs.version }}"
FORMULA_BASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}"
FORMULA_BASE_URL="https://github.com/${REPOSITORY}/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 -- \
Expand All @@ -402,9 +441,11 @@ jobs:

- name: Update tap repository
if: env.HOMEBREW_TAP_DEPLOY_KEY != ''
env:
VERSION: ${{ needs.release.outputs.version }}
REPOSITORY_OWNER: ${{ github.repository_owner }}
run: |
VERSION="${{ needs.release.outputs.version }}"
git clone git@github.com:${{ github.repository_owner }}/homebrew-xcodebuildmcp.git tap-repo
git clone git@github.com:${REPOSITORY_OWNER}/homebrew-xcodebuildmcp.git tap-repo
mkdir -p tap-repo/Formula
cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb
cd tap-repo
Expand Down
10 changes: 5 additions & 5 deletions scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ EOF

# Function to get the highest version from git tags
get_highest_version() {
git tag | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?$' | sed 's/^v//' | sort -V | tail -1
git tag | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$' | sed 's/^v//' | sort -V | tail -1
}

# Function to parse version components
Expand Down Expand Up @@ -92,9 +92,9 @@ bump_version() {
# Function to validate version format
validate_version() {
local version=$1
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?$ ]]; then
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$ ]]; then
echo "❌ Invalid version format: $version"
echo "Version must be in format: x.y.z or x.y.z-tag.n (e.g., 1.4.0 or 1.4.0-beta.3)"
echo "Version must be in format: x.y.z or x.y.z-prerelease (e.g., 1.4.0, 1.4.0-beta, 1.4.0-beta.3)"
return 1
fi
return 0
Expand Down Expand Up @@ -427,8 +427,8 @@ if [[ "$SKIP_VERSION_UPDATE" == "false" ]]; then
# README update
echo ""
echo "📝 Updating install tags in README.md and docs/GETTING_STARTED.md..."
README_AT_TAG_REGEX='xcodebuildmcp@([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?|latest|beta|alpha)'
README_URLENCODED_AT_TAG_REGEX='xcodebuildmcp%40([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?|latest|beta|alpha)'
README_AT_TAG_REGEX='xcodebuildmcp@([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?|latest|beta|alpha)'
README_URLENCODED_AT_TAG_REGEX='xcodebuildmcp%40([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?|latest|beta|alpha)'
run sed_inplace "s/${README_AT_TAG_REGEX}/xcodebuildmcp@${NPM_TAG}/g" README.md
run sed_inplace "s/${README_AT_TAG_REGEX}/xcodebuildmcp@${NPM_TAG}/g" docs/GETTING_STARTED.md
run sed_inplace "s/${README_URLENCODED_AT_TAG_REGEX}/xcodebuildmcp%40${NPM_TAG}/g" README.md
Expand Down
Loading