Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .github/workflows/cache-warm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Warm CI cache

# WHY THIS EXISTS
# ---------------
# The `Tests` workflow (test.yml) runs ONLY on pull_request + version tags —
# never on push to main. GitHub Actions cache scoping means a run can only
# restore caches saved on its OWN branch or on the DEFAULT branch (main); one
# PR can never read another PR's cache. So with nothing running the cache-
# producing build on main, no main-scoped cache ever existed, and every PR
# started cold:
# - Swatinem/rust-cache in test.yml has `save-if: refs/heads/main`, but
# test.yml never runs on main → it never saved target/.
# - sccache (now a persisted disk cache, v0.5.1179) likewise only saw
# PR-scoped saves, invisible to other PRs.
# Result: cargo-test recompiled the whole dependency graph cold every run
# (~90-103 min), tipping over its timeout on perry-runtime/perry-codegen PRs.
#
# This workflow runs the cache-producing build ONCE per main merge so the
# main-scoped rust-cache (target/) and sccache disk cache exist for every PR
# to restore. It uses the SAME shared-key / sccache key prefix as test.yml's
# jobs, so those jobs warm-restore from here. It is best-effort
# (continue-on-error), never a gate.
on:
push:
branches: [main]
paths:
- 'crates/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/cache-warm.yml'
workflow_dispatch:

concurrency:
# Only the latest main commit needs a warm cache; cancel older in-flight warms.
group: cache-warm-${{ github.ref }}
cancel-in-progress: true

jobs:
warm:
runs-on: ubuntu-latest
# Best-effort cache population — must never block or fail anything.
continue-on-error: true
# Comfortably above the cold first run; later runs are far shorter.
timeout-minutes: 120
env:
RUSTC_WRAPPER: sccache
# Mirror test.yml's sccache config EXACTLY (local disk cache, NOT the GHA
# backend) so the cache this job saves is byte-compatible with what the
# cargo-test / api-docs-drift / compiler-output-regression jobs restore.
SCCACHE_GHA_ENABLED: "false"
SCCACHE_DIR: ${{ github.workspace }}/.sccache
SCCACHE_CACHE_SIZE: "12G"
CARGO_INCREMENTAL: "0"
# Keep artifacts small so building the heavy crates' test binaries does
# not exhaust the shared-runner disk (same reason test.yml sets these).
CARGO_PROFILE_TEST_DEBUG: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS: "-C linker-features=-lld"
steps:
- uses: actions/checkout@v6

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Install sccache
uses: mozilla-actions/sccache-action@v0.0.10

# Save the sccache disk cache under the SAME prefix test.yml restores
# (`sccache-<os>-perry-`). This runs on main, so the saved entry is
# main-scoped → restorable by every PR. run_id keeps the key unique.
- name: Cache sccache objects
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/.sccache
key: sccache-${{ runner.os }}-perry-warm-${{ github.run_id }}
restore-keys: |
sccache-${{ runner.os }}-perry-

# Same shared-key as test.yml; save-if true because this only runs on main.
- uses: Swatinem/rust-cache@v2
with:
shared-key: "${{ runner.os }}-perry"
save-if: "true"

# Compile (do NOT run) the test binaries of the heaviest crates. This
# pulls in essentially the entire third-party dependency graph plus the
# big first-party crates — the bulk of cargo-test's compile time — so the
# cheap ext-* crates that follow in cargo-test reuse the cached units.
# `--no-run` skips execution (this is a cache warm, not a test run); the
# per-crate prune keeps target/ under the runner's disk budget.
- name: Warm build (heavy crates)
run: |
for pkg in perry-runtime perry-stdlib perry-codegen perry; do
echo "::group::cargo test -p $pkg --no-run"
cargo test -p "$pkg" --no-run
echo "::endgroup::"
find target/debug/deps -maxdepth 1 -type f -perm -111 ! -name '*.so' -delete || true
done

- name: sccache stats
if: always()
run: sccache --show-stats || true
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## v0.5.1180 — ci: warm the main-scoped CI cache so PRs stop building cold

Follow-up to v0.5.1179. Moving sccache to a persisted disk cache was necessary
but not sufficient: `test.yml` runs only on pull_request + version tags, never
on push to main. GitHub Actions cache scoping lets a run restore caches from its
own branch or the default branch (main) — but never another PR's — so with
nothing running the cache-producing build on main, no main-scoped cache ever
existed and every PR started cold. (The same flaw silently disabled
`Swatinem/rust-cache`: its `save-if: refs/heads/main` never fired because
test.yml doesn't run on main, so target/ was never saved either.)

New `cache-warm.yml`: on every push to main that touches Rust (`crates/**`,
`Cargo.toml`, `Cargo.lock`), compile — `--no-run` — the test binaries of the
heaviest crates (`perry-runtime`, `perry-stdlib`, `perry-codegen`, `perry`),
which pull in essentially the whole dependency graph. It saves under the SAME
`rust-cache` shared-key (`<os>-perry`) and sccache key prefix
(`sccache-<os>-perry-`) that test.yml's jobs restore, so the main-scoped caches
now exist for every PR to pick up. The job is `continue-on-error` (a cache warm,
never a gate), prunes test binaries between crates to stay within the runner
disk budget, and uses `concurrency` to cancel superseded warms.

Expected effect: after the first warm run lands on main, PR `cargo-test` should
restore a warm cache and run ~50-60 min instead of ~90-103 min cold. No
production code changes.

## v0.5.1179 — ci: move sccache off the GHA backend onto a persisted disk cache (fix cargo-test timeouts)

The `cargo-test` gate was timing out at its 120-min cap on PRs that touch
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation.

**Current Version:** 0.5.1179
**Current Version:** 0.5.1180


## TypeScript Parity Status
Expand Down
Loading
Loading