Skip to content

Commit df12a80

Browse files
authored
ci: share ISO build steps between test and release workflows
2 parents b0c6fbb + ded2bfb commit df12a80

4 files changed

Lines changed: 205 additions & 96 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Build one coder/box image kind for the runner's native architecture.
2+
#
3+
# Shared by the test and release workflows so both build images the exact same
4+
# way. Nix is already on the host (see the setup-nix action), so make/git
5+
# (preinstalled on the runner) build straight into the host /nix/store — no
6+
# container. A full build runs `make <target>/iso`, then dereferences the image
7+
# and its .sha256 sidecar (colocated in out/<target>-iso/iso) into a real dir so
8+
# a single upload carries both; a drv-only build just instantiates the
9+
# derivation (`make <target>/drv`, cheap validation, no image). Bare target →
10+
# native currentSystem (callers pin the matching native runner).
11+
name: Build coder/box ISO
12+
description: >-
13+
Build a coder/box image kind (installer or appliance) as a full ISO or, for
14+
cheap validation, just instantiate its derivation.
15+
16+
inputs:
17+
target:
18+
description: Image kind to build — "installer" or "appliance".
19+
required: true
20+
full:
21+
description: >-
22+
"true" builds the full ISO (make <target>/iso) and dereferences the image
23+
+ its .sha256 sidecar into the output dir; "false" only instantiates the
24+
derivation (make <target>/drv — cheap validation, no image).
25+
required: false
26+
default: "true"
27+
iso-compression:
28+
description: >-
29+
Optional squashfs compression override passed as ISO_COMPRESSION (e.g.
30+
"zstd -Xcompression-level 3" to trade ISO size for build speed on
31+
verification builds). Empty keeps the nixpkgs default so shipped ISOs stay
32+
small.
33+
required: false
34+
default: ""
35+
pr-title:
36+
description: >-
37+
PR title woven into the pretty version name (coderBox.prTitle). Empty for
38+
tag/main builds so they keep their plain names.
39+
required: false
40+
default: ""
41+
pr-number:
42+
description: PR number woven into the pretty version name (coderBox.prNumber).
43+
required: false
44+
default: ""
45+
branch:
46+
description: >-
47+
Source branch for the boot-screen "<short-sha>@<branch>" stamp
48+
(github.head_ref). Empty falls back to the Makefile's local branch name.
49+
required: false
50+
default: ""
51+
dist:
52+
description: >-
53+
Directory to collect built ISO(s) + .sha256 sidecars into. Empty creates a
54+
fresh mktemp dir; pass an existing dir to accumulate multiple kinds into
55+
one place (the test workflow builds installer + appliance side by side).
56+
required: false
57+
default: ""
58+
59+
outputs:
60+
dist:
61+
description: >-
62+
Absolute path to the dir holding the built ISO(s) + .sha256 sidecars (no
63+
ISO added for a drv-only build).
64+
value: ${{ steps.build.outputs.dist }}
65+
66+
runs:
67+
using: composite
68+
steps:
69+
- name: Build image
70+
id: build
71+
shell: bash
72+
env:
73+
TARGET: ${{ inputs.target }}
74+
FULL: ${{ inputs.full }}
75+
DIST: ${{ inputs.dist }}
76+
# Read by the Makefile under --impure for the image's pretty version
77+
# name / boot-screen label. Set through env (not inlined into the script)
78+
# so an arbitrary PR title can't break the shell; empty on non-PR events.
79+
ISO_COMPRESSION: ${{ inputs.iso-compression }}
80+
CODER_BOX_PR_TITLE: ${{ inputs.pr-title }}
81+
CODER_BOX_PR_NUMBER: ${{ inputs.pr-number }}
82+
CODER_BOX_BRANCH: ${{ inputs.branch }}
83+
run: |
84+
# Reuse the caller-provided dist dir or make a fresh one, then expose it
85+
# so later workflow steps (size recording, artifact upload) can find the
86+
# built image(s).
87+
dist="${DIST:-$(mktemp -d)}"
88+
echo "dist=$dist" >>"$GITHUB_OUTPUT"
89+
if [ "$FULL" = "true" ]; then
90+
make "$TARGET/iso"
91+
cp -L "out/$TARGET-iso/iso"/* "$dist/"
92+
else
93+
make "$TARGET/drv"
94+
fi
95+
ls -lh "$dist"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Install Nix natively on the runner and cache the /nix/store across runs.
2+
#
3+
# Shared by the test and release workflows so every job sets Nix up the exact
4+
# same way. Nix is installed on the host (not the `nixos/nix` container — that
5+
# container lacks a standard glibc loader, so GitHub's bundled Node couldn't run
6+
# there and JS actions failed); installing on the host avoids that and, crucially,
7+
# lets us cache the /nix/store (nix-community/cache-nix-action, backed by the
8+
# GitHub Actions cache). Each arch runs on its own native runner, so bare
9+
# `make <target>` resolves to the runner's native `builtins.currentSystem`.
10+
name: Set up Nix
11+
description: >-
12+
Install Nix natively on the runner and cache the /nix/store so bare
13+
`make <target>` builds straight into the host store.
14+
15+
inputs:
16+
cache-key-prefix:
17+
description: >-
18+
Prefix for the /nix/store cache key (e.g. "nix-images" for image builds,
19+
"nix-flake" for the cheap flake-check job) so unrelated jobs don't share a
20+
cache entry.
21+
required: false
22+
default: nix-images
23+
gc-max-store-size:
24+
description: >-
25+
Cap on the saved /nix/store size (cache-nix-action gc-max-store-size-linux)
26+
so a run can't blow past the repo's GitHub Actions cache budget (10G total).
27+
required: false
28+
default: 8G
29+
30+
runs:
31+
using: composite
32+
steps:
33+
- name: Install Nix
34+
uses: DeterminateSystems/nix-installer-action@main
35+
36+
- name: Cache Nix store
37+
uses: nix-community/cache-nix-action@v6
38+
with:
39+
# Key on the lockfile + all Nix sources; restore the most recent
40+
# arch-matching cache otherwise.
41+
primary-key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock', '**/*.nix') }}
42+
restore-prefixes-first-match: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-${{ runner.arch }}-
43+
gc-max-store-size-linux: ${{ inputs.gc-max-store-size }}

