From ce05da596908daf1a7d68a9010b63f6af8e681e9 Mon Sep 17 00:00:00 2001 From: Alan Clucas Date: Wed, 29 Jul 2026 12:05:21 +0100 Subject: [PATCH] ci(nix): build the development shell into the Cachix cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flake pins tools that only this project builds, so nothing public caches them: every contributor, dev container and CI job that enters the shell compiles the same derivations from scratch. Point them all at the `argo-workflows` Cachix cache instead, and keep it stocked. A composite action installs Nix and configures the cache, so the installer version and the cache name live in one place rather than in every job that will start using the shell. It pulls by default and only pushes when the caller hands it a token, which is how a fork pull request — where secrets are not available — stays read-only without having to say so. The workflow builds the shell on the two platforms people develop on. Linux runs on pull requests too, so a toolchain change is proven before it lands rather than after; macOS is left out of those because of what the runner costs, and warms the cache once the change is merged. The weekly schedule covers the case where nothing changes but the cache expires underneath us anyway. Nothing consumes the cache yet: converting CI jobs, the dev container and the release to the shell comes next. Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Alan Clucas --- .github/actions/setup-nix/action.yml | 34 +++++++++++++ .github/workflows/nix.yaml | 74 ++++++++++++++++++++++++++++ docs/running-locally.md | 15 ++++++ 3 files changed, 123 insertions(+) create mode 100644 .github/actions/setup-nix/action.yml create mode 100644 .github/workflows/nix.yaml diff --git a/.github/actions/setup-nix/action.yml b/.github/actions/setup-nix/action.yml new file mode 100644 index 000000000000..47613b1f5d56 --- /dev/null +++ b/.github/actions/setup-nix/action.yml @@ -0,0 +1,34 @@ +name: Set up Nix +description: >- + Install Nix and point it at the shared `argo-workflows` Cachix cache, so that + a job entering the development shell downloads the toolchain instead of + building it. Every job that runs `nix develop` should use this action rather + than installing Nix itself, so there is one place to change the installer + version and the cache configuration. + + Pull-only unless the caller passes `cachix-auth-token`. GitHub does not give + secrets to workflows triggered by pull requests from forks, so those jobs read + the cache and never write to it, whatever the caller asks for. + +inputs: + cachix-auth-token: + description: >- + Cachix token with write access to the cache, i.e. + `secrets.CACHIX_AUTH_TOKEN`. Pass it only from jobs whose whole purpose is + to populate the cache; leave it unset everywhere else and the job pulls + without pushing. + default: "" + +runs: + using: composite + steps: + # The installer turns on `nix-command` and `flakes`, and passes the job's + # GITHUB_TOKEN to Nix so that flake inputs fetched from GitHub are not + # rate-limited. + - uses: cachix/install-nix-action@630ae543ea3a38a9a4166f03376c02c50f408342 # v31.11.0 + - uses: cachix/cachix-action@5f2d7c5294214f71b873db4b969586b980625e71 # v17 + with: + name: argo-workflows + authToken: ${{ inputs.cachix-auth-token }} + # Pushing without a token fails the job, and most jobs have no token. + skipPush: ${{ inputs.cachix-auth-token == '' }} diff --git a/.github/workflows/nix.yaml b/.github/workflows/nix.yaml new file mode 100644 index 000000000000..f428ed37c67c --- /dev/null +++ b/.github/workflows/nix.yaml @@ -0,0 +1,74 @@ +# Keeps the Cachix cache stocked with the development shell, so entering it is a +# download everywhere else: on a contributor's machine, in the dev container and +# in CI. Nothing depends on this workflow succeeding — a cold cache only means +# the next `nix develop` builds the tools itself, slowly. +# +# Pushing needs a CACHIX_AUTH_TOKEN repository secret with write access to the +# `argo-workflows` cache. Without one — on a fork — the jobs still build the +# shell, they only read from the cache. +name: Nix + +on: + push: + branches: + - main + paths: # keep in step with the pull_request list below + - flake.nix + - flake.lock + - go.mod # the flake takes the Go version from here + - .github/actions/setup-nix/action.yml + - .github/workflows/nix.yaml + # So that a change to the toolchain is built and reviewed before it is merged, + # rather than breaking everyone's shell afterwards. + pull_request: + paths: + - flake.nix + - flake.lock + - go.mod + - .github/actions/setup-nix/action.yml + - .github/workflows/nix.yaml + # The tools also stop being cached when nothing changes: Cachix evicts, and + # nixpkgs garbage-collects what our closure is built against. + schedule: + - cron: "0 4 * * 1" # Mondays, ahead of the working week + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + linux: + name: Build development shell (x86_64-linux) + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: ./.github/actions/setup-nix + with: + # Pull requests only read the cache, whoever opened them: a branch + # anyone can push to must not be able to write to a cache everyone + # else trusts. + cachix-auth-token: ${{ github.event_name != 'pull_request' && secrets.CACHIX_AUTH_TOKEN || '' }} + - name: Build the development shell + # Realises every tool the shell puts on PATH. Cachix pushes each one as + # it is built; anything substituted from cache.nixos.org is already + # public and is not copied into our cache. + run: nix develop --profile "$RUNNER_TEMP/devshell" --command true + + darwin: + name: Build development shell (aarch64-darwin) + # A macOS runner bills at ten times the Linux rate, so this one sits out + # pull requests and warms the cache once the change is merged. Apple + # silicon only: nobody develops on x86_64-darwin any more. + if: github.event_name != 'pull_request' + runs-on: macos-15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: ./.github/actions/setup-nix + with: + cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }} + - name: Build the development shell + run: nix develop --profile "$RUNNER_TEMP/devshell" --command true diff --git a/docs/running-locally.md b/docs/running-locally.md index 552fa6554d85..1e8c2d0a18b8 100644 --- a/docs/running-locally.md +++ b/docs/running-locally.md @@ -41,6 +41,21 @@ whatever the shell provides is what `make codegen`, `make lint` and `make test` If you use [direnv](https://direnv.net), `direnv allow` once and the shell is entered whenever you `cd` into the repo. Install [nix-direnv](https://github.com/nix-community/nix-direnv) alongside it so that is instant. +#### Binary cache + +Most of the shell comes from the public nixpkgs cache, but the pinned tools are built by this project and nobody else, +so they are not in it. CI builds them for `x86_64-linux` and `aarch64-darwin` and pushes them to the +[`argo-workflows`](https://app.cachix.org/cache/argo-workflows) [Cachix](https://cachix.org) cache. Point Nix at it and +entering the shell is a download rather than a compile: + +```bash +nix run nixpkgs#cachix -- use argo-workflows +``` + +That writes the cache and its public key into your Nix configuration; on a multi-user install it will ask for `sudo`, +or tell you to add yourself to `trusted-users` first. It is optional — without it everything still works, it is just +slower the first time and after each tool upgrade. + #### Upgrading a tool Everything lives in `flake.nix`, in two tiers: