Skip to content

ci(release): warm the release profile in R2 from main every night - #344

Open
hyperb1iss wants to merge 1 commit into
mainfrom
nova/release-cache-warm
Open

hyperb1iss wants to merge 1 commit into
mainfrom
nova/release-cache-warm

Conversation

@hyperb1iss

Copy link
Copy Markdown
Owner

What this changes

Release builds stop compiling the release profile from scratch on every tag. A new warm mode for the CI/CD dispatch runs the real release build jobs on main (web assets, Native App for Windows and macOS, and the Linux release lanes) without signing and without the normal CI lanes. Because it runs on main, every compile it performs lands in the shared R2 cache, and the next tag's release builds read those entries instead of compiling them. A new Release Cache Warm workflow dispatches it every night, and RELEASING.md covers dispatching it by hand before a release that follows a lockfile or toolchain change.

Why

Tags read R2 but never write it, and main never built the release profile, so no release-profile compile was ever cached. The 0.6.1 tag run shows the cost: the macOS sidecar build missed 1,954 of 1,956 cacheable units and took 232 minutes, making Native App (macos-arm64) a 250-minute job and the long pole of every release. The Windows sidecar build missed 2,422 units and took 101 minutes.

How it works

Dispatch or event Normal CI lanes Release builds Signing and publishing
Push to main, pull request Run Skipped Skipped
Tag push Run Run, reading R2 Run
full dispatch Run Run macOS signing runs; nothing publishes
warm dispatch from main Skipped, like smoke Run, writing R2 Skipped
warm dispatch from any other ref Skipped Refused Refused

The last row closes a real hole. The macOS signing job runs for any tag ref, so a warm dispatch from a tag would have notarized. The credentials job, which every signing and release build job needs, now fails a warm run that is not on main.

