Skip to content

Commit 8001d9c

Browse files
committed
fix(release): harden workflow version handling and shell interpolation
1 parent 8aae9b6 commit 8001d9c

File tree

1 file changed

+76
-38
lines changed

1 file changed

+76
-38
lines changed

.github/workflows/release.yml

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
workflow_dispatch:
88
inputs:
99
version:
10-
description: 'Test version (e.g., 1.9.1-test)'
10+
description: 'Test version (e.g., 1.9.1-beta.1)'
1111
required: true
1212
type: string
1313

@@ -62,25 +62,43 @@ jobs:
6262
GH_EVENT_NAME: ${{ github.event_name }}
6363
GH_INPUT_VERSION: ${{ github.event.inputs.version }}
6464
run: |
65+
set -euo pipefail
66+
6567
if [ "$GH_EVENT_NAME" = "workflow_dispatch" ]; then
6668
VERSION="$GH_INPUT_VERSION"
67-
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
68-
echo "IS_TEST=true" >> $GITHUB_OUTPUT
69+
IS_TEST=true
70+
else
71+
VERSION=${GITHUB_REF#refs/tags/v}
72+
IS_TEST=false
73+
fi
74+
75+
# Validate version format before using it in later steps.
76+
if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+\.[0-9]+)?$ ]]; then
77+
echo "Invalid version format: $VERSION" >&2
78+
exit 1
79+
fi
80+
81+
{
82+
echo "VERSION<<EOF"
83+
echo "$VERSION"
84+
echo "EOF"
85+
} >> "$GITHUB_OUTPUT"
86+
echo "IS_TEST=$IS_TEST" >> "$GITHUB_OUTPUT"
87+
88+
if [ "$IS_TEST" = "true" ]; then
6989
echo "📝 Test version: $VERSION"
7090
# Update package.json version for test releases only
7191
npm version "$VERSION" --no-git-tag-version
7292
else
73-
VERSION=${GITHUB_REF#refs/tags/v}
74-
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
75-
echo "IS_TEST=false" >> $GITHUB_OUTPUT
7693
echo "🚀 Release version: $VERSION"
7794
# For tag-based releases, package.json was already updated by release script
7895
fi
7996
8097
- name: Resolve npm tag from version
8198
id: resolve_npm_tag
99+
env:
100+
VERSION: ${{ steps.get_version.outputs.VERSION }}
82101
run: |
83-
VERSION="${{ steps.get_version.outputs.VERSION }}"
84102
if [[ "$VERSION" == *"-beta"* ]]; then
85103
NPM_TAG="beta"
86104
elif [[ "$VERSION" == *"-alpha"* ]]; then
@@ -95,30 +113,35 @@ jobs:
95113
96114
- name: Generate GitHub release notes (production releases only)
97115
if: github.event_name == 'push'
116+
env:
117+
VERSION: ${{ steps.get_version.outputs.VERSION }}
98118
run: |
99119
node scripts/generate-github-release-notes.mjs \
100-
--version "${{ steps.get_version.outputs.VERSION }}" \
120+
--version "$VERSION" \
101121
--out github-release-body.md
102122
103123
- name: Create package
104124
run: npm pack
105125

106126
- name: Test publish (dry run for manual triggers)
107127
if: github.event_name == 'workflow_dispatch'
128+
env:
129+
NPM_TAG: ${{ steps.resolve_npm_tag.outputs.NPM_TAG }}
108130
run: |
109131
echo "🧪 Testing package creation (dry run)"
110-
npm publish --dry-run --access public --tag "${{ steps.resolve_npm_tag.outputs.NPM_TAG }}"
132+
npm publish --dry-run --access public --tag "$NPM_TAG"
111133
112134
- name: Publish to NPM (production releases only)
113135
if: github.event_name == 'push'
136+
env:
137+
VERSION: ${{ steps.get_version.outputs.VERSION }}
138+
NPM_TAG: ${{ steps.resolve_npm_tag.outputs.NPM_TAG }}
114139
run: |
115-
VERSION="${{ steps.get_version.outputs.VERSION }}"
116140
# Skip if this exact version is already published (idempotent reruns)
117141
if npm view xcodebuildmcp@"$VERSION" version >/dev/null 2>&1; then
118142
echo "✅ xcodebuildmcp@$VERSION already on NPM. Skipping publish."
119143
exit 0
120144
fi
121-
NPM_TAG="${{ steps.resolve_npm_tag.outputs.NPM_TAG }}"
122145
echo "📦 Publishing to NPM with tag: $NPM_TAG"
123146
npm publish --access public --tag "$NPM_TAG"
124147
@@ -135,14 +158,17 @@ jobs:
135158
prerelease: false
136159

137160
- name: Summary
161+
env:
162+
IS_TEST: ${{ steps.get_version.outputs.IS_TEST }}
163+
VERSION: ${{ steps.get_version.outputs.VERSION }}
138164
run: |
139-
if [ "${{ steps.get_version.outputs.IS_TEST }}" = "true" ]; then
140-
echo "🧪 Test completed for version: ${{ steps.get_version.outputs.VERSION }}"
165+
if [ "$IS_TEST" = "true" ]; then
166+
echo "🧪 Test completed for version: $VERSION"
141167
echo "Ready for production release!"
142168
else
143169
echo "🎉 Production release completed!"
144-
echo "Version: ${{ steps.get_version.outputs.VERSION }}"
145-
echo "📦 NPM: https://www.npmjs.com/package/xcodebuildmcp/v/${{ steps.get_version.outputs.VERSION }}"
170+
echo "Version: $VERSION"
171+
echo "📦 NPM: https://www.npmjs.com/package/xcodebuildmcp/v/$VERSION"
146172
echo "📚 MCP Registry: publish attempted in separate job (mcp_registry)"
147173
fi
148174
@@ -152,18 +178,15 @@ jobs:
152178
runs-on: ubuntu-latest
153179
env:
154180
MCP_DNS_PRIVATE_KEY: ${{ secrets.MCP_DNS_PRIVATE_KEY }}
181+
VERSION: ${{ needs.release.outputs.version }}
155182
steps:
156183
- name: Checkout code
157184
uses: actions/checkout@v4
158185
with:
159186
fetch-depth: 0
160187

161-
- name: Get version from tag
162-
id: get_version_mcp
163-
run: |
164-
VERSION=${GITHUB_REF#refs/tags/v}
165-
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
166-
echo "🚢 MCP publish for version: $VERSION"
188+
- name: Release version context
189+
run: echo "🚢 MCP publish for version: $VERSION"
167190

168191
- name: Missing secret — skip MCP publish
169192
if: env.MCP_DNS_PRIVATE_KEY == ''
@@ -238,12 +261,16 @@ jobs:
238261
run: npm ci --ignore-scripts
239262

240263
- name: Package portable artifact
264+
env:
265+
VERSION: ${{ needs.release.outputs.version }}
241266
run: |
242-
npm run package:macos -- --arch "${{ matrix.arch }}" --version "${{ needs.release.outputs.version }}"
267+
npm run package:macos -- --arch "${{ matrix.arch }}" --version "$VERSION"
243268
244269
- name: Verify portable artifact
270+
env:
271+
VERSION: ${{ needs.release.outputs.version }}
245272
run: |
246-
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-${{ matrix.arch }}.tar.gz"
273+
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${VERSION}-darwin-${{ matrix.arch }}.tar.gz"
247274
248275
- name: Upload arch artifact
249276
uses: actions/upload-artifact@v4
@@ -283,8 +310,9 @@ jobs:
283310

284311
- name: Expand per-arch archives
285312
id: expand_archives
313+
env:
314+
VERSION: ${{ needs.release.outputs.version }}
286315
run: |
287-
VERSION="${{ needs.release.outputs.version }}"
288316
ARM64_TGZ="dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz"
289317
X64_TGZ="dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz"
290318
ARM64_ROOT="dist/portable/unpacked/arm64/xcodebuildmcp-${VERSION}-darwin-arm64"
@@ -296,15 +324,19 @@ jobs:
296324
echo "X64_ROOT=$X64_ROOT" >> "$GITHUB_OUTPUT"
297325
298326
- name: Build universal portable artifact
327+
env:
328+
VERSION: ${{ needs.release.outputs.version }}
299329
run: |
300330
npm run package:macos:universal -- \
301-
--version "${{ needs.release.outputs.version }}" \
331+
--version "$VERSION" \
302332
--arm64-root "${{ steps.expand_archives.outputs.ARM64_ROOT }}" \
303333
--x64-root "${{ steps.expand_archives.outputs.X64_ROOT }}"
304334
305335
- name: Verify universal portable artifact
336+
env:
337+
VERSION: ${{ needs.release.outputs.version }}
306338
run: |
307-
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz"
339+
npm run verify:portable -- --archive "dist/portable/xcodebuildmcp-${VERSION}-darwin-universal.tar.gz"
308340
309341
- name: Upload universal artifact
310342
uses: actions/upload-artifact@v4
@@ -341,16 +373,18 @@ jobs:
341373
- name: Upload portable assets to GitHub Release
342374
env:
343375
GH_TOKEN: ${{ github.token }}
376+
VERSION: ${{ needs.release.outputs.version }}
377+
REPOSITORY: ${{ github.repository }}
344378
run: |
345-
gh release upload "v${{ needs.release.outputs.version }}" \
346-
dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz \
347-
dist/portable/arm64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-arm64.tar.gz.sha256 \
348-
dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz \
349-
dist/portable/x64/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-x64.tar.gz.sha256 \
350-
dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz \
351-
dist/portable/universal/xcodebuildmcp-${{ needs.release.outputs.version }}-darwin-universal.tar.gz.sha256 \
379+
gh release upload "v${VERSION}" \
380+
dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz \
381+
dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256 \
382+
dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz \
383+
dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256 \
384+
dist/portable/universal/xcodebuildmcp-${VERSION}-darwin-universal.tar.gz \
385+
dist/portable/universal/xcodebuildmcp-${VERSION}-darwin-universal.tar.gz.sha256 \
352386
--clobber \
353-
--repo "${{ github.repository }}"
387+
--repo "$REPOSITORY"
354388
355389
update_homebrew_tap:
356390
if: github.event_name == 'push'
@@ -385,9 +419,11 @@ jobs:
385419

386420
- name: Generate formula
387421
if: env.HOMEBREW_TAP_DEPLOY_KEY != ''
422+
env:
423+
VERSION: ${{ needs.release.outputs.version }}
424+
REPOSITORY: ${{ github.repository }}
388425
run: |
389-
VERSION="${{ needs.release.outputs.version }}"
390-
FORMULA_BASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}"
426+
FORMULA_BASE_URL="https://github.com/${REPOSITORY}/releases/download/v${VERSION}"
391427
ARM64_SHA="$(awk '{print $1}' dist/portable/arm64/xcodebuildmcp-${VERSION}-darwin-arm64.tar.gz.sha256)"
392428
X64_SHA="$(awk '{print $1}' dist/portable/x64/xcodebuildmcp-${VERSION}-darwin-x64.tar.gz.sha256)"
393429
npm run homebrew:formula -- \
@@ -405,9 +441,11 @@ jobs:
405441

406442
- name: Update tap repository
407443
if: env.HOMEBREW_TAP_DEPLOY_KEY != ''
444+
env:
445+
VERSION: ${{ needs.release.outputs.version }}
446+
REPOSITORY_OWNER: ${{ github.repository_owner }}
408447
run: |
409-
VERSION="${{ needs.release.outputs.version }}"
410-
git clone git@github.com:${{ github.repository_owner }}/homebrew-xcodebuildmcp.git tap-repo
448+
git clone git@github.com:${REPOSITORY_OWNER}/homebrew-xcodebuildmcp.git tap-repo
411449
mkdir -p tap-repo/Formula
412450
cp dist/homebrew/Formula/xcodebuildmcp.rb tap-repo/Formula/xcodebuildmcp.rb
413451
cd tap-repo

0 commit comments

Comments
 (0)