Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions .github/actions/build-iso/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Build one coder/box image kind for the runner's native architecture.
#
# Shared by the test and release workflows so both build images the exact same
# way. Nix is already on the host (see the setup-nix action), so make/git
# (preinstalled on the runner) build straight into the host /nix/store — no
# container. A full build runs `make <target>/iso`, then dereferences the image
# and its .sha256 sidecar (colocated in out/<target>-iso/iso) into a real dir so
# a single upload carries both; a drv-only build just instantiates the
# derivation (`make <target>/drv`, cheap validation, no image). Bare target →
# native currentSystem (callers pin the matching native runner).
name: Build coder/box ISO
description: >-
Build a coder/box image kind (installer or appliance) as a full ISO or, for
cheap validation, just instantiate its derivation.

inputs:
target:
description: Image kind to build — "installer" or "appliance".
required: true
full:
description: >-
"true" builds the full ISO (make <target>/iso) and dereferences the image
+ its .sha256 sidecar into the output dir; "false" only instantiates the
derivation (make <target>/drv — cheap validation, no image).
required: false
default: "true"
iso-compression:
description: >-
Optional squashfs compression override passed as ISO_COMPRESSION (e.g.
"zstd -Xcompression-level 3" to trade ISO size for build speed on
verification builds). Empty keeps the nixpkgs default so shipped ISOs stay
small.
required: false
default: ""
pr-title:
description: >-
PR title woven into the pretty version name (coderBox.prTitle). Empty for
tag/main builds so they keep their plain names.
required: false
default: ""
pr-number:
description: PR number woven into the pretty version name (coderBox.prNumber).
required: false
default: ""
branch:
description: >-
Source branch for the boot-screen "<short-sha>@<branch>" stamp
(github.head_ref). Empty falls back to the Makefile's local branch name.
required: false
default: ""
dist:
description: >-
Directory to collect built ISO(s) + .sha256 sidecars into. Empty creates a
fresh mktemp dir; pass an existing dir to accumulate multiple kinds into
one place (the test workflow builds installer + appliance side by side).
required: false
default: ""

outputs:
dist:
description: >-
Absolute path to the dir holding the built ISO(s) + .sha256 sidecars (no
ISO added for a drv-only build).
value: ${{ steps.build.outputs.dist }}

runs:
using: composite
steps:
- name: Build image
id: build
shell: bash
env:
TARGET: ${{ inputs.target }}
FULL: ${{ inputs.full }}
DIST: ${{ inputs.dist }}
# Read by the Makefile under --impure for the image's pretty version
# name / boot-screen label. Set through env (not inlined into the script)
# so an arbitrary PR title can't break the shell; empty on non-PR events.
ISO_COMPRESSION: ${{ inputs.iso-compression }}
CODER_BOX_PR_TITLE: ${{ inputs.pr-title }}
CODER_BOX_PR_NUMBER: ${{ inputs.pr-number }}
CODER_BOX_BRANCH: ${{ inputs.branch }}
run: |
# Reuse the caller-provided dist dir or make a fresh one, then expose it
# so later workflow steps (size recording, artifact upload) can find the
# built image(s).
dist="${DIST:-$(mktemp -d)}"
echo "dist=$dist" >>"$GITHUB_OUTPUT"
if [ "$FULL" = "true" ]; then
make "$TARGET/iso"
cp -L "out/$TARGET-iso/iso"/* "$dist/"
else
make "$TARGET/drv"
fi
ls -lh "$dist"
43 changes: 43 additions & 0 deletions .github/actions/setup-nix/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Install Nix natively on the runner and cache the /nix/store across runs.
#
# Shared by the test and release workflows so every job sets Nix up the exact
# same way. Nix is installed on the host (not the `nixos/nix` container — that
# container lacks a standard glibc loader, so GitHub's bundled Node couldn't run
# there and JS actions failed); installing on the host avoids that and, crucially,
# lets us cache the /nix/store (nix-community/cache-nix-action, backed by the
# GitHub Actions cache). Each arch runs on its own native runner, so bare
# `make <target>` resolves to the runner's native `builtins.currentSystem`.
name: Set up Nix
description: >-
Install Nix natively on the runner and cache the /nix/store so bare
`make <target>` builds straight into the host store.

inputs:
cache-key-prefix:
description: >-
Prefix for the /nix/store cache key (e.g. "nix-images" for image builds,
"nix-flake" for the cheap flake-check job) so unrelated jobs don't share a
cache entry.
required: false
default: nix-images
gc-max-store-size:
description: >-
Cap on the saved /nix/store size (cache-nix-action gc-max-store-size-linux)
so a run can't blow past the repo's GitHub Actions cache budget (10G total).
required: false
default: 8G

runs:
using: composite
steps:
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

- name: Cache Nix store
uses: nix-community/cache-nix-action@v6
with:
# Key on the lockfile + all Nix sources; restore the most recent
# arch-matching cache otherwise.
primary-key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('flake.lock', '**/*.nix') }}
restore-prefixes-first-match: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-${{ runner.arch }}-
gc-max-store-size-linux: ${{ inputs.gc-max-store-size }}
35 changes: 10 additions & 25 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,34 +80,19 @@ jobs:
# to release an arbitrary commit.
ref: ${{ github.event.inputs.ref }}

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

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

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

- name: Upload ISO artifact
uses: actions/upload-artifact@v6
Expand Down
128 changes: 57 additions & 71 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,14 @@ jobs:
with:
ref: ${{ github.event.inputs.ref }}

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

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

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

- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

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

# Per-kind plan (full ISO vs drv only) is in the job name; the run summary
# below also records it. INSTALLER_FULL / APPLIANCE_FULL come from the
# job-level env above.
- name: Build images
# Record the per-kind plan (full ISO vs drv only, in the job name too) in
# the run summary, and stage one dist dir that both kinds' builds collect
# into so the size/upload steps below have a single place to look.
# INSTALLER_FULL / APPLIANCE_FULL come from the job-level env above.
- name: Build plan
id: build
env:
# These are verification images, not shipped artifacts, so trade ISO
# size for build speed: a low squashfs compression level is far faster
# than the nixpkgs default (zstd level 19), which otherwise dominates
# the build — and the rev baked into /etc forces that recompress on
# every commit regardless of caching. Releases keep the slow default.
ISO_COMPRESSION: zstd -Xcompression-level 3
run: |
# Record the per-kind plan in the run summary for quick scanning.
plan() { [ "$1" = "true" ] && echo "full ISO" || echo "derivation only"; }
{
echo "### Build plan (${{ matrix.system }})"
echo "- installer: $(plan "$INSTALLER_FULL")"
echo "- appliance: $(plan "$APPLIANCE_FULL")"
} >>"$GITHUB_STEP_SUMMARY"
echo "dist=$(mktemp -d)" >>"$GITHUB_OUTPUT"

# Nix is on the host now, so make/git (preinstalled on the runner)
# build straight into the host /nix/store — no container. A full build
# → make <kind>/iso, then dereference the image + its .sha256 sidecar
# (colocated in out/<kind>-iso/iso) into a real dir for upload; a
# drv-only kind just instantiates. Bare target → native currentSystem.
dist="$(mktemp -d)"
echo "dist=$dist" >>"$GITHUB_OUTPUT"
# Nix is on the host (make/git preinstalled on the runner) so the
# build goes straight into the host /nix/store — no container. The
# job-level CODER_BOX_PR_TITLE / CODER_BOX_PR_NUMBER env is inherited
# by make directly (read under --impure for the pretty version name).
build_kind() {
kind="$1"; full="$2"
if [ "$full" = "true" ]; then
make "$kind/iso"
cp -L "out/$kind-iso/iso"/* "$dist/"
else
make "$kind/drv"
fi
}
build_kind installer "$INSTALLER_FULL"
build_kind appliance "$APPLIANCE_FULL"
ls -lh "$dist"
# Build each kind through the shared action (same as the release workflow).
# These are verification images, not shipped artifacts, so trade ISO size
# for build speed via ISO_COMPRESSION: a low squashfs compression level is
# far faster than the nixpkgs default (zstd level 19), which otherwise
# dominates the build — and the rev baked into /etc forces that recompress
# on every commit regardless of caching. Releases keep the slow default.
# Both kinds collect into the one staged dist dir; a drv-only kind
# (full=false) instantiates without adding an ISO.
- name: Build installer
uses: ./.github/actions/build-iso
with:
target: installer
full: ${{ env.INSTALLER_FULL }}
dist: ${{ steps.build.outputs.dist }}
iso-compression: zstd -Xcompression-level 3
pr-title: ${{ github.event.pull_request.title }}
pr-number: ${{ github.event.pull_request.number }}
branch: ${{ github.head_ref }}

- name: Build appliance
uses: ./.github/actions/build-iso
with:
target: appliance
full: ${{ env.APPLIANCE_FULL }}
dist: ${{ steps.build.outputs.dist }}
iso-compression: zstd -Xcompression-level 3
pr-title: ${{ github.event.pull_request.title }}
pr-number: ${{ github.event.pull_request.number }}
branch: ${{ github.head_ref }}

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