Repository navigation
64 lines (61 loc) · 2.65 KB
/
Copy pathrelease.yml
File metadata and controls
64 lines (61 loc) · 2.65 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
name: Release
on:
push:
tags: ["v*"]
workflow_dispatch:
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v7
with:
python-version: "3.12"
- name: Install test dependencies (the tag gate runs the whole suite, skips and all)
run: pip install build pytest cryptography pyyaml
- name: Test the tag before shipping it
# `-ra`, and no `-q`: `addopts` already carries one and a second would suppress the
# counts line. The release log is the evidence that the tag was tested.
run: PYTHONPATH=src python -m pytest tests -ra
- name: Build
run: python -m build
- name: Checksums
run: |
cd dist && sha256sum * > SHA256SUMS && cat SHA256SUMS
- name: Extract this version's notes
# The changelog is 100 KB and leads with [Unreleased], so a 0.1.0 release whose notes
# open with unreleased work tells a reader nothing about 0.1.0. Take the one section
# whose heading names this tag. An empty result fails the job: a release with no notes
# is worse than a failed run, because the failure is visible and the release is not.
run: |
VERSION="${GITHUB_REF_NAME#v}"
awk -v ver="$VERSION" '
$0 ~ "^## \\[" ver "\\]" {f=1; print; next}
f && /^## \[/ {exit}
f {print}
' CHANGELOG.md > RELEASE_NOTES.md
if [ ! -s RELEASE_NOTES.md ]; then
echo "CHANGELOG.md has no section for $VERSION; refusing to publish a release with no notes" >&2
exit 1
fi
cat RELEASE_NOTES.md
- name: Publish the GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${GITHUB_REF_NAME}" \
--title "Docket ${GITHUB_REF_NAME#v}" \
--notes-file RELEASE_NOTES.md \
dist/*
# PyPI upload stays a deliberate manual step rather than a `twine upload` in this job.
# The reason is blast radius, not missing plumbing: a PyPI token is a credential that
# can publish under the project's name forever and cannot be scoped to a branch, so
# storing one as a repository secret hands it to everyone who ever gains admin on this
# repository. The GitHub release is published by this workflow because `github.token`
# is scoped to this run and expires with it. To ship a release to PyPI:
# python -m build && twine upload dist/*
# which is also the only way to publish from a machine whose key never touches CI.