Repository navigation
ci: install Nix on the host + cache /nix/store (drop docker run) #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Test Nix — evaluates the flake and builds the installer AND appliance ISOs | |
| # for every supported architecture. | |
| # | |
| # A `flake` job runs `nix flake check` (cheap, builds nothing) so Nix typos / | |
| # bad references / type errors surface fast; the matrix build jobs then realise | |
| # (or, drv-only, just instantiate) the images. | |
| # | |
| # Nix is installed natively on each runner (cachix/install-nix-action) rather | |
| # than run from 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 entirely and, crucially, lets us | |
| # cache the /nix/store across runs (nix-community/cache-nix-action, backed by | |
| # the GitHub Actions cache). Each arch builds on its own native runner, so | |
| # `make <kind>/iso` resolves to the runner's native `builtins.currentSystem`. | |
| # | |
| # Triggers / what gets built per kind: | |
| # * push to main, workflow_dispatch → always build both full ISOs. | |
| # * pull_request → realise a kind's full ISO only when the | |
| # PR is ready-for-review (non-draft) AND its label (test-installer-iso / | |
| # test-appliance-iso) is applied; otherwise (draft, or no label) that kind | |
| # is just instantiated (.drv, cheap validation, no image). The `labeled` | |
| # trigger means adding the label kicks off the full build. | |
| # A tiny `plan` job computes the per-kind plan once and a short title fragment | |
| # so the build job's name stays readable. The build job always runs (drafts just | |
| # do derivations). Verification artifacts are short-lived (1 day). | |
| name: Test Nix | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| # `opened`/`reopened` cover a PR created/reopened already non-draft, | |
| # `ready_for_review` a draft promoted to ready, `labeled` so applying a | |
| # test-*-iso label starts the full build, and `synchronize` so pushing new | |
| # commits re-runs the build — re-evaluating the labels so a labelled kind | |
| # is re-built (not just its derivation) on every commit. | |
| types: [opened, reopened, ready_for_review, labeled, synchronize] | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: "Git ref/commit to build (defaults to the selected branch)" | |
| required: false | |
| type: string | |
| # Cancel superseded runs on the same ref; a full ISO build is expensive so | |
| # don't waste runners on stale commits. | |
| concurrency: | |
| group: build-${{ github.ref }}-${{ github.event.inputs.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # Flake evaluation — cheap, builds nothing. `nix flake check --no-build | |
| # --all-systems` evaluates every flake output (nixosConfigurations, packages, | |
| # …) for all declared systems (x86_64 + aarch64), catching typos / bad | |
| # references / type errors in seconds. The per-kind ISO derivations are | |
| # instantiated separately by the `Images` job below (its drv-only path), so | |
| # this covers the flake outputs that path doesn't touch. | |
| flake: | |
| name: Flake eval | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| with: | |
| ref: ${{ github.event.inputs.ref }} | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v30 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - 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. 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 | |
| - name: nix flake check | |
| run: nix flake check --impure --no-build --all-systems | |
| # Tiny pre-job that decides, per kind, whether to build the full ISO or just | |
| # instantiate the derivation, and assembles a short human title for the build | |
| # job. Doing this here (rather than inline in the build job's `name:`) keeps | |
| # that name a SHORT expression — `Build ${{ needs.plan.outputs.kinds }} | |
| # (${{ matrix.system }})` — so the raw "Matrix:" preview / a skipped job shows | |
| # something readable instead of a wall of inlined label checks. | |
| plan: | |
| name: Plan image targets | |
| runs-on: ubuntu-latest | |
| outputs: | |
| # "true"/"false" per kind: realise the full ISO, or (drv-only) instantiate. | |
| installer_full: ${{ steps.plan.outputs.installer_full }} | |
| appliance_full: ${{ steps.plan.outputs.appliance_full }} | |
| # Human title fragment, e.g. "installer & appliance ISO" or | |
| # "installer ISO & appliance DRV". | |
| kinds: ${{ steps.plan.outputs.kinds }} | |
| steps: | |
| - id: plan | |
| # Non-PR events (push, manual dispatch) build both full; a PR builds a | |
| # kind's full ISO only when it is ready-for-review (non-draft) AND its | |
| # label is applied. Draft PRs / unlabelled kinds → drv-only. | |
| env: | |
| INSTALLER_FULL: ${{ github.event_name != 'pull_request' || (github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'test-installer-iso')) }} | |
| APPLIANCE_FULL: ${{ github.event_name != 'pull_request' || (github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'test-appliance-iso')) }} | |
| run: | | |
| isuf=$([ "$INSTALLER_FULL" = "true" ] && echo ISO || echo DRV) | |
| asuf=$([ "$APPLIANCE_FULL" = "true" ] && echo ISO || echo DRV) | |
| # Always join the two kinds with "&". Collapse to a shared suffix when | |
| # they match, otherwise spell each out. | |
| if [ "$isuf" = "$asuf" ]; then | |
| kinds="installer & appliance $isuf" | |
| else | |
| kinds="installer $isuf & appliance $asuf" | |
| fi | |
| { | |
| echo "installer_full=$INSTALLER_FULL" | |
| echo "appliance_full=$APPLIANCE_FULL" | |
| echo "kinds=$kinds" | |
| } >>"$GITHUB_OUTPUT" | |
| # Job key is "Images" so the matrix shows as "Matrix: Images". `needs: plan` | |
| # also means these matrix jobs are skipped if the plan job fails. | |
| Images: | |
| needs: plan | |
| # Short, readable name — the per-kind plan is computed by the `plan` job | |
| # above. e.g. "Build installer & appliance ISO (x86_64-linux)" or | |
| # "Build installer ISO & appliance DRV (aarch64-linux)". | |
| 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. | |
| INSTALLER_FULL: ${{ needs.plan.outputs.installer_full }} | |
| APPLIANCE_FULL: ${{ needs.plan.outputs.appliance_full }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - system: x86_64-linux | |
| runner: ubuntu-24.04 | |
| - system: aarch64-linux | |
| runner: ubuntu-24.04-arm | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| with: | |
| # Empty for push/PR (checks out the event ref); honored for manual | |
| # dispatch to build an arbitrary commit. | |
| ref: ${{ github.event.inputs.ref }} | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v30 | |
| with: | |
| extra_nix_config: | | |
| experimental-features = nix-command flakes | |
| - 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 | |
| # 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 | |
| id: build | |
| 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" | |
| # 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" | |
| 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" | |
| - name: Upload installer ISO artifact | |
| if: env.INSTALLER_FULL == 'true' | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: coder-box-installer-${{ matrix.system }} | |
| path: ${{ steps.build.outputs.dist }}/coder-box-installer-*.iso | |
| # Verification build; keep storage cost minimal. | |
| retention-days: 1 | |
| if-no-files-found: error | |
| - name: Upload installer checksum artifact | |
| if: env.INSTALLER_FULL == 'true' | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: coder-box-installer-${{ matrix.system }}-sha256 | |
| path: ${{ steps.build.outputs.dist }}/coder-box-installer-*.iso.sha256 | |
| retention-days: 1 | |
| if-no-files-found: error | |
| - name: Upload appliance ISO artifact | |
| if: env.APPLIANCE_FULL == 'true' | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: coder-box-appliance-${{ matrix.system }} | |
| path: ${{ steps.build.outputs.dist }}/coder-box-appliance-*.iso | |
| retention-days: 1 | |
| if-no-files-found: error | |
| - name: Upload appliance checksum artifact | |
| if: env.APPLIANCE_FULL == 'true' | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: coder-box-appliance-${{ matrix.system }}-sha256 | |
| path: ${{ steps.build.outputs.dist }}/coder-box-appliance-*.iso.sha256 | |
| retention-days: 1 | |
| if-no-files-found: error |