Supporting changes:

  • Concurrency. Warm runs take their own concurrency group. The main group never cancels in progress, so a multi-hour warm run would otherwise leave main pushes pending, and a newer push cancels an older pending run.
  • Actions cache budget. Web assets, Native App, and the Linux release lanes set save-if: "false". A warm run never saves a release target directory into the Actions cache, and R2 still receives every compile because its write mode follows the trusted ref, not save-if.
  • Tauri. The Tauri bundle steps run cargo themselves, outside the cache wrappers, so they now set RUSTC_WRAPPER: sccache and the app's own dependency tree is cached too.
  • Artifacts. Warm uploads expire after one day; other runs keep their retention (0 is upload-artifact's "repository default").
  • Release gate. release.yml's "Require passing CI for the release source" ignores warm runs: it accepts only main pushes and the tag's own runs.

Verification

  • Added or updated tests
  • Added or updated docs (README, AGENTS.md, relevant spec, or guide)
  • just verify passes locally (Rust fmt + lint + test) (not applicable: no Rust changes)
  • just deny passes (required for dependency or license changes)
  • just ui-test and just ui-build pass (required for crates/hypercolor-ui/)
  • just sdk-lint, just sdk-check, and just sdk-build pass (required for sdk/)
  • just python-verify passes (required for python/)
  • just compat-check passes (required for data/drivers/vendors/*.toml)
  • just docs-build passes (required for docs or README changes)
  • cd docs && zola check passes (required for docs link/content changes)
  • Packaging scripts were syntax-checked (required for scripts/ or packaging/)
  • just e2e-build passes with the normal Servo stack (required for daemon/UI/effect integration changes)
  • just e2e-build-cpu passes when validating the CPU smoke fallback
  • just e2e passes against the Servo stack (required for end-to-end behavior changes; starts daemon/browser)
  • Tested on real hardware, simulator, or e2e harness (describe below)

The new scripts/tests/release-cache-warm.test.mjs evaluates the real if: expressions from ci.yml for each trigger. It checks which jobs run on warm, that nothing signs or publishes, that warm is refused off main, the changes-job outputs, and the concurrency group. It also checks the save-if, retention, and Tauri wiring, plus the scheduler. Every mutation I tried was caught, including dropping or inverting the refusal step, signing on warm, sharing the concurrency group, and saving an archive from a release lane. The macOS release test's "signing runs exactly when the build runs" check now allows exactly one exception: warm builds unsigned.

All workflow contract suites pass locally: node --test .github/actions/rust-build-cache/*.test.mjs scripts/tests/*.test.mjs, 72 tests, with sccache 0.17.0. One macOS release test needs shasum, which I shimmed on Linux; CI has it. actionlint reports nothing new.

An independent review confirmed three more things. A tag build should hit a warm run's dependency entries: neither version step exports env, the tauri.conf.json version patch reaches only the uncached app binary, and main's last Windows run already hit R2 on 584 of 586 units. CARGO_INCREMENTAL=0 reaches the Tauri steps. And a GITHUB_TOKEN dispatch creates a run, which release.yml already relies on.

The real proof needs a merge. The first warm run on main compiles cold and fills R2, and then the next tag's Native App jobs should report mostly cache hits. I'll dispatch the first warm run by hand once this lands rather than wait for the schedule.

Notes for reviewers

  • Known gaps in the nightly run:
    • A warm run that fails notifies nobody, because github-actions[bot] dispatched it and the scheduler job itself succeeds.
    • A night with no new commits on main still repeats every release link, because sccache never caches links.
  • Trust model: shipped release binaries will now be built mostly from R2 objects that main wrote. A leaked read-write key could poison them without a commit. This is the two-key model SERVO_BUILD_CACHING.md already documents, and only main can reach that key.

🤖 Generated with Claude Code

Release builds run only on tags and release dispatches, and tags read
the shared compiler cache without writing it, so nothing ever stored a
release-profile compile. In 0.6.1 the macOS sidecar build missed 1,954
of 1,956 cacheable units and took 232 minutes; the Windows one missed
2,422 and took 101.

CI/CD gains a warm dispatch mode. From main it runs the real release
build jobs (credentials check, web assets, Native App, Linux release)
without signing, and skips the normal CI lanes the way smoke does.
Signing and publishing stay on tags and full dispatches. Running on
main, warm writes every compile to R2 for the next tag to read. A new
Release Cache Warm workflow dispatches it nightly, and RELEASING.md
says when to run it by hand.

Signing runs for any tag ref, so the credentials job, which every
signing and release build job needs, refuses warm outside main. Warm
also takes its own concurrency group: the main group never cancels, so
a long warm run would otherwise leave main pushes pending, and a newer
push cancels the older pending run.

Web assets and the release lanes set save-if false so release
dispatches never save their target directories into the Actions
budget; R2 still receives their compiles. Warm uploads expire after a
day. The Tauri bundle steps run cargo outside the cache wrappers, so
they name RUSTC_WRAPPER directly and the app's dependency tree is
cached too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hyperb1iss
hyperb1iss force-pushed the nova/release-cache-warm branch from 9ce4297 to c6f1347 Compare October 2, 2026 23:24
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 6 billable files and costs up to $1.50.

Or wait 32 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2fe39bad-bf24-42fe-9880-d2ec1caf2d2e
📥 Commits

Reviewing files that changed from the base of the PR and between 874e553 and c6f1347.

📒 Files selected for processing (6)
  • .github/workflows/ci.yml
  • .github/workflows/release-cache-warm.yml
  • docs/development/RELEASING.md
  • docs/development/SERVO_BUILD_CACHING.md
  • scripts/tests/macos-release.test.mjs
  • scripts/tests/release-cache-warm.test.mjs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

@hyperb1iss hyperb1iss closed this Oct 3, 2026
@hyperb1iss
hyperb1iss deleted the nova/release-cache-warm branch October 3, 2026 00:44
@hyperb1iss
hyperb1iss restored the nova/release-cache-warm branch October 3, 2026 00:44
@hyperb1iss hyperb1iss reopened this Oct 3, 2026

This branch has not been deployed

No deployments
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.

1 participant