44 workflow_dispatch :
55 inputs :
66 version :
7- description : ' Exact version (e.g. 0.6.0). Takes precedence over bump_type.'
7+ description : ' Exact version (e.g. 0.6.0). Leave empty to auto-calculate from bump_type.'
88 required : false
99 type : string
1010 bump_type :
11- description : ' Version bump type (used if version is empty)'
11+ description : ' Version bump type (used when version is empty)'
1212 required : false
1313 type : choice
1414 default : patch
2323
2424permissions :
2525 id-token : write # MCP Registry OIDC authentication
26- contents : write # Git tags, releases, push version bump
26+ contents : write # Git tags and GitHub Releases
2727 packages : write # Docker push to GHCR
2828
2929jobs :
3030 release :
3131 name : Release
3232 runs-on : ubuntu-latest
33- # Requires the "release" environment — configure protection rules in
34- # Settings > Environments to restrict to admins/maintainers
3533 environment : release
3634 steps :
3735 - name : Checkout repository
5856 - name : Calculate version
5957 id : version
6058 run : |
61- CURRENT=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
59+ # Derive current version from the latest git tag (single source of truth)
60+ CURRENT=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1 | sed 's/^v//')
61+ if [ -z "$CURRENT" ]; then
62+ CURRENT="0.0.0"
63+ echo "::warning::No existing version tags found, starting from 0.0.0"
64+ fi
6265 echo "current=$CURRENT" >> $GITHUB_OUTPUT
6366
6467 if [ -n "${{ inputs.version }}" ]; then
@@ -73,25 +76,32 @@ jobs:
7376 esac
7477 fi
7578
79+ # Validate version format
80+ if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
81+ echo "::error::Invalid version format: $VERSION (expected X.Y.Z)"
82+ exit 1
83+ fi
84+
85+ # Check if tag already exists
7686 if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
77- echo "::error::Tag v$VERSION already exists"
87+ echo "::error::Tag v$VERSION already exists. Delete it first or choose a different version. "
7888 exit 1
7989 fi
8090
8191 echo "version=$VERSION" >> $GITHUB_OUTPUT
82- echo "### 🚀 Releasing $CURRENT → $VERSION" >> $GITHUB_STEP_SUMMARY
92+ echo "### Releasing $CURRENT → $VERSION" >> $GITHUB_STEP_SUMMARY
8393
84- - name : Update pyproject.toml version
94+ - name : Prepare server.json for publish
8595 run : |
86- sed -i 's/^version = ".*"/version = " ${{ steps.version.outputs.version }}"/' pyproject.toml
96+ VERSION=" ${{ steps.version.outputs.version }}"
8797
88- - name : Update server.json version
89- run : |
98+ # Update server.json in working directory (not committed)
99+ # pyproject.toml uses setuptools-scm — version comes from Docker build arg
90100 python -c "
91101 import json
92102 with open('server.json', 'r') as f:
93103 data = json.load(f)
94- version = '${{ steps.version.outputs.version }} '
104+ version = '$VERSION '
95105 data['version'] = version
96106 if 'packages' in data:
97107 for package in data['packages']:
@@ -106,6 +116,8 @@ jobs:
106116 f.write('\n')
107117 "
108118
119+ echo "Updated server.json to $VERSION (working dir only)"
120+
109121 - name : Validate server.json
110122 run : |
111123 python -c "
@@ -117,7 +129,7 @@ jobs:
117129 with open('server.json', 'r') as f:
118130 data = json.load(f)
119131 validate(instance=data, schema=schema)
120- print('✓ server.json validation passed')
132+ print('server.json validation passed')
121133 "
122134
123135 - name : Set up Docker Buildx
@@ -136,6 +148,7 @@ jobs:
136148 push : true
137149 platforms : linux/amd64,linux/arm64
138150 file : ./Dockerfile
151+ build-args : VERSION=${{ steps.version.outputs.version }}
139152 tags : |
140153 ${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}
141154 ${{ env.IMAGE_NAME }}:v${{ steps.version.outputs.version }}
@@ -145,39 +158,34 @@ jobs:
145158 cache-from : type=gha
146159 cache-to : type=gha
147160
148- # Git operations happen AFTER Docker push — if Docker fails, repo stays clean
149- - name : Commit version bump and create tag
161+ # Git tag created AFTER Docker push succeeds — if Docker fails, no stale tag
162+ - name : Create and push git tag
150163 run : |
151164 git config user.name "github-actions[bot]"
152165 git config user.email "github-actions[bot]@users.noreply.github.com"
153- git add pyproject.toml server.json
154- git commit -m "chore: release v${{ steps.version.outputs.version }} [skip ci]"
155- git tag "v${{ steps.version.outputs.version }}"
156- git push origin main --tags
166+ git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}"
167+ git push origin "v${{ steps.version.outputs.version }}"
157168
158169 - name : Install MCP Publisher CLI
159170 run : |
160171 curl -L "https://github.com/modelcontextprotocol/registry/releases/download/v1.5.0/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
161172 chmod +x mcp-publisher
162- ./mcp-publisher version || true
163173
164174 - name : Login to MCP Registry (GitHub OIDC)
165- run : |
166- ./mcp-publisher login github-oidc
175+ run : ./mcp-publisher login github-oidc
167176
168177 - name : Publish to MCP Registry
169- run : |
170- ./mcp-publisher publish
178+ run : ./mcp-publisher publish
171179
172180 - name : Create GitHub Release
173- uses : softprops/action-gh-release@v1
181+ uses : softprops/action-gh-release@v2
174182 with :
175183 tag_name : v${{ steps.version.outputs.version }}
176184 name : CodeAlive MCP v${{ steps.version.outputs.version }}
177185 body : |
178186 ## CodeAlive MCP Server v${{ steps.version.outputs.version }}
179187
180- ### 🚀 Deployment Options
188+ ### Deployment Options
181189
182190 **Docker Container (Local)**
183191 ```bash
0 commit comments