.github/workflows/release.yml

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,34 +80,19 @@ jobs:
8080
# to release an arbitrary commit.
8181
ref: ${{ github.event.inputs.ref }}
8282

83-
- name: Install Nix
84-
uses: DeterminateSystems/nix-installer-action@main
85-
86-
- name: Cache Nix store
87-
uses: nix-community/cache-nix-action@v6
88-
with:
89-
# ISO closures are large; cap the saved store so a build can't blow
90-
# past the repo's GitHub Actions cache budget (10G total).
91-
primary-key: nix-images-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock', '**/*.nix') }}
92-
restore-prefixes-first-match: nix-images-${{ runner.os }}-${{ runner.arch }}-
93-
gc-max-store-size-linux: 8G
83+
# Install Nix natively + cache the /nix/store (shared with the test
84+
# workflow so releases build the exact same way).
85+
- name: Set up Nix
86+
uses: ./.github/actions/setup-nix
9487

88+
# Build the full ISO via the shared action. No ISO_COMPRESSION / PR
89+
# stamping inputs: releases keep the slow nixpkgs default (small shipped
90+
# ISOs) and their plain version names.
9591
- name: Build ISO
9692
id: build
97-
env:
98-
TARGET: ${{ matrix.target }}
99-
run: |
100-
# Nix is on the host, so make/git (preinstalled on the runner) build
101-
# straight into the host /nix/store. The ISO target puts the image and
102-
# its .sha256 sidecar together in out/<target>-iso/iso, so a single
103-
# cp -L dereferences both into a real mktemp dir for upload (release
104-
# consumers can verify the download). Bare target → native
105-
# currentSystem (matrix pins the matching native runner).
106-
dist="$(mktemp -d)"
107-
echo "dist=$dist" >>"$GITHUB_OUTPUT"
108-
make "$TARGET/iso"
109-
cp -L "out/$TARGET-iso/iso"/* "$dist/"
110-
ls -lh "$dist"
93+
uses: ./.github/actions/build-iso
94+
with:
95+
target: ${{ matrix.target }}
11196

11297
- name: Upload ISO artifact
11398
uses: actions/upload-artifact@v6

.github/workflows/test.yml

Lines changed: 57 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,14 @@ jobs:
7979
with:
8080
ref: ${{ github.event.inputs.ref }}
8181

82-
- name: Install Nix
83-
uses: DeterminateSystems/nix-installer-action@main
84-
85-
- name: Cache Nix store
86-
uses: nix-community/cache-nix-action@v6
82+
# Install Nix natively + cache the /nix/store. The flake check builds
83+
# nothing, so it gets its own smaller cache (distinct prefix + gc cap) so
84+
# it doesn't share an entry with the large image builds.
85+
- name: Set up Nix
86+
uses: ./.github/actions/setup-nix
8787
with:
88-
# Key on the lockfile + all Nix sources; restore the most recent
89-
# arch-matching cache otherwise. Cap the saved store so a run can't
90-
# blow past the repo's GitHub Actions cache budget.
91-
primary-key: nix-flake-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock', '**/*.nix') }}
92-
restore-prefixes-first-match: nix-flake-${{ runner.os }}-${{ runner.arch }}-
93-
gc-max-store-size-linux: 5G
88+
cache-key-prefix: nix-flake
89+
gc-max-store-size: 5G
9490

