Repository navigation
134 lines (122 loc) · 5.51 KB
/
Copy pathrelease.yml
File metadata and controls
134 lines (122 loc) · 5.51 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
name: Release Build
# A stable release starts with the maintainer pushing its tag:
#
# git tag v1.2.0.0 <commit on release/v1.2.0.0>
# git push origin v1.2.0.0
#
# and ends with them publishing the draft this workflow leaves behind:
#
# venv\Scripts\python.exe .github\scripts\release_signing.py sign-stable v1.2.0.0 --key <stable key>
#
# It used to start from a release created by hand on the Releases page
# (`release: created`), with assets uploaded into that already-published
# release afterwards. Two things ended that:
#
# - "Immutable releases": once published, a release's assets cannot be added,
# replaced or deleted. That is exactly the protection wanted — otherwise any
# account with write access can swap WinZapp.zip and SHA256SUMS.txt together
# on a release users already trust — and it makes uploading into a published
# release impossible. Assets now go into a DRAFT.
# - A draft cannot be the trigger: GitHub does not run workflows for the
# `created` activity of a draft release. Pushing the tag can, and creating
# non-alpha tags is already restricted to admins by the tag ruleset.
#
# Signing is the one step deliberately NOT here. A stable release is signed
# with a key that never touches GitHub (client/core/release_keys.py explains
# why), so a stolen GitHub account can at most produce an unsigned draft that
# no updated client will install.
#
# The tag has to point at a commit that already contains this file: GitHub runs
# the workflow as it exists in the pushed commit, so a release branch cut before
# this change would run the old `release: created` pipeline instead.
on:
push:
tags:
- "v*"
# Alpha tags are created by alpha-release.yml with GITHUB_TOKEN, which
# never triggers workflows anyway — this is the second line of defence.
- "!v*alpha*"
env:
PYTHON_VERSION: "3.13"
jobs:
# Gate for everything below: a release is only worth building if the test
# suite is green. Cheap to run and fails fast, before the ~10+ minute build.
# Nothing exists on the Releases page until `publish` runs, so a failure
# here leaves only the tag behind — delete it and push a corrected one.
test:
name: Run test suite
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Tags are needed by the version-ordering check below.
fetch-depth: 0
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
# uv itself, pinned like every action: "latest" would let a release
# build change toolchains on the day uv ships.
version: "0.12.13"
- name: Install Python dependencies
run: uv sync --locked
# -m "not docs": the docs-marked tests assert that CLAUDE.md still
# describes the code accurately. ci.yml runs them on every PR; a stale
# sentence in a Markdown file must not be what blocks a release build.
- name: Run pytest
run: uv run pytest --run-wx-gui -m "not docs"
# Alpha builds (alpha-release.yml) are versioned as
# <major>.<minor>.<patch>.<commit count>alpha off the CURRENT stable
# version, so they consume the fourth component. A stable release that
# only bumps that fourth component therefore sorts BELOW alphas already
# published — and updater.py would never offer it to anyone running one,
# stranding every alpha user permanently with no error anywhere.
- name: Reject a stable tag that alphas have already passed
env:
RELEASE_TAG: ${{ github.ref_name }}
run: uv run python .github/scripts/check_stable_release_ordering.py
# The build itself lives in build-windows.yml, which release.yml,
# alpha-release.yml and test-build.yml all call.
build:
needs: test
uses: ./.github/workflows/build-windows.yml
with:
# Baked into version.py so the compiled app reports the right version,
# and into SHA256SUMS.txt's version line, which the signature covers.
# release-published.yml commits the same value back to main.
version: ${{ github.ref_name }}
strip-version-prefix: true
artifact-name: winzapp-release
publish:
name: Upload assets into a draft release
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
steps:
- name: Download the built assets
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: winzapp-release
path: dist
# A DRAFT, never published from here: publishing is the maintainer's
# signed step (see the top of this file). A draft is invisible to the
# updater, so nothing reaches users before that. The release notes can
# still be edited on the draft before publishing.
- name: Create the draft release and upload assets
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: true
prerelease: false
generate_release_notes: true
fail_on_unmatched_files: true
files: |
dist/WinZappInstaller.exe
dist/WinZapp.zip
dist/SHA256SUMS.txt