Repository navigation
292 lines (273 loc) · 10.6 KB
/
Copy pathpublish.yml
File metadata and controls
292 lines (273 loc) · 10.6 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
name: Release pyQuil
# Tags the version in pyproject.toml, drafts its GitHub release from
# CHANGELOG.md, and publishes to PyPI and DockerHub.
# See CONTRIBUTING.md, "Release Process".
#
# This file's name and the `pypi` environment are both part of the PyPI trusted
# publisher configuration, which matches the workflow a run starts from.
# Renaming either stops publishing until the publisher is updated:
# https://pypi.org/manage/project/pyquil/settings/publishing/
on:
push:
branches: [master]
workflow_dispatch:
inputs:
version:
description: "Re-publish this already-released version, without a leading v (e.g. 4.19.0)"
required: true
type: string
publish-docker:
required: false
type: boolean
default: true
publish-grpc-web:
required: false
type: boolean
default: true
concurrency:
group: release-master
cancel-in-progress: false
jobs:
version:
name: Resolve version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
is-release: ${{ steps.resolve.outputs.is-release }}
is-prerelease: ${{ steps.resolve.outputs.is-prerelease }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install --quiet packaging
- id: resolve
env:
GH_TOKEN: ${{ github.token }}
REPUBLISH: ${{ inputs.version }}
run: |
if [ -n "$REPUBLISH" ]; then
python scripts/ci.py detect-release --version "$REPUBLISH"
else
python scripts/ci.py detect-release
fi
tag:
name: Tag and draft release
needs: version
if: needs.version.outputs.is-release == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
# necessary for creating the git tag (see repository rulesets)
ssh-key: ${{ secrets.RELEASE_TAG_DEPLOY_KEY }}
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install --quiet packaging
# Only a final release versions its deprecations.
- name: Check that deprecations are versioned
if: needs.version.outputs.is-prerelease != 'true' && github.event_name == 'push'
run: python scripts/ci.py check-deprecations
- name: Tag and create the GitHub release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ needs.version.outputs.version }}
PRERELEASE: ${{ needs.version.outputs.is-prerelease }}
EVENT: ${{ github.event_name }}
run: |
echo "Releasing v$VERSION"
# A dispatch re-publishes a release that already exists. It must never
# mint a new one: the version it was given is not checked against
# pyproject.toml until the build job, which is too late to un-tag.
if [ "$EVENT" = "workflow_dispatch" ] \
&& [ -z "$(git ls-remote --tags origin "refs/tags/v$VERSION")" ]; then
echo "::error::v$VERSION is not tagged; a dispatch re-publishes an existing release"
exit 1
fi
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
# Generate the notes before anything irreversible happens. This is the
# step most likely to fail -- a release PR that forgot to rename the
# changelog heading -- and on its own it has no side effects.
python scripts/ci.py release-notes "$VERSION" > notes.md
# Both mutations below are skipped if they have already happened, so a
# run that died between them resumes cleanly. The tag is checked
# against the remote rather than the local clone, which is shallow.
if [ -z "$(git ls-remote --tags origin "refs/tags/v$VERSION")" ]; then
git tag -a "v$VERSION" -m "pyquil v$VERSION"
git push origin "v$VERSION"
else
echo "v$VERSION is already tagged; leaving it alone"
fi
if gh release view "v$VERSION" >/dev/null 2>&1; then
echo "the GitHub release for v$VERSION already exists; leaving it alone"
else
gh release create "v$VERSION" \
--title "v$VERSION" \
--notes-file notes.md \
$([ "$PRERELEASE" = "true" ] && echo --prerelease)
fi
build-publish:
name: Build and Publish
needs: [version, tag]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # required for PyPI Trusted Publishing
contents: read
steps:
- uses: actions/checkout@v7
with:
ref: v${{ needs.version.outputs.version }}
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: snok/install-poetry@v1
with:
virtualenvs-in-project: true
# Inlined rather than delegated to scripts/, because the checked-out tree is
# the ref being published: a script read from there would be the very thing
# under test.
- name: "Verify this version may be published from this ref"
env:
VERSION: ${{ needs.version.outputs.version }}
EVENT: ${{ github.event_name }}
REF: ${{ github.ref }}
run: |
pip install --quiet packaging
python - <<'EOF'
import os, subprocess, sys
from packaging.version import Version
version = os.environ["VERSION"]
ref = os.environ["REF"]
def die(message):
sys.exit(f"::error::{message}")
if ref != "refs/heads/master":
die(f"refusing to publish {version} from {ref}; releases come from master")
packaged = subprocess.check_output(["poetry", "version", "--short"], text=True).strip()
if Version(packaged) != Version(version):
die(f"tag v{version} does not match the version in pyproject.toml ({packaged})")
if os.environ["EVENT"] == "workflow_dispatch":
# Re-publishing an existing tag: make sure it is a tag on master and
# not one left behind on a branch.
if subprocess.run(
["git", "merge-base", "--is-ancestor", f"v{version}", "origin/master"]
).returncode != 0:
die(f"v{version} is not an ancestor of master")
print(f"ok: publishing {version} from {ref}")
EOF
- name: "Build"
run: |
poetry build --no-interaction
- name: "Publish"
uses: pypa/gh-action-pypi-publish@release/v1
with:
# A version already on the index is not an error: it means an earlier
# attempt got this far. Without this, re-running to fix the half that
# failed would fail on the half that succeeded.
skip-existing: true
- id: publish
name: "Compute image tags"
env:
PYQUIL_TAG_VERSION: ${{ needs.version.outputs.version }}
run: |
export PYQUIL_TAG_LATEST=$([[ "$PYQUIL_TAG_VERSION" =~ ^[0-9]+[.][0-9]+[.][0-9]+$ ]] && echo latest || echo)
export PYQUIL_TAG_RC=$([[ "$PYQUIL_TAG_VERSION" =~ ^[0-9]+[.][0-9]+[.][0-9]+-?rc[.]?[0-9]+$ ]] && echo rc || echo)
echo "PYQUIL_TAG_VERSION=$PYQUIL_TAG_VERSION" >> "$GITHUB_OUTPUT"
echo "PYQUIL_TAG_LATEST=$PYQUIL_TAG_LATEST" >> "$GITHUB_OUTPUT"
echo "PYQUIL_TAG_RC=$PYQUIL_TAG_RC" >> "$GITHUB_OUTPUT"
outputs:
PYQUIL_TAG_VERSION: ${{ steps.publish.outputs.PYQUIL_TAG_VERSION }}
PYQUIL_TAG_LATEST: ${{ steps.publish.outputs.PYQUIL_TAG_LATEST }}
PYQUIL_TAG_RC: ${{ steps.publish.outputs.PYQUIL_TAG_RC }}
build-publish-grpc-web:
name: Build and Publish (pyquil-grpc-web)
needs: [version, tag]
if: ${{ github.event_name == 'push' || inputs.publish-grpc-web }}
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v7
with:
ref: v${{ needs.version.outputs.version }}
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: snok/install-poetry@v1
with:
virtualenvs-in-project: true
- name: Patch package metadata for grpc-web
run: |
pip install toml
python scripts/ci.py patch-grpc-web
- name: Poetry Build
run: |
poetry build --no-interaction
- name: Upload wheels as artifacts
uses: actions/upload-artifact@v4
with:
name: wheels
path: dist
- name: Publish
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
docker-publish:
name: Docker Publish
needs: [version, build-publish]
if: ${{ github.event_name == 'push' || inputs.publish-docker }}
runs-on: ubuntu-latest
env:
PYQUIL_TAG_VERSION: ${{ needs.build-publish.outputs.PYQUIL_TAG_VERSION }}
PYQUIL_TAG_LATEST: ${{ needs.build-publish.outputs.PYQUIL_TAG_LATEST }}
PYQUIL_TAG_RC: ${{ needs.build-publish.outputs.PYQUIL_TAG_RC }}
steps:
# Determine the tags to publish based on the release tag
- name: Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ vars.DOCKER_IMAGE_NAME }}
tags: |
type=raw,value=${{ env.PYQUIL_TAG_VERSION }}
type=raw,value=${{ env.PYQUIL_TAG_LATEST }},enable=${{ env.PYQUIL_TAG_LATEST != '' }}
type=raw,value=${{ env.PYQUIL_TAG_RC }},enable=${{ env.PYQUIL_TAG_RC != '' }}
# Checkout is needed to use the path context: .
- name: Checkout
uses: actions/checkout@v7
with:
ref: v${{ needs.version.outputs.version }}
- name: Build and Test
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: "${{ vars.DOCKER_IMAGE_NAME }}:test"
build-args: |
pyquil_version=${{ env.PYQUIL_TAG_VERSION }}
- name: Test Image
run: |
docker run --rm "${{ vars.DOCKER_IMAGE_NAME }}:test" python -c "from pyquil import get_qc"
# Build and publish the image
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
pyquil_version=${{ env.PYQUIL_TAG_VERSION }}