-
-
Notifications
You must be signed in to change notification settings - Fork 0
336 lines (303 loc) · 12.4 KB
/
Copy pathrelease.yml
File metadata and controls
336 lines (303 loc) · 12.4 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
name: Release
# Nothing here publishes by itself.
#
# A release happens when the owner pushes a tag, and that push is the command -
# his instruction of 2026-08-19. Running this by hand from the Actions tab
# builds every binary and attaches them to the run instead, so the whole
# pipeline can be tried without a release existing and without a tag being
# spent. The publish job below is the only one that writes anything to the
# repository, and it is fenced off behind the tag.
#
# The release is created as a DRAFT. The tag starts it, the owner still presses
# publish, and until he does nobody can download a half correct release. Drop
# --draft in the publish step to have a tag go straight out.
on:
push:
tags:
- "v*"
workflow_dispatch:
# Read only by default. The one job that needs to write says so itself.
permissions:
contents: read
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
env:
# The same pin CI carries. A release built on a different toolchain than the
# one the byte stability guards ran under is a release nobody measured.
GO_VERSION: "1.26.6"
jobs:
check:
name: the tree is green and the tag agrees with the code
runs-on: ubuntu-latest
timeout-minutes: 30
outputs:
version: ${{ steps.version.outputs.version }}
env:
CGO_ENABLED: "0"
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: ${{ env.GO_VERSION }}
- name: test
# A release built from a red tree is the one kind of release that
# cannot be taken back, because the binaries are already on somebody's
# disk. This is the same command CI runs.
run: go test ./... -count=1
- name: what version the code says
id: version
# Asked of the program rather than read out of the source, so this
# cannot drift from what a manifest will record.
run: |
set -euo pipefail
version="$(go run ./cmd/tfg version)"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "the code says $version"
- name: the tag says the same as the code
if: startsWith(github.ref, 'refs/tags/')
# The version lives in internal/version and only the owner raises it -
# immutable rule 12, and under it a major bump is a statement about
# file hashes in other people's CI. A tag that disagrees with the code
# would put one number in the release page and another one in every
# manifest the release writes, and the manifest is the copy that turns
# up months later attached to somebody's failing test.
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME#v}"
code="${{ steps.version.outputs.version }}"
if [ "$tag" != "$code" ]; then
echo "The tag is ${GITHUB_REF_NAME} and internal/version says ${code}."
echo "Raise the version in the code first, commit it, then tag that commit."
exit 1
fi
echo "tag and code agree on ${code}"
cli:
name: command line binaries
needs: check
runs-on: ubuntu-latest
timeout-minutes: 30
env:
# The command line binary has no C in it and no graphics toolkit, so one
# runner cross compiles the lot. That is not a shortcut: it is the same
# property the dependency guard exists to keep, and building all six from
# one place is how it stays visible if it ever stops being true.
CGO_ENABLED: "0"
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: ${{ env.GO_VERSION }}
- name: build and package every target
run: |
set -euo pipefail
version="${{ needs.check.outputs.version }}"
mkdir -p dist
# Go calls it darwin and almost nobody else does. The name on a
# download is read by somebody deciding whether it is the file for
# their machine, and "darwin" asks them to already know - the owner's
# point on 2026-08-20. The GOOS stays darwin, because that is what
# the compiler is told. Only the label a person reads changes.
friendly() {
case "$1" in
darwin) echo "macos" ;;
*) echo "$1" ;;
esac
}
# No darwin/amd64. Intel Macs are not supported, decided by the owner
# on 2026-08-20 - Apple silicon only.
for target in \
windows/amd64 windows/arm64 \
linux/amd64 linux/arm64 \
darwin/arm64
do
os="${target%/*}"
arch="${target#*/}"
label="$(friendly "$os")"
work="$(mktemp -d)"
binary="tfg"
if [ "$os" = "windows" ]; then
binary="tfg.exe"
fi
# -trimpath keeps the build machine's directory names out of the
# binary. On a public release that is a privacy line as much as a
# reproducibility one.
GOOS="$os" GOARCH="$arch" go build -trimpath -o "${work}/${binary}" ./cmd/tfg
# The notices travel with the binary because their licences require
# it - see THIRD-PARTY-NOTICES.md, which says so itself. The licence
# and the readme go along for the person who downloaded an archive
# and nothing else.
cp LICENSE THIRD-PARTY-NOTICES.md README.md "${work}/"
base="tfg_${version}_${label}_${arch}"
if [ "$os" = "windows" ]; then
(cd "${work}" && zip -q -r "${GITHUB_WORKSPACE}/dist/${base}.zip" .)
else
tar -czf "dist/${base}.tar.gz" -C "${work}" .
fi
echo "packaged ${base}"
done
ls -l dist
- uses: actions/upload-artifact@v7
with:
name: cli
path: dist/*
if-no-files-found: error
gui:
name: window binary on ${{ matrix.os }}
needs: check
runs-on: ${{ matrix.os }}
timeout-minutes: 60
strategy:
# One system failing should not throw away the binaries that did build.
# A release missing one platform is a decision the owner can make. A
# release missing all of them because one runner was busy is not.
fail-fast: false
matrix:
os:
- windows-latest
- ubuntu-latest
# Apple silicon only. macos-26-intel was here for one release and was
# measured working, so this is a decision rather than a limitation:
# the owner does not support Intel Macs, so nothing is built for them
# and nothing pretends to be.
- macos-latest
env:
# The window reaches OpenGL through C, so this one cannot be cross
# compiled the way the command line binary is. Each system builds its
# own, which is also the first time this project has built a real
# windowed binary anywhere but the owner's machine.
CGO_ENABLED: "1"
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: ${{ env.GO_VERSION }}
- name: graphics and windowing headers
if: runner.os == 'Linux'
# Taken from the toolkit's own CI rather than from a list somebody
# remembered. No GitHub runner carries these by default, and without
# them the toolkit's app package does not compile at all.
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libgl1-mesa-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
xorg-dev
- name: build and package
run: |
set -euo pipefail
version="${{ needs.check.outputs.version }}"
os="$(go env GOOS)"
arch="$(go env GOARCH)"
# Same rename as the command line archives above, and for the same
# reason: darwin is what the compiler is told, macos is what a person
# recognises on a download page.
# if rather than a && chain: under set -e a false test would end the
# step, and it would end it on every system that is not macOS.
label="$os"
if [ "$os" = "darwin" ]; then
label="macos"
fi
work="$(mktemp -d)"
mkdir -p dist
if [ "$os" = "windows" ]; then
# The linker flags come from the file and nowhere else, so that a
# release and a local build cannot drift. Without them Windows
# hangs a black console window behind the program. Windows only:
# the flag names a Windows subsystem and means nothing elsewhere.
go build -trimpath -ldflags="$(cat .github/gui-ldflags)" \
-o "${work}/tfg-gui.exe" ./cmd/tfg-gui
else
go build -trimpath -o "${work}/tfg-gui" ./cmd/tfg-gui
fi
cp LICENSE THIRD-PARTY-NOTICES.md README.md "${work}/"
base="tfg-gui_${version}_${label}_${arch}"
if [ "$os" = "windows" ]; then
(cd "${work}" && 7z a -tzip -bso0 "${GITHUB_WORKSPACE}/dist/${base}.zip" .)
else
tar -czf "dist/${base}.tar.gz" -C "${work}" .
fi
echo "packaged ${base}"
ls -l dist
- uses: actions/upload-artifact@v7
with:
name: gui-${{ matrix.os }}
path: dist/*
if-no-files-found: error
publish:
name: publish the release
needs: [check, cli, gui]
# The only job that writes anything, and the only one fenced behind a tag.
# Running this workflow by hand stops before here on purpose.
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
path: incoming
merge-multiple: true
- name: one list of what is being published
run: |
set -euo pipefail
cd incoming
# Bare names rather than ./name. Somebody checking one download runs
# sha256sum -c beside the file they just fetched, and a leading ./ is
# one more thing between them and an answer.
sha256sum -- * > SHA256SUMS.txt
cat SHA256SUMS.txt
- name: notes
run: |
set -euo pipefail
{
echo "Generate test files, and know how the system under test should react to them."
echo
echo "## What to download"
echo
echo "- \`tfg_*\` is the command line. It carries no graphics toolkit and no network stack."
echo "- \`tfg-gui_*\` is the desktop window. Same engine, same features."
echo
echo "Match the file to your system and architecture. Check what you downloaded against"
echo "\`SHA256SUMS.txt\`."
echo
echo "## These binaries are not signed"
echo
echo "Signing is not set up yet, so your system will say so."
echo
echo "- **Windows** shows a SmartScreen warning. More info, then Run anyway."
echo "- **macOS** refuses to open it on the first try. Open it from the right click menu,"
echo " or allow it in System Settings under Privacy and Security."
echo "- **Linux** says nothing, but you may need \`chmod +x\`."
echo
echo "If that is not a trade you want to make, build from source - it takes one command"
echo "and the readme has it."
echo
echo "## Everything that changed"
echo
echo "See [CHANGELOG.md](CHANGELOG.md)."
} > notes.md
cat notes.md
- name: create the draft release
env:
GH_TOKEN: ${{ github.token }}
# A draft, so the tag starts the release and the owner finishes it. It
# is also the last chance to read the notes before anybody downloads
# anything. A tag whose name carries a hyphen - v0.2.0-rc1 - is marked
# a prerelease as well.
run: |
set -euo pipefail
flags=(--draft --title "${GITHUB_REF_NAME}" --notes-file notes.md)
case "${GITHUB_REF_NAME}" in
*-*) flags+=(--prerelease) ;;
esac
gh release create "${GITHUB_REF_NAME}" "${flags[@]}" incoming/*
echo "drafted ${GITHUB_REF_NAME} - publish it from the Releases page"