diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 131c210e3..dccb26da3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -368,21 +368,28 @@ jobs: # auto-optimize *integration* tests (tests/*.rs — each shells out to # `perry compile`, ~4–6 min apiece, ~30 of them) are NOT run per-PR; # they run in the nightly full job, on release tags, and on demand - # via the `run-extended-tests` label. Unit-test binaries are small, - # so builds parallelize safely (no serialization / clean churn) and - # there is no staticlib dependency to build. + # via the `run-extended-tests` label. No staticlib dependency to + # build (no integration tests). # + # Each crate's test binary statically links the whole runtime, so + # building many at once OOMs the runner (same reason the FULL path + # uses CARGO_BUILD_JOBS=1). Serialize here too, and drop crates with + # no `src/` unit tests (their lib test binary has zero tests yet + # still pays the heavy link). + export CARGO_BUILD_JOBS=1 # perry-runtime first, single-threaded (process-global state); it is # a lib-only crate, so filter to --lib. if printf '%s\n' "$scope" | grep -qx 'perry-runtime'; then RUST_TEST_THREADS=1 cargo test --lib -p perry-runtime fi - rest="$(printf '%s\n' "$scope" | grep -vx 'perry-runtime' || true)" + rest="$(printf '%s\n' "$scope" | grep -vx 'perry-runtime' \ + | python3 scripts/ci_test_scope.py --with-tests || true)" + echo "Crates with unit tests in scope:"; printf '%s\n' "$rest" if [ -n "$rest" ]; then - # One parallel invocation across the rest of the scope. `cargo test - # --lib` errors if NO selected package has a library (a perry-only - # diff → just the bin-only `perry` crate), so include --lib only - # when some selected crate has one; --bins is always safe. + # One invocation across the rest of the scope. `cargo test --lib` + # errors if NO selected package has a library (a perry-only diff → + # just the bin-only `perry` crate), so include --lib only when some + # selected crate has one; --bins is always safe. pkg_args="$(printf '%s\n' "$rest" | sed 's/^/-p /' | tr '\n' ' ')" if printf '%s\n' "$rest" | python3 scripts/ci_test_scope.py --has-lib; then target_filter="--lib --bins" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e569c348..5a7e42c06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## v0.5.1186 — fix(ci): per-PR cargo-test link-OOM — serialize fast-path links + skip zero-unit-test crates + +The #5411 fast per-PR path built every affected crate's unit-test binary in one +unbounded-parallel `cargo test --lib --bins -p ...` invocation. Each test binary +statically links the whole runtime, so a wide-fan-out diff (a perry-runtime change +selects ~50 crates) linked ~50 huge binaries at once and the runner OOMed +(`linking with \`cc\` failed`) — the exact failure the FULL path avoids with +`CARGO_BUILD_JOBS=1`. Surfaced on #5402 (Tier 2 touches perry-runtime). + +- `.github/workflows/test.yml` — the fast path now sets `CARGO_BUILD_JOBS=1` + (serialize the heavy links; no OOM) and filters the scope through + `ci_test_scope.py --with-tests`, dropping crates with no `src/` unit tests + (~13 zero-test FFI shims in the wide case) whose lib test binary would link the + runtime for zero tests. +- `scripts/ci_test_scope.py` — new `--with-tests` mode: prints the subset of stdin + package names whose `src/` contains a `#[test]` / `#[tokio::test]` (unit tests + the `--lib --bins` filter actually runs; integration-only crates are excluded). + ## v0.5.1185 — perf(ci): make per-PR cargo-test fast (<10 min) — unit tests for affected crates; integration tests move to nightly/tags The `cargo-test` gate took ~90 min: it built the **entire workspace** in debug, diff --git a/CLAUDE.md b/CLAUDE.md index d23c68ce0..14e4f69a4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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.1185 +**Current Version:** 0.5.1186 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index 999dfaa09..e6762a47d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5283,7 +5283,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "base64", @@ -5340,14 +5340,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "cc", "libc", @@ -5355,7 +5355,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "log", @@ -5370,7 +5370,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-hir", @@ -5379,7 +5379,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-hir", @@ -5387,7 +5387,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-dispatch", @@ -5397,7 +5397,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-hir", @@ -5406,7 +5406,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "base64", @@ -5419,7 +5419,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-hir", @@ -5427,7 +5427,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "async-trait", @@ -5456,14 +5456,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "serde", "serde_json", @@ -5471,7 +5471,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1185" +version = "0.5.1186" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5482,7 +5482,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "clap", @@ -5497,14 +5497,14 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-argon2" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "argon2", "perry-ffi", @@ -5512,7 +5512,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "reqwest", @@ -5521,7 +5521,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "bcrypt", "perry-ffi", @@ -5529,7 +5529,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "rusqlite", @@ -5537,7 +5537,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "scraper", @@ -5545,7 +5545,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "perry-runtime", @@ -5553,7 +5553,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "chrono", "cron 0.16.0", @@ -5563,7 +5563,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "chrono", "perry-ffi", @@ -5571,7 +5571,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "rust_decimal", @@ -5579,7 +5579,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "serde_json", @@ -5587,7 +5587,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "rand 0.8.6", @@ -5595,7 +5595,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "perry-runtime", @@ -5603,14 +5603,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "bytes", "http-body-util", @@ -5627,7 +5627,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lazy_static", "perry-ffi", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lazy_static", "perry-ext-http-server", @@ -5652,7 +5652,7 @@ dependencies = [ [[package]] name = "perry-ext-http-server" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "bytes", "h2", @@ -5675,7 +5675,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lazy_static", "perry-ffi", @@ -5685,7 +5685,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "jsonwebtoken", @@ -5696,7 +5696,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lru", "perry-ffi", @@ -5704,7 +5704,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "chrono", "perry-ffi", @@ -5712,7 +5712,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "bson", "futures-util", @@ -5724,7 +5724,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "chrono", "perry-ffi", @@ -5734,7 +5734,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "nanoid", "perry-ffi", @@ -5743,7 +5743,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "perry-runtime", @@ -5755,7 +5755,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lettre", "perry-ffi", @@ -5765,7 +5765,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "printpdf", @@ -5773,7 +5773,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "sqlx", @@ -5782,7 +5782,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "governor", "perry-ffi", @@ -5790,7 +5790,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "image", @@ -5799,14 +5799,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "lazy_static", "perry-ffi", @@ -5815,7 +5815,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "uuid", @@ -5823,7 +5823,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ffi", "regex", @@ -5833,7 +5833,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "futures-util", "lazy_static", @@ -5845,7 +5845,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "brotli", "flate2", @@ -5854,7 +5854,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "dashmap", "once_cell", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-api-manifest", @@ -5881,7 +5881,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-diagnostics", @@ -5893,7 +5893,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "base64", @@ -5926,7 +5926,7 @@ dependencies = [ [[package]] name = "perry-stdlib" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "aes 0.8.4", "aes-gcm", @@ -6018,7 +6018,7 @@ dependencies = [ [[package]] name = "perry-transform" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "perry-hir", @@ -6028,7 +6028,7 @@ dependencies = [ [[package]] name = "perry-types" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "anyhow", "thiserror 1.0.69", @@ -6036,14 +6036,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "itoa", @@ -6060,7 +6060,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "rand 0.8.6", "serde", @@ -6070,7 +6070,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "cairo-rs", @@ -6093,7 +6093,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "block2", @@ -6109,7 +6109,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "block2", @@ -6124,7 +6124,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1185" +version = "0.5.1186" [[package]] name = "perry-ui-test" @@ -6132,11 +6132,11 @@ version = "0.1.0" [[package]] name = "perry-ui-testkit" -version = "0.5.1185" +version = "0.5.1186" [[package]] name = "perry-ui-tvos" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "block2", @@ -6152,7 +6152,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "block2", @@ -6168,7 +6168,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "block2", "libc", @@ -6181,7 +6181,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "libc", @@ -6198,14 +6198,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "base64", "ed25519-dalek", @@ -6219,7 +6219,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1185" +version = "0.5.1186" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 02653513f..3de0fc748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,7 +215,7 @@ strip = false codegen-units = 16 [workspace.package] -version = "0.5.1185" +version = "0.5.1186" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/scripts/ci_test_scope.py b/scripts/ci_test_scope.py index a2c0c8865..6b742198c 100755 --- a/scripts/ci_test_scope.py +++ b/scripts/ci_test_scope.py @@ -26,6 +26,8 @@ Usage: | python3 scripts/ci_test_scope.py [--full] """ import json +import os +import re import subprocess import sys @@ -137,9 +139,54 @@ def _has_lib_mode() -> int: return 0 if has else 1 +_TEST_ATTR = re.compile(r"#\[\s*(?:tokio::)?test\b") + + +def _with_tests_mode() -> int: + """Print the subset of stdin package names whose source contains unit tests. + + The per-PR fast path serializes test-binary links to avoid OOM (each crate's + test binary statically links the whole runtime); building a test binary for a + crate with *zero* `#[test]`s is pure wasted link time. Filtering them out + keeps the wide-fan-out case (a perry-runtime change selects ~50 crates, ~30 of + which are zero-test FFI shims) under the time budget. Conservative: a crate is + kept if any `.rs` under its dir has a `#[test]` / `#[tokio::test]` attribute. + """ + names = set(sys.stdin.read().split()) + md = _load_metadata() + for p in md["packages"]: + if p["name"] not in names: + continue + # Only scan `src/` — the fast path runs `--lib --bins`, i.e. unit tests + # compiled into the lib/bin targets. Integration tests under `tests/` + # are NOT run per-PR, so a crate whose only `#[test]`s live there must + # not be selected (its lib test binary would have zero tests yet still + # pay the heavy runtime link). + crate_dir = os.path.join(os.path.dirname(p["manifest_path"]), "src") + has_test = False + for root, _dirs, files in os.walk(crate_dir): + for fname in files: + if not fname.endswith(".rs"): + continue + try: + with open(os.path.join(root, fname), encoding="utf-8", errors="ignore") as fh: + if _TEST_ATTR.search(fh.read()): + has_test = True + break + except OSError: + pass + if has_test: + break + if has_test: + print(p["name"]) + return 0 + + def main() -> int: if "--has-lib" in sys.argv: return _has_lib_mode() + if "--with-tests" in sys.argv: + return _with_tests_mode() full = "--full" in sys.argv changed = [line.strip() for line in sys.stdin if line.strip()]