forked from ChelseaKR/exitdrill
-
Notifications
You must be signed in to change notification settings - Fork 0
128 lines (120 loc) · 4.66 KB
/
Copy pathrelease.yml
File metadata and controls
128 lines (120 loc) · 4.66 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
name: release
# Trusted-main, signed-tag, split-authority release, dispatch only.
#
# NOT YET USED: no version of ExitDrill has been tagged or released. This
# workflow is prepared ahead of the first tag so cutting v0.1.0 does not also
# require writing a release pipeline under time pressure. It runs only when a
# maintainer dispatches it with an existing signed annotated tag. The shared
# authorization workflow checks out trusted main, requires stable SemVer,
# verifies the annotated tag object and its SSH signature against
# .github/allowed_signers, and requires the tagged commit to be an ancestor of
# origin/main. The build job then re-runs the exact local gate set (make
# verify plus both declared demo outcomes) at that verified commit and builds
# the wheel, sdist, and checksums. The publish job holds the only write
# authority, never checks out code, rechecks the immutable tag object against
# the live repository, and publishes the GitHub Release.
#
# Deliberately out of scope until an explicit decision records otherwise:
# PyPI or any package-registry publishing (no trusted publisher configured).
on:
workflow_dispatch:
inputs:
tag:
description: "Existing signed stable SemVer tag (vX.Y.Z)"
required: true
type: string
permissions:
contents: read
concurrency:
group: release
cancel-in-progress: false
jobs:
authorize:
permissions:
contents: read
uses: ChelseaKR/portfolio-standards/.github/workflows/release-authorize.yml@3a9e2bca7a8e7e6a79a78cd743bef0a8b9253097
with:
tag: ${{ inputs.tag }}
build:
name: re-verify at the tagged commit and build the candidate
needs: authorize
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.authorize.outputs.release-commit }}
persist-credentials: false
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: "0.11.29"
python-version: "3.12"
enable-cache: false
- name: Require tag and package versions to match
env:
RELEASE_TAG: ${{ needs.authorize.outputs.release-tag }}
run: |
python - <<'PY'
import os
import pathlib
import tomllib
version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
if os.environ["RELEASE_TAG"] != f"v{version}":
raise SystemExit("tag and package versions do not match")
PY
- name: Verify and build the candidate at the verified commit
run: |
uv sync --locked
make verify
make demo
make demo-lossy
make package
sha256sum dist/* > dist/SHA256SUMS
- name: Require a CHANGELOG section for this version
env:
RELEASE_TAG: ${{ needs.authorize.outputs.release-tag }}
run: |
version="${RELEASE_TAG#v}"
awk -v ver="$version" '
index($0, "## [" ver "]") == 1 { found=1; print; next }
found && /^## \[/ { exit }
found { print }
' CHANGELOG.md > release-notes.md
if [ ! -s release-notes.md ]; then
echo "::error::CHANGELOG.md has no '## [$version]' section; add one before tagging" >&2
exit 1
fi
- name: Upload immutable candidate inputs
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-assets
path: |
dist/
release-notes.md
if-no-files-found: error
publish:
name: recheck the immutable tag and publish the GitHub Release
needs: [authorize, build]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-assets
path: release-assets
- name: Recheck the immutable tag object and create the release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.authorize.outputs.release-tag }}
TAG_OBJECT_SHA: ${{ needs.authorize.outputs.tag-object-sha }}
run: |
set -euo pipefail
LIVE_TAG_OBJECT="$(gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" --jq .object.sha)"
test "${LIVE_TAG_OBJECT}" = "${TAG_OBJECT_SHA}"
gh release create "${RELEASE_TAG}" \
--title "${RELEASE_TAG}" \
--notes-file release-assets/release-notes.md \
--verify-tag \
release-assets/dist/*