Skip to content

feat(ci): substitute from the shared binary cache - #59

Open
nikzen wants to merge 2 commits into
mainfrom
feat/nix-cache
Open

feat(ci): substitute from the shared binary cache#59
nikzen wants to merge 2 commits into
mainfrom
feat/nix-cache

Conversation

@nikzen

@nikzen nikzen commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Every workflow builds the SDKs and the vodozemac bindings from source, while
the famedly Cachix the rest of the company uses sits unused next to it.

Reading. A step in steps.setup, so every generated workflow gets it. It
may fail without failing the workflow: a cache out of reach should cost a run
its time and not its result.

Writing. Only main and tags. A pull request and a merge queue entry both
build what a branch decided, and a run there that could write would let any
branch put a store path in front of every other repository's builds. The ref
decides this rather than the event name, which gets two cases wrong: a run
started by hand is not a push, and a push is whatever branch a repository
chose to run on.

Filling. Nothing here runs on main today, so the cache would have stayed
empty no matter who read it. populate-binary-cache builds what every
repository pinning these standards resolves against — the devshell, both SDKs,
the Rust toolchain and vodozemac — and, unlike every other workflow, fails
loudly when it cannot hand them over. Other repositories need nothing: the
option is empty by default, and what a workflow on main builds anyway is
already pushed by the step in setup.

x86_64-linux only here, though the option reaches all three systems. Darwin
bills ten times the minutes to move SDK archives Google already built, which
shortens nobody's wait; the one thing genuinely compiled there is vodozemac, at
24 seconds. aarch64-linux became a standard runner in private repositories in
January and costs no more than x64, so it is worth revisiting once we know
whether the arm64 image builds downstream compile anything the cache could hand
them.

vodozemac had to become an unconditional package to be built here at all: it was exposed only where a Dart project asked for it, and this repository has none.

Supersedes #50. Conflicts with #56, which moves the vodozemac module and adds an import next to this one's; whichever lands second wants a rebase.

Test plan

  • nix flake check --all-systems, prek --all-files --stage pre-push
  • every generated workflow carries the read step, and only the filling one
    carries it without continue-on-error
  • skipPush is false only for refs/heads/main and refs/tags/*, so a
    pull request, a merge queue entry and a dispatch from a branch all read
  • the run on main after merge fills the cache, and a later pull request
    substitutes vodozemac instead of building it

Comment thread nix/general/workflows/ci-steps.nix Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6362449. Configure here.

Comment thread nix/general/workflows/ci-steps.nix Outdated
Comment thread nix/general/workflows/populate-binary-cache.nix Outdated
nikzen and others added 2 commits August 4, 2026 09:25
Every workflow built everything from source, including the SDKs, while the
cache the rest of the company uses sat next to it unused.

Only `main` and the tags write. What a pull request builds is decided by its
branch, and so is what a merge queue builds — an entry that is dequeued was
never on `main` at all. A run there that could write would let any branch put a
store path in front of every other repository's builds. The ref decides this
rather than the event name, which gets two cases wrong: a run started by hand
is not a `push`, and a `push` is whatever branch a repository chose to run on.

Reading may fail without failing the workflow: a cache out of reach should cost
a run its time and not its result. Writing may not, so the step takes
`{ required }` for the run whose purpose is to fill it.

Signed-off-by: Niklas Zender <n.zender@famedly.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The cache the commit before this one wired up has no writer here. This
repository's only workflow runs on pull requests and the merge queue, the queue
is not enabled at all, and nothing runs on `main` — so every run would read
from a cache that stays empty.

A workflow on `main` now builds what every repository pinning these standards
resolves against and hands it over: the devshell, both SDKs, the Rust
toolchain, and vodozemac. Unlike every other workflow it fails loudly when it
cannot, because the run that writes is the one every other repository waits on.

vodozemac is what earns the run. It is a Rust build rather than a download, it
is the same library for everyone, and it was only a package where a Dart
project had already asked for it — so the repository that could most cheaply
build it never did. It is unconditional now.

`x86_64-linux` only, though the option reaches all three systems. Darwin bills
ten times the minutes to move SDK archives Google already built, which shortens
nobody's wait, and the one thing genuinely compiled there takes 24 seconds.
GitHub's arm64 Linux runners reached private repositories in January against
the same free minutes as the x64 ones, so the option can name one once an arm64
image build downstream turns out to compile something worth caching.

Signed-off-by: Niklas Zender <n.zender@famedly.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants