Skip to content

Commit c76ca67

Browse files
committed
a release the iso could have taken down with it
The ISO was built before the publish step, so a live session that failed to reach a desktop would have failed the job before the release existed and taken the binary down with it. The comment on the attach step says the opposite in as many words: an ISO that failed to attach should not take the binary's release down with it. The build now runs after the release exists, and is an addition to something that has already happened. The switch that turns it on could not reach the trigger that matters. A tag is a push event and the inputs context is empty for those, so defaults declared under workflow_dispatch and workflow_call never materialise, and the comment telling you to flip the default described something that could not affect a release. One ATTACH_ISO at job level now: the push path reads a literal, every other trigger reads its input. And the ISO would have been attached unsigned. SECURITY.md says every release asset is signed with Sigstore, and that has to stay true of the asset that becomes a bootable machine; a checksum beside it is served from the same page, so anything that can replace one can replace the other. It gets a cosign bundle like the binary. Also: the runner disk it reclaims was a shorter list than the CI job that proves the same build, which is the wrong way round for the path with less headroom. Separately, a __pycache__ entry, and the .pyc that was committed for want of one. It is invalidated by any fresh clone, so nothing would have run it, but a binary nobody can read does not belong in a repository whose case rests on being auditable.
1 parent 80fda24 commit c76ca67

4 files changed

Lines changed: 80 additions & 32 deletions

File tree

‎.github/workflows/release.yml‎

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ env:
4141
jobs:
4242
release:
4343
runs-on: ubuntu-latest
44+
env:
45+
# Whether a release carries the live ISO, decided in one place.
46+
#
47+
# A tag is a `push` event, and the `inputs` context is empty for
48+
# those: defaults declared under workflow_dispatch and
49+
# workflow_call never materialise on a push. So the tag path — the
50+
# only trigger that actually cuts a release — reads the literal
51+
# below, and every other trigger reads its input verbatim. Flip
52+
# 'false' to 'true' to start attaching the ISO; the steps at the
53+
# end of this job say why it is off.
54+
ATTACH_ISO: ${{ github.event_name == 'push' && 'false' || inputs.attach_iso }}
4455
steps:
4556
- uses: actions/checkout@v7
4657

@@ -214,32 +225,6 @@ jobs:
214225
function flush() { if (buf != "") { print buf; buf = "" } }
215226
' section.md > reflowed.md && mv reflowed.md section.md
216227
217-
# Off by default, and deliberately so. ci.yml builds and boots this
218-
# ISO on every push, which is where its failures should be found;
219-
# attaching it to a tag means a 1.8 GB asset whose first real
220-
# exercise happens during a release. Turn this on once that job has
221-
# a run history, by dispatching with attach_iso or flipping the
222-
# default. Until then a release carries the binary, as it always
223-
# has, and the ISO is a workflow artifact.
224-
- uses: ./.github/actions/kvm
225-
if: inputs.attach_iso && steps.what.outputs.publish == 'true'
226-
227-
- name: live ISO
228-
id: iso
229-
if: inputs.attach_iso && steps.what.outputs.publish == 'true'
230-
env:
231-
QEMU_VGA: virtio-vga
232-
QEMU_DISPLAY: none
233-
run: |
234-
set -euo pipefail
235-
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc || true
236-
cargo build --locked
237-
./scripts/smoke.sh --iso --keep niri
238-
name="kuma-${{ steps.what.outputs.tag }}-x86_64.iso"
239-
mv vm-smoke/niri-iso/KUMA.iso "$name"
240-
sha256sum "$name" > "$name.sha256"
241-
echo "name=$name" >> "$GITHUB_OUTPUT"
242-
243228
- name: publish
244229
if: steps.what.outputs.publish == 'true'
245230
run: |
@@ -274,15 +259,68 @@ jobs:
274259
env:
275260
GH_TOKEN: ${{ github.token }}
276261

262+
# Everything below runs *after* the release exists, and that
263+
# ordering is the point: building the ISO means a full image build
264+
# and a QEMU boot with a serial-console assertion, so putting it
265+
# ahead of `publish` meant a live session that failed to reach a
266+
# desktop took the binary's release down with it. The ISO is an
267+
# addition to a release that has already happened.
268+
#
269+
# Off by default, and deliberately so. ci.yml builds and boots this
270+
# ISO on every push, which is where its failures should be found;
271+
# attaching it to a tag means a 1.8 GB asset whose first real
272+
# exercise happens during a release. Turn it on by flipping the
273+
# `|| false` below to `|| true`, once that job has a run history.
274+
#
275+
# Read through `env` rather than `inputs.attach_iso` directly
276+
# because a tag is a `push` event, and the `inputs` context is empty
277+
# for those: defaults declared under workflow_dispatch and
278+
# workflow_call never materialise there. Testing the input alone
279+
# meant the documented way to switch this on could not affect the
280+
# only trigger that actually cuts a release.
281+
- uses: ./.github/actions/kvm
282+
if: env.ATTACH_ISO == 'true' && steps.what.outputs.publish == 'true'
283+
284+
- name: live ISO
285+
id: iso
286+
if: env.ATTACH_ISO == 'true' && steps.what.outputs.publish == 'true'
287+
env:
288+
QEMU_VGA: virtio-vga
289+
QEMU_DISPLAY: none
290+
run: |
291+
set -euo pipefail
292+
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \
293+
/usr/local/share/boost "$AGENT_TOOLSDIRECTORY" || true
294+
cargo build --locked
295+
./scripts/smoke.sh --iso --keep niri
296+
name="kuma-${{ steps.what.outputs.tag }}-x86_64.iso"
297+
mv vm-smoke/niri-iso/KUMA.iso "$name"
298+
sha256sum "$name" > "$name.sha256"
299+
echo "name=$name" >> "$GITHUB_OUTPUT"
300+
301+
# SECURITY.md says every release asset is signed with Sigstore, and
302+
# that has to stay true of the asset that becomes a bootable
303+
# machine. A checksum beside the file is not a substitute: it is
304+
# served from the same page, so anything that can replace one can
305+
# replace the other.
306+
- name: sign the live ISO
307+
if: env.ATTACH_ISO == 'true' && steps.what.outputs.publish == 'true'
308+
run: |
309+
set -euo pipefail
310+
cosign sign-blob --yes \
311+
--bundle '${{ steps.iso.outputs.name }}.bundle' \
312+
'${{ steps.iso.outputs.name }}'
313+
277314
# Uploaded separately rather than templated into the file list
278315
# above, so the release exists either way: an ISO that failed to
279316
# attach should not take the binary's release down with it.
280317
- name: attach the live ISO
281-
if: inputs.attach_iso && steps.what.outputs.publish == 'true'
318+
if: env.ATTACH_ISO == 'true' && steps.what.outputs.publish == 'true'
282319
run: |
283320
set -euo pipefail
284321
gh release upload '${{ steps.what.outputs.tag }}' \
285322
'${{ steps.iso.outputs.name }}' \
286-
'${{ steps.iso.outputs.name }}.sha256'
323+
'${{ steps.iso.outputs.name }}.sha256' \
324+
'${{ steps.iso.outputs.name }}.bundle'
287325
env:
288326
GH_TOKEN: ${{ github.token }}

‎.gitignore‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
/target
22
/Containerfile
3+
# CPython writes these beside any script it imports or compiles, and the
4+
# name carries the interpreter version, so one ran on a laptop and got
5+
# committed. A binary nobody reads has no business in a repository whose
6+
# case rests on being auditable.
7+
__pycache__/
38
# vm, vm-cosmic, vm-smoke, and any future disk directories
49
/vm*
510
/.claude

‎CHANGELOG.md‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ by building against it rather than by reading its release notes.
2828
installer media has no disk to inspect and its account has no password for
2929
ssh to use. Attaching the ISO to a release is wired but off by default,
3030
waiting on this job having a run history rather than on a tag being the
31-
first real test.
31+
first real test. When it is turned on the ISO is signed like every other
32+
release asset and the build runs after the release exists, so a live
33+
session that fails to come up cannot take the binary's release with it.
3234
- Every image now refuses an unsigned kuma update. Images carry kuma's
3335
signing key at `/etc/pki/containers/kuma.pub` and a
3436
`/etc/containers/policy.json` requiring a valid signature for
@@ -48,9 +50,12 @@ by building against it rather than by reading its release notes.
4850
answer, which left a stranger whose machine did not boot with nothing
4951
useful to paste. `user.password_hash` is removed, by parsing rather than by
5052
rewriting lines, because the value can be quoted four ways and a report is
51-
pasted by somebody who will not read it first. A declaration kuma cannot
52-
parse is omitted rather than pasted raw: not being able to redact a file is
53-
not a reason to publish it.
53+
pasted by somebody who will not read it first. The same goes for anything
54+
else on a short list of secret-bearing key names, wherever in the file it
55+
sits, and a secret that survives the redaction costs the report its
56+
declaration rather than getting published. A declaration kuma cannot parse
57+
is omitted rather than pasted raw: not being able to redact a file is not a
58+
reason to publish it.
5459
- Fedora 45 bases are named Callisto, after the nymph Zeus placed in the sky
5560
as Ursa Major. Bear names follow the alphabet where a letter has a name
5661
worth using.
-5.14 KB
Binary file not shown.

0 commit comments

Comments
 (0)