-
-
Notifications
You must be signed in to change notification settings - Fork 1
129 lines (118 loc) · 6.11 KB
/
Copy pathrelease.yml
File metadata and controls
129 lines (118 loc) · 6.11 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
name: Release
# Build and publish a Windows release whenever a version tag is pushed. Tag the
# default branch only after CI (tests + build) is green:
# git tag v1.0.0 && git push origin v1.0.0
on:
push:
tags:
- "v*"
# The default token only reads the repo; creating a Release and uploading assets
# needs write. Scoped to this workflow, not the whole repository.
permissions:
contents: write
jobs:
release:
name: Build and publish the Windows executable
runs-on: windows-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
# The interpreter PyInstaller freezes into the shipped bundle. Keep it in
# step with the build job in ci.yml, or CI smoke-tests one artefact and
# users download another.
- uses: actions/setup-python@v7
with:
python-version: "3.14"
cache: pip
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt pyinstaller
# The version is single-sourced in VERSION.txt. The tag is v<version> for a
# final release, or v<version>-rc.N (also -beta.N / -alpha.N) for a pre-release.
# Its BASE must match VERSION.txt so a release can never ship mislabelled, and the
# presence of a -suffix decides whether GitHub marks it a pre-release.
- name: Check the tag against VERSION.txt
shell: bash
run: |
ref="${GITHUB_REF_NAME}" # v0.3.0 or v0.3.0-rc.4
ver="${ref#v}" # 0.3.0 0.3.0-rc.4
base="${ver%%-*}" # 0.3.0 0.3.0
version="$(cat VERSION.txt)"
echo "tag=$ref base=$base VERSION.txt=$version"
test "$base" = "$version" || { echo "tag base v$base != VERSION.txt $version"; exit 1; }
if [ "$ver" != "$base" ]; then echo "PRERELEASE=true" >> "$GITHUB_ENV"
else echo "PRERELEASE=false" >> "$GITHUB_ENV"; fi
echo "TITLE=$ver" >> "$GITHUB_ENV"
# The step above proves the tag is LABELLED right. This one proves the release
# NOTES are, which is a different thing and had no check at all until 0.4.0:
# 43 commits landed after the v0.4.0-rc.1 tag and collected under [Unreleased],
# three of them BREAKING. Tagging v0.4.0 then would have passed the check above
# (the base still matched VERSION.txt) and published a changelog that dated
# 0.4.0 weeks earlier and filed three contract breaks as "not released yet".
# A pre-release tag is checked the same way: -rc.N ships the same notes.
- name: Check the changelogs are closed for this version
shell: bash
run: |
version="$(cat VERSION.txt)"
escaped="${version//./\\.}"
fail=0
# CHANGELOG-INTERNAL.md is not in the repository, so this job cannot
# see it. Its structure is guarded locally by .claude/hooks/check_notes.py.
for f in CHANGELOG.md; do
grep -qE "^## \[${escaped}\] +- +[0-9]{4}-[0-9]{2}-[0-9]{2}" "$f" \
|| { echo "$f: no dated '## [$version] - YYYY-MM-DD' section"; fail=1; }
open="$(awk '/^## \[Unreleased\]/{u=1;next} /^## \[/{u=0} u && /^- /{n++} END{print n+0}' "$f")"
test "$open" = "0" \
|| { echo "$f: [Unreleased] still holds $open entries - they would ship as $version while reading as unreleased"; fail=1; }
done
test "$fail" = "0" || exit 1
echo "changelogs closed for $version"
# CI (ci.yml) already runs the full test suite on every push to master - i.e. on
# exactly this commit - so the release does not re-test. It builds, smoke-tests the
# exe and publishes. (Re-testing here would also need the dev deps and could hit
# environment-specific flakes that CI's environment does not.)
# onedir + console subsystem + asInvoker - see BeanNetworkTester.spec
- name: Build
run: pyinstaller --noconfirm BeanNetworkTester.spec
# Prove the shipped artefact actually runs before it is attached to a public
# release (the console-subsystem build means the exe returns an exit code).
- name: Smoke the built exe
shell: bash
run: |
set +e
EXE="dist/BeanNetworkTester/BeanNetworkTester.exe"
"$EXE" --version ; v=$?
"$EXE" --simulate --duration 2 --interval 1 --seed 42 ; r=$?
set -e
echo "version=$v run=$r"
test "$v" = "0" || { echo "--version failed"; exit 1; }
test "$r" = "0" || { echo "simulated run failed"; exit 1; }
# Zip the onedir bundle and write the checksum the README tells users to verify.
- name: Package (zip + SHA-256)
shell: bash
run: |
name="BeanNetworkTester-${GITHUB_REF_NAME}-windows-x64"
( cd dist && 7z a "../${name}.zip" BeanNetworkTester >/dev/null )
sha256sum "${name}.zip" > SHA256SUMS.txt
cat SHA256SUMS.txt
echo "ASSET=${name}.zip" >> "$GITHUB_ENV"
# gh is preinstalled on the runner - no third-party action, uses the job token.
# A -rc/-beta/-alpha tag publishes as a "Pre-release"; a plain tag as "Latest".
#
# The body is this version's section of CHANGELOG.md, not --generate-notes.
# --generate-notes lists merged pull request titles, so the release page showed
# something nobody wrote and never showed the changelog at all - for 0.4.0 that
# would have been 43 PR titles. Extracting the section instead means the release
# page, a blog post and CHANGELOG.md are one text that cannot drift apart.
# The step above has already proved that section exists and is dated.
- name: Publish the GitHub Release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
python tools/release_notes.py > RELEASE_NOTES.md
head -5 RELEASE_NOTES.md
flags=(--title "$TITLE" --notes-file RELEASE_NOTES.md)
if [ "$PRERELEASE" = "true" ]; then flags+=(--prerelease); else flags+=(--latest); fi
gh release create "$GITHUB_REF_NAME" "$ASSET" SHA256SUMS.txt "${flags[@]}"