forked from ChelseaKR/qfer-preflight
-
Notifications
You must be signed in to change notification settings - Fork 0
119 lines (105 loc) · 3.95 KB
/
Copy pathrelease.yml
File metadata and controls
119 lines (105 loc) · 3.95 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
name: Release
# Publishing is split into two jobs on purpose. The job that can verify has no
# write permission, and the job that can write never checks out repository
# content. Neither can publish something the other did not approve.
#
# The key that signs release tags is committed at .github/allowed_signers, one
# line per identity in the format git's gpg.ssh.allowedSignersFile expects, and
# it is read from trusted main rather than from the tag under release. While
# that file names no principal, the verification job fails closed and nothing
# is published.
on:
workflow_dispatch:
inputs:
tag:
description: "Signed tag to publish, for example v0.1.0"
required: true
type: string
permissions:
contents: read
jobs:
verify-tag:
name: Verify the tag is signed and reachable from main
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out main with full history
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main
fetch-depth: 0
persist-credentials: false
- name: Refuse to release from anything but main
run: |
set -euo pipefail
echo "workflow ref is ${GITHUB_REF}"
test "${GITHUB_REF}" = "refs/heads/main"
- name: Verify the tag object, its signature and its ancestry
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
# Present is not the same as populated. A signer list holding only
# comments and blank lines would hand signature verification an
# empty principal list, and the failure would read like a bad
# signature rather than a missing key, so refuse before anything
# else runs.
grep -qv '^[[:space:]]*\(#\|$\)' .github/allowed_signers
git fetch --tags --force origin
# An annotated or signed tag is a tag object. A lightweight tag
# points straight at a commit and carries no signature.
test "$(git cat-file -t "${TAG}")" = "tag"
git config gpg.ssh.allowedSignersFile .github/allowed_signers
git verify-tag "${TAG}"
# The tag must already be on main, so a release can never ship code
# that did not go through the branch.
git merge-base --is-ancestor "${TAG}" origin/main
- name: Install uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
with:
enable-cache: false
- name: Run the full gate at the tagged commit
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
git checkout --detach "${TAG}^{commit}"
uv python install 3.12
uv sync --locked
make verify
- name: Confirm the tag and the package version agree
env:
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
want="${TAG#v}"
have="$(uv run python -c 'import tomllib, pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
test "${want}" = "${have}"
publish:
name: Publish the release
needs: verify-tag
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Re-resolve the tag object after verification
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
sha="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${TAG}" --jq '.object.sha')"
test -n "${sha}"
echo "verified tag object resolves to ${sha}"
- name: Create the GitHub release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
gh release create "${TAG}" \
--repo "${GITHUB_REPOSITORY}" \
--title "${TAG}" \
--notes-from-tag \
--verify-tag