9591
- name: Flake check
9692
run: make check
@@ -146,22 +142,19 @@ jobs:
146142
name: Build ${{ needs.plan.outputs.kinds }} (${{ matrix.system }})
147143
runs-on: ${{ matrix.runner }}
148144
env:
149-
# Resolved per-kind plan from the `plan` job. Steps below branch on these.
145+
# Resolved per-kind plan from the `plan` job. Steps below (the build-plan
146+
# summary + the per-kind build/upload gating) branch on these. The
147+
# build-iso action instead receives the plan per kind via its `full` input.
148+
#
149+
# PR title/number + branch are passed straight to the build-iso action's
150+
# inputs (pr-title / pr-number / branch): the title + number are woven into
151+
# the image's pretty version name (coderBox.prTitle / coderBox.prNumber),
152+
# and the branch feeds the boot-screen "<short-sha>@<branch>" stamp
153+
# (github.head_ref is the real source branch on PRs — a PR checkout is a
154+
# detached HEAD — and empty otherwise so the Makefile falls back to the
155+
# local branch name; tag/main builds keep their plain names).
150156
INSTALLER_FULL: ${{ needs.plan.outputs.installer_full }}
151157
APPLIANCE_FULL: ${{ needs.plan.outputs.appliance_full }}
152-
# PR title + number woven into the image's pretty version name (boot-menu
153-
# label + ISO file name) via coderBox.prTitle / coderBox.prNumber. Set
154-
# through `env:` (not inlined into a run script) so an arbitrary title
155-
# can't break the shell, and empty for non-PR events so tag/main builds
156-
# keep their plain names.
157-
CODER_BOX_PR_TITLE: ${{ github.event.pull_request.title }}
158-
CODER_BOX_PR_NUMBER: ${{ github.event.pull_request.number }}
159-
# Branch name for the boot-screen label's "<short-sha>@<branch>" stamp. A
160-
# PR checkout is a detached HEAD (so the Makefile's `git rev-parse
161-
# --abbrev-ref HEAD` would say "HEAD"); github.head_ref is the real source
162-
# branch on pull_request events and empty otherwise (the Makefile then
163-
# falls back to the local branch name).
164-
CODER_BOX_BRANCH: ${{ github.head_ref }}
165158
strategy:
166159
fail-fast: false
167160
matrix:
@@ -178,62 +171,55 @@ jobs:
178171
# dispatch to build an arbitrary commit.
179172
ref: ${{ github.event.inputs.ref }}
180173

181-
- name: Install Nix
182-
uses: DeterminateSystems/nix-installer-action@main
183-
184-
- name: Cache Nix store
185-
uses: nix-community/cache-nix-action@v6
186-
with:
187-
# ISO closures are large; cap the saved store so a build can't blow
188-
# past the repo's GitHub Actions cache budget (10G total).
189-
primary-key: nix-images-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock', '**/*.nix') }}
190-
restore-prefixes-first-match: nix-images-${{ runner.os }}-${{ runner.arch }}-
191-
gc-max-store-size-linux: 8G
174+
# Install Nix natively + cache the /nix/store (shared with the release
175+
# workflow so both build images the exact same way).
176+
- name: Set up Nix
177+
uses: ./.github/actions/setup-nix
192178

193-
# Per-kind plan (full ISO vs drv only) is in the job name; the run summary
194-
# below also records it. INSTALLER_FULL / APPLIANCE_FULL come from the
195-
# job-level env above.
196-
- name: Build images
179+
# Record the per-kind plan (full ISO vs drv only, in the job name too) in
180+
# the run summary, and stage one dist dir that both kinds' builds collect
181+
# into so the size/upload steps below have a single place to look.
182+
# INSTALLER_FULL / APPLIANCE_FULL come from the job-level env above.
183+
- name: Build plan
197184
id: build
198-
env:
199-
# These are verification images, not shipped artifacts, so trade ISO
200-
# size for build speed: a low squashfs compression level is far faster
201-
# than the nixpkgs default (zstd level 19), which otherwise dominates
202-
# the build — and the rev baked into /etc forces that recompress on
203-
# every commit regardless of caching. Releases keep the slow default.
204-
ISO_COMPRESSION: zstd -Xcompression-level 3
205185
run: |
206-
# Record the per-kind plan in the run summary for quick scanning.
207186
plan() { [ "$1" = "true" ] && echo "full ISO" || echo "derivation only"; }
208187
{
209188
echo "### Build plan (${{ matrix.system }})"
210189
echo "- installer: $(plan "$INSTALLER_FULL")"
211190
echo "- appliance: $(plan "$APPLIANCE_FULL")"
212191
} >>"$GITHUB_STEP_SUMMARY"
192+
echo "dist=$(mktemp -d)" >>"$GITHUB_OUTPUT"
213193
214-
# Nix is on the host now, so make/git (preinstalled on the runner)
215-
# build straight into the host /nix/store — no container. A full build
216-
# → make <kind>/iso, then dereference the image + its .sha256 sidecar
217-
# (colocated in out/<kind>-iso/iso) into a real dir for upload; a
218-
# drv-only kind just instantiates. Bare target → native currentSystem.
219-
dist="$(mktemp -d)"
220-
echo "dist=$dist" >>"$GITHUB_OUTPUT"
221-
# Nix is on the host (make/git preinstalled on the runner) so the
222-
# build goes straight into the host /nix/store — no container. The
223-
# job-level CODER_BOX_PR_TITLE / CODER_BOX_PR_NUMBER env is inherited
224-
# by make directly (read under --impure for the pretty version name).
225-
build_kind() {
226-
kind="$1"; full="$2"
227-
if [ "$full" = "true" ]; then
228-
make "$kind/iso"
229-
cp -L "out/$kind-iso/iso"/* "$dist/"
230-
else
231-
make "$kind/drv"
232-
fi
233-
}
234-
build_kind installer "$INSTALLER_FULL"
235-
build_kind appliance "$APPLIANCE_FULL"
236-
ls -lh "$dist"
194+
# Build each kind through the shared action (same as the release workflow).
195+
# These are verification images, not shipped artifacts, so trade ISO size
196+
# for build speed via ISO_COMPRESSION: a low squashfs compression level is
197+
# far faster than the nixpkgs default (zstd level 19), which otherwise
198+
# dominates the build — and the rev baked into /etc forces that recompress
199+
# on every commit regardless of caching. Releases keep the slow default.
200+
# Both kinds collect into the one staged dist dir; a drv-only kind
201+
# (full=false) instantiates without adding an ISO.
202+
- name: Build installer
203+
uses: ./.github/actions/build-iso
204+
with:
205+
target: installer
206+
full: ${{ env.INSTALLER_FULL }}
207+
dist: ${{ steps.build.outputs.dist }}
208+
iso-compression: zstd -Xcompression-level 3
209+
pr-title: ${{ github.event.pull_request.title }}
210+
pr-number: ${{ github.event.pull_request.number }}
211+
branch: ${{ github.head_ref }}
212+
213+
- name: Build appliance
214+
uses: ./.github/actions/build-iso
215+
with:
216+
target: appliance
217+
full: ${{ env.APPLIANCE_FULL }}
218+
dist: ${{ steps.build.outputs.dist }}
219+
iso-compression: zstd -Xcompression-level 3
220+
pr-title: ${{ github.event.pull_request.title }}
221+
pr-number: ${{ github.event.pull_request.number }}
222+
branch: ${{ github.head_ref }}
237223

238224
# Record the exact byte size of every ISO this arch actually built, one
239225
# TSV row per ISO (system, filename, bytes). The iso-table job downloads

0 commit comments

Comments
 (0)