-
Notifications
You must be signed in to change notification settings - Fork 0
159 lines (124 loc) · 5.39 KB
/
release.yaml
File metadata and controls
159 lines (124 loc) · 5.39 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
# This workflow MUST be audited with zizmor.
# This workflow SHOULD be using pinned action refs.
# (I use 'pinact' but that's an implementation detail.)
# Dev note: copied liberally from zizmor's release workflow, as "probably current best practice".
# We can simplify the PyPI step though as we're pure Python with no compilation steps needed.
#
# And for the uv setup/invocations, refer to <https://docs.astral.sh/uv/guides/integration/github/>
name: Release PyPI mcp-test-driver
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
# The tag should still exist, but use this if some transient error means that
# we failed to start, or complete.
#
# If we did start and it was just a failure to upload, you should consider
# re-running from the failed jobs in the existing action run, instead
# of a new run.
description: "The tag to release (e.g., v1.0.0)"
required: true
permissions: {}
# We don't want to interrupt an existing flow, and we do (or should!) have a
# tag protection ruleset preventing updates/deletions/force-pushes for v*
#
# But if something goes wrong where I want to manually create a release, we do
# have workflow_dispatch. For that, we don't cancel and we don't guard on a
# release existing. My assumption is that the draft release will have been created
# but a transient error blocked the release (PyPI service outage or somesuch).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
plan-release:
name: Plan release
runs-on: ubuntu-slim
permissions: {}
outputs:
release-tag: ${{ steps.determine-tag.outputs.RELEASE_TAG }}
steps:
- name: Determine release tag
id: determine-tag
run: echo "RELEASE_TAG=${RELEASE_TAG}" >> "${GITHUB_OUTPUT}"
env:
RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }}
draft-release:
needs: plan-release
name: Draft GitHub release for ${{ needs.plan-release.outputs.release-tag }}
runs-on: ubuntu-slim
permissions:
contents: write # to create the release
steps:
- name: Create draft release
run: |
# NOTE: No quotes around PRERELEASE_FLAG so that it expands to either `--prerelease` or an empty string,
# rather than an empty argument.
set -e # no 'u' (even though PRERELEASE_FLAG should always exist, just usually be empty)
if ! gh release create --repo "$GITHUB_REPOSITORY" --draft --verify-tag ${PRERELEASE_FLAG} "${RELEASE_TAG}" --title "${RELEASE_TAG}"
then
# record some information about the already-existing release in our output
gh release view --json id,tagName,createdAt,isDraft,isPrerelease,isImmutable "${RELEASE_TAG}"
# if that fails, then it doesn't exist and we failed to create, so abort under `-set -e` is correct
# Now, refuse to proceed if the release is no longer draft
gh release view --json isDraft "${RELEASE_TAG}" | jq -er .isDraft
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_TAG: ${{ needs.plan-release.outputs.release-tag }}
PRERELEASE_FLAG: ${{ contains(needs.plan-release.outputs.release-tag, '-rc') && '--prerelease' || '' }}
build:
name: Build release assets
runs-on: ubuntu-slim
permissions:
contents: read # to clone repo
# it seems actions:write for CI artifacts is not needed, it's implicit from $ACTIONS_RUNTIME_TOKEN instead of $GITHUB_TOKEN
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.10.12"
enable-cache: false
- name: "Set up Python"
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version-file: "pyproject.toml"
- name: Install the project
run: uv sync --locked --all-extras --dev
- name: Build
run: uv build --no-sources
- name: Test across Python versions
run: uv run tox
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: dist
path: dist/
pypi-publish:
name: Publish to PyPI for ${{ needs.plan-release.outputs.release-tag }} 🐍
needs: [plan-release, draft-release, build]
permissions:
id-token: write # for Trusted Publishing
# future: attestations:write with GH attestations via actions/attest-build-provenance?
runs-on: ubuntu-slim
environment: pypi
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- name: Install uv
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
with:
version: "0.10.12"
# For a release workflow, we disable the cache, to avoid propagating a poisoned cache.
enable-cache: false
# pinact can't handle @release/v1 so let's use 'uv publish' instead.
# - name: Publish package distributions to PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
- name: Publish to PyPI 🐍
run: uv publish --trusted-publishing=always