Repository navigation
ci: fix Build ISO skipping draft PRs + unreadable skipped-job name #12
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
| # Build ISO — builds the installer AND appliance ISOs for every supported | |
| # architecture. | |
| # | |
| # Each arch builds on a native runner; the build itself runs inside the | |
| # official `nixos/nix` container via `docker run`. We deliberately do NOT put | |
| # the whole job in `container:` — the nixos/nix image lacks a standard glibc | |
| # loader, so GitHub's bundled Node can't run there and every JS action | |
| # (checkout, upload-artifact) fails with `exec .../node: no such file or | |
| # directory`. So checkout/upload run on the host runner and only `make` runs in | |
| # the container. | |
| # | |
| # One job per arch builds BOTH kinds in the SAME container (installer first, | |
| # appliance second). `make <kind>/iso` resolves to the runner's native | |
| # `builtins.currentSystem` and produces | |
| # out/<kind>-iso/iso/coder-box-<kind>-<arch>-linux.iso (+ a .sha256 sidecar); | |
| # the container /nix/store is ephemeral, so we dereference the ISOs into a host | |
| # /dist volume and upload from there. | |
| # | |
| # 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. | |
| # The job always runs (drafts just do derivations) so its name renders rather | |
| # than showing a skipped job's raw name expression; the job name shows the | |
| # per-kind plan (installer/appliance → ISO or drv). Verification artifacts are | |
| # short-lived (1 day). | |
| name: Build ISO | |
| 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: | |
| iso: | |
| # Title shows, at a glance, what each kind is doing: "ISO" (full image) or | |
| # "drv" (derivation only), e.g. | |
| # Build installer ISO + appliance drv (x86_64-linux) | |
| # The literal words "installer"/"appliance" live OUTSIDE the expressions, so | |
| # both kinds stay legible even in the raw form GitHub shows for a skipped job | |
| # (the per-kind suffix just defaults to "drv"). NOTE: the `env` context is | |
| # not available in a job name, so the label/draft checks are inlined here — | |
| # they mirror the INSTALLER_FULL/APPLIANCE_FULL job env below. | |
| name: >- | |
| Build installer ${{ (github.event_name != 'pull_request' || (github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'test-installer-iso'))) && 'ISO' || 'drv' }} | |
| + appliance ${{ (github.event_name != 'pull_request' || (github.event.pull_request.draft == false && contains(github.event.pull_request.labels.*.name, 'test-appliance-iso'))) && 'ISO' || 'drv' }} | |
| (${{ matrix.system }}) | |
| # NOTE: no job-level draft skip. Skipping the whole job on draft PRs (a) left | |
| # the job name unrendered/garbage and (b) meant a draft built nothing here. | |
| # Instead the per-kind FULL flags below treat drafts as "no full build", so | |
| # the job always runs and a draft just instantiates derivations (cheap). | |
| runs-on: ${{ matrix.runner }} | |
| env: | |
| # Per kind: realise the full ISO, or (drv-only) just instantiate. 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 → drv-only for both. Steps below branch on these. | |
| 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')) }} | |
| 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 }} | |
| # 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 ISOs | |
| 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" | |
| # Host /dist volume, bind-mounted into the container, where full builds | |
| # drop the finished ISO + checksum (the container's /nix/store is | |
| # ephemeral, so the in-tree out/ symlink would dangle on the host). | |
| dist="$(mktemp -d)" | |
| echo "dist=$dist" >>"$GITHUB_OUTPUT" | |
| # Both kinds run in ONE container; installer first, appliance later. | |
| docker run --rm \ | |
| -v "$PWD":/work -w /work \ | |
| -v "$dist":/dist \ | |
| -e INSTALLER_FULL -e APPLIANCE_FULL \ | |
| nixos/nix:latest \ | |
| sh -euc ' | |
| # The Makefile builds via "nix build --impure" and needs flakes + | |
| # nix-command, which the nixos/nix image does not enable by default. | |
| export NIX_CONFIG="experimental-features = nix-command flakes" | |
| # Repo is bind-mounted and owned by a different uid; allow git to | |
| # read it so the Makefile can stamp the build rev. | |
| git config --global --add safe.directory /work | |
| # full build → realise the ISO and copy it (+ its .sha256 sidecar, | |
| # written next to out/) into /dist; otherwise just instantiate the | |
| # derivation. Bare target → native currentSystem (matrix pins the | |
| # runner arch). gnumake provides "make"; git stamps the build rev. | |
| build_kind() { | |
| kind="$1"; full="$2" | |
| if [ "$full" = "true" ]; then | |
| nix-shell -p gnumake git --run "make $kind/iso" | |
| cp -L "out/$kind-iso/iso"/*.iso /dist/ | |
| cp "out/coder-box-$kind"-*.iso.sha256 /dist/ | |
| else | |
| nix-shell -p gnumake git --run "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 |