-
Notifications
You must be signed in to change notification settings - Fork 516
346 lines (311 loc) · 13 KB
/
Copy pathrelease.yml
File metadata and controls
346 lines (311 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# .github/workflows/release.yml
name: Auto Release
on:
push:
branches: [main]
paths: ['pyproject.toml'] # Only trigger when pyproject.toml changes
# Every job then declares what it needs on top of this. Nothing here may run
# third-party code and hold a credential it does not need: a job with
# id-token: write can mint an OIDC token for PyPI, so anything executing a
# build backend, an npm package or a Dockerfile stays in a job without it.
permissions:
contents: read
jobs:
check-version-bump:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
should-release: ${{ steps.check.outputs.should-release }}
new-version: ${{ steps.check.outputs.new-version }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
fetch-depth: 2 # Need to compare with previous commit
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
- name: Check if version was bumped
id: check
run: |
# Get current version
CURRENT_VERSION=$(uv version | cut -d' ' -f2)
echo "Current version: $CURRENT_VERSION"
# Get previous version from git (before this commit)
git checkout HEAD~1 -- pyproject.toml || true
PREVIOUS_VERSION=$(uv version | cut -d' ' -f2) 2>/dev/null || echo "0.0.0"
git checkout HEAD -- pyproject.toml
echo "Previous version: $PREVIOUS_VERSION"
# Check if version actually changed
if [[ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]]; then
echo "✅ Version bump detected: $PREVIOUS_VERSION → $CURRENT_VERSION"
echo "should-release=true" >> $GITHUB_OUTPUT
echo "new-version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
else
echo "ℹ️ No version change detected"
echo "should-release=false" >> $GITHUB_OUTPUT
fi
# Builds the distributions without any publishing or administration
# credentials: the build backend is third-party code and must not run where
# it could reach the OIDC identity. Its output travels to the publishing job
# as an artifact.
build:
needs: check-version-bump
if: needs.check-version-bump.outputs.should-release == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Set up uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# build-constraints.txt pins the build backend to a hashed version, so a
# tampered setuptools release cannot slip into the distributions. The
# requirement in pyproject.toml stays a lower bound, because an exact pin
# there would be baked into every published sdist.
- name: Build package distributions
run: |
uv build --no-sources --build-constraint build-constraints.txt --require-hashes
echo "Built package distributions:"
ls -lh dist/
- name: Optimize uv cache for CI
run: uv cache prune --ci
# The upload is not the last thing the job does: a post step such as the
# uv cache save can still fail it. Artifacts are immutable, so overwrite
# keeps a retry from tripping over the one the failed attempt wrote.
- name: Upload distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: python-package-distributions
path: dist/
if-no-files-found: error
overwrite: true
retention-days: 7
# PyPI is the cutover-critical artifact: publish first so a failure in any
# later job (Docker build, MCPB pack) cannot leave the README pointing at a
# package version that does not exist yet.
#
# This job holds the OIDC identity of the project, so it runs no repository
# code: no checkout, no build, only the artifact download and the upload.
publish-pypi:
needs: [check-version-bump, build]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/mcp-server-linkedin/${{ needs.check-version-bump.outputs.new-version }}/
permissions:
id-token: write # Required for PyPI Trusted Publishing
steps:
- name: Download distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: python-package-distributions
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
print-hash: true
verbose: true
skip-existing: true
# Writes the released version into the repository and tags it. Runs nothing
# but checkout and the tools the runner ships with, so the admin token never
# shares a job with a package manager.
prepare-release:
needs: [check-version-bump, publish-pypi]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.check-version-bump.outputs.new-version }}
permissions:
contents: write
outputs:
release-sha: ${{ steps.release-sha.outputs.release-sha }}
steps:
# The only checkout that keeps its credentials: this job pushes the
# version commit and the tag. fetch-depth: 0 brings the existing tags in
# for the duplicate check below.
- name: Checkout code
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 # zizmor: ignore[artipacked]
with:
fetch-depth: 0
- name: Update manifest.json and docker-compose.yml version
run: |
set -e
sed -i 's/"version": ".*"/"version": "'$VERSION'"/' manifest.json
sed -i 's/stickerdaniel\/linkedin-mcp-server:[^ ]*/stickerdaniel\/linkedin-mcp-server:'$VERSION'/' docker-compose.yml
echo "✅ Updated manifest.json and docker-compose.yml to version $VERSION"
- name: Remove branch protection (temporary)
run: |
gh api repos/${{ github.repository }}/branches/main/protection \
--method DELETE
env:
GH_TOKEN: ${{ secrets.GH_ADMIN_TOKEN }}
- name: Commit version updates
run: |
set -e
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add manifest.json docker-compose.yml
if git diff --staged --quiet; then
echo "ℹ️ No changes to commit"
else
git commit -m "chore: update manifest.json and docker-compose.yml to v$VERSION [skip ci]"
git push origin main
echo "✅ Committed version updates"
fi
- name: Restore branch protection
if: always()
env:
GH_TOKEN: ${{ secrets.GH_ADMIN_TOKEN }}
PAYLOAD: >-
{
"required_status_checks": {
"strict": true,
"checks": [
{"context": "lint-and-check", "app_id": 15368},
{"context": "test", "app_id": 15368}
]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"dismiss_stale_reviews": false,
"require_code_owner_reviews": false,
"required_approving_review_count": 0
},
"restrictions": null
}
run: |
echo "$PAYLOAD" | gh api repos/${{ github.repository }}/branches/main/protection \
--method PUT \
--input -
- name: Create release tag
run: |
set -e
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
echo "⚠️ Tag v$VERSION already exists, skipping tag creation"
else
git tag "v$VERSION"
git push origin "v$VERSION"
echo "✅ Created and pushed tag v$VERSION"
fi
# The Docker image and the MCP bundle are built from the commit that
# carries the updated manifest.json and docker-compose.yml, so the jobs
# below check out this SHA instead of the one that triggered the run.
- name: Publish the released commit
id: release-sha
run: echo "release-sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
docker:
needs: [check-version-bump, prepare-release]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.check-version-bump.outputs.new-version }}
permissions:
contents: read
steps:
- name: Checkout released commit
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.prepare-release.outputs.release-sha }}
persist-credentials: false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
- name: Log in to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker images
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: .
push: true
tags: |
stickerdaniel/linkedin-mcp-server:${{ env.VERSION }}
stickerdaniel/linkedin-mcp-server:latest
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: stickerdaniel/linkedin-mcp-server
readme-filepath: docs/docker-hub.md
build-mcpb:
needs: [check-version-bump, prepare-release]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.check-version-bump.outputs.new-version }}
MCPB_VERSION: 2.1.2
permissions:
contents: read
steps:
- name: Checkout released commit
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
ref: ${{ needs.prepare-release.outputs.release-sha }}
persist-credentials: false
- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
- name: Validate and build MCP bundle
run: |
bunx "@anthropic-ai/mcpb@$MCPB_VERSION" validate manifest.json
bunx "@anthropic-ai/mcpb@$MCPB_VERSION" pack .
mv linkedin-mcp-server.mcpb linkedin-mcp-server-v$VERSION.mcpb
- name: Generate release notes
run: |
envsubst < RELEASE_NOTES_TEMPLATE.md > RELEASE_NOTES.md
echo "✅ Generated release notes from template"
# Overwrites for the same reason as the distributions upload: the bun
# cache save runs after this and can fail the job around it.
- name: Upload release assets
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: github-release-assets
path: |
linkedin-mcp-server-v${{ env.VERSION }}.mcpb
RELEASE_NOTES.md
if-no-files-found: error
overwrite: true
retention-days: 7
# Waits for Docker so the release never announces an image that failed to
# push. Runs no repository code, only the artifact download and the release.
create-github-release:
needs: [check-version-bump, docker, build-mcpb]
runs-on: ubuntu-latest
env:
VERSION: ${{ needs.check-version-bump.outputs.new-version }}
permissions:
contents: write
steps:
- name: Download release assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: github-release-assets
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3
with:
tag_name: v${{ env.VERSION }}
files: |
*.mcpb
generate_release_notes: true
draft: false
prerelease: false
name: "v${{ env.VERSION }}"
body_path: RELEASE_NOTES.md
- name: Summary
run: |
echo "Successfully released v$VERSION!"
echo "Docker: stickerdaniel/linkedin-mcp-server:$VERSION"
echo "PyPI: https://pypi.org/project/mcp-server-linkedin/$VERSION/"
echo "GitHub: https://github.com/${{ github.repository }}/releases/tag/v$VERSION"