diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dccb26da3..ec810c320 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -371,11 +371,16 @@ jobs: # 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). + # Run each crate in its OWN `cargo test` invocation — NOT one + # multi-package invocation. Building several crates together unifies + # perry-runtime's cargo features, which turns on optional impls (e.g. + # `fetch`) whose extern symbols (`js_fetch_with_options`) live in a + # separate crate the other test binaries don't link → `undefined + # reference` at link. Per-package builds keep each crate's + # perry-runtime feature set isolated. CARGO_BUILD_JOBS=1 also bounds + # the heavy per-binary runtime link so the runner doesn't OOM. + # `--with-tests` drops crates whose `src/` has no unit tests (their + # lib test binary would link the runtime for zero tests). export CARGO_BUILD_JOBS=1 # perry-runtime first, single-threaded (process-global state); it is # a lib-only crate, so filter to --lib. @@ -385,20 +390,19 @@ jobs: 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 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 + for package in $rest; do + # `cargo test --lib` errors on a bin-only crate (perry), so pick + # the target filter per package: --lib --bins when it has a lib + # (lenient if it has no bins), else --bins. + if printf '%s\n' "$package" | python3 scripts/ci_test_scope.py --has-lib; then target_filter="--lib --bins" else target_filter="--bins" fi - echo "Running: cargo test $target_filter $pkg_args" - cargo test $target_filter $pkg_args - fi + echo "::group::cargo test $target_filter -p $package" + cargo test $target_filter -p "$package" + echo "::endgroup::" + done fi # --------------------------------------------------------------------------- diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a7e42c06..dfc1d2441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +## v0.5.1187 — fix(ci): per-PR cargo-test — per-package runs + don't fan into FFI shims (feature-unification link errors) + +Follow-up to #5411/#5413. Two more issues made the fast per-PR path fail on +foundational diffs (#5402): + +1. **Feature-unification link errors.** Testing several crates in ONE + `cargo test --lib --bins -p A -p B ...` invocation unifies perry-runtime's + cargo features across them. A crate that enables an optional impl (e.g. + `fetch`) turns on perry-runtime's reference to `js_fetch_with_options`, whose + definition lives in a *separate* crate (perry-ext-fetch / perry-stdlib) that + the other test binaries don't link → `undefined reference` at link. Fix: run + **each crate in its own `cargo test` invocation** so feature sets stay isolated + (mirrors how the pre-#5411 job looped per-package). +2. **Over-broad fan-out.** A perry-runtime change reverse-dep-closured into ~40 + FFI-shim crates (`perry-ext-*`, perry-stdlib), each triggering a perry-runtime + feature rebuild. Those crates' UNIT tests are self-contained pure-Rust logic + that don't exercise runtime internals (the nightly full run + perry's + integration tests cover that interaction). Fix: `ci_test_scope.py` no longer + fans *into* `perry-ext-*` / perry-stdlib (`_is_fanout_leaf`); a direct change + to one still selects it. A perry-runtime change now selects 4 crates (perry, + perry-ffi, perry-runtime, perry-updater) instead of ~50. + +Net: per-PR cargo-test on a perry-runtime/codegen/hir change runs ~12 core-crate +unit-test suites per-package, validated locally green in ~2 min warm. + ## 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 diff --git a/CLAUDE.md b/CLAUDE.md index 14e4f69a4..db98aee68 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.1186 +**Current Version:** 0.5.1187 ## TypeScript Parity Status diff --git a/Cargo.lock b/Cargo.lock index e6762a47d..52a4335a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5283,7 +5283,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "base64", @@ -5340,14 +5340,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "cc", "libc", @@ -5355,7 +5355,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "log", @@ -5370,7 +5370,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-hir", @@ -5379,7 +5379,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-hir", @@ -5387,7 +5387,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-dispatch", @@ -5397,7 +5397,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-hir", @@ -5406,7 +5406,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "base64", @@ -5419,7 +5419,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-hir", @@ -5427,7 +5427,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "async-trait", @@ -5456,14 +5456,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "serde", "serde_json", @@ -5471,7 +5471,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1186" +version = "0.5.1187" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5482,7 +5482,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "clap", @@ -5497,14 +5497,14 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-argon2" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "argon2", "perry-ffi", @@ -5512,7 +5512,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "reqwest", @@ -5521,7 +5521,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "bcrypt", "perry-ffi", @@ -5529,7 +5529,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "rusqlite", @@ -5537,7 +5537,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "scraper", @@ -5545,7 +5545,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "perry-runtime", @@ -5553,7 +5553,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "chrono", "cron 0.16.0", @@ -5563,7 +5563,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "chrono", "perry-ffi", @@ -5571,7 +5571,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "rust_decimal", @@ -5579,7 +5579,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "serde_json", @@ -5587,7 +5587,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "rand 0.8.6", @@ -5595,7 +5595,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "perry-runtime", @@ -5603,14 +5603,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "bytes", "http-body-util", @@ -5627,7 +5627,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lazy_static", "perry-ffi", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lazy_static", "perry-ext-http-server", @@ -5652,7 +5652,7 @@ dependencies = [ [[package]] name = "perry-ext-http-server" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "bytes", "h2", @@ -5675,7 +5675,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lazy_static", "perry-ffi", @@ -5685,7 +5685,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "jsonwebtoken", @@ -5696,7 +5696,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lru", "perry-ffi", @@ -5704,7 +5704,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "chrono", "perry-ffi", @@ -5712,7 +5712,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "bson", "futures-util", @@ -5724,7 +5724,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "chrono", "perry-ffi", @@ -5734,7 +5734,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "nanoid", "perry-ffi", @@ -5743,7 +5743,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "perry-runtime", @@ -5755,7 +5755,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lettre", "perry-ffi", @@ -5765,7 +5765,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "printpdf", @@ -5773,7 +5773,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "sqlx", @@ -5782,7 +5782,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "governor", "perry-ffi", @@ -5790,7 +5790,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "image", @@ -5799,14 +5799,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "lazy_static", "perry-ffi", @@ -5815,7 +5815,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "uuid", @@ -5823,7 +5823,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ffi", "regex", @@ -5833,7 +5833,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "futures-util", "lazy_static", @@ -5845,7 +5845,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "brotli", "flate2", @@ -5854,7 +5854,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "dashmap", "once_cell", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-api-manifest", @@ -5881,7 +5881,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-diagnostics", @@ -5893,7 +5893,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "base64", @@ -5926,7 +5926,7 @@ dependencies = [ [[package]] name = "perry-stdlib" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "aes 0.8.4", "aes-gcm", @@ -6018,7 +6018,7 @@ dependencies = [ [[package]] name = "perry-transform" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "perry-hir", @@ -6028,7 +6028,7 @@ dependencies = [ [[package]] name = "perry-types" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "anyhow", "thiserror 1.0.69", @@ -6036,14 +6036,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "itoa", @@ -6060,7 +6060,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "rand 0.8.6", "serde", @@ -6070,7 +6070,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "cairo-rs", @@ -6093,7 +6093,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "block2", @@ -6109,7 +6109,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "block2", @@ -6124,7 +6124,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1186" +version = "0.5.1187" [[package]] name = "perry-ui-test" @@ -6132,11 +6132,11 @@ version = "0.1.0" [[package]] name = "perry-ui-testkit" -version = "0.5.1186" +version = "0.5.1187" [[package]] name = "perry-ui-tvos" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "block2", @@ -6152,7 +6152,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "block2", @@ -6168,7 +6168,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "block2", "libc", @@ -6181,7 +6181,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "libc", @@ -6198,14 +6198,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "base64", "ed25519-dalek", @@ -6219,7 +6219,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1186" +version = "0.5.1187" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 3de0fc748..26fda6ef3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,7 +215,7 @@ strip = false codegen-units = 16 [workspace.package] -version = "0.5.1186" +version = "0.5.1187" 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 6b742198c..a39be9117 100755 --- a/scripts/ci_test_scope.py +++ b/scripts/ci_test_scope.py @@ -102,8 +102,26 @@ def _runtime_link_augment(seeds): return augmented +def _is_fanout_leaf(name): + """Crates we never fan *into* when a dependency changes. + + `perry-ext-*` and `perry-stdlib` are runtime FFI shims whose UNIT tests are + self-contained pure-Rust logic — they do not exercise perry-runtime internals, + so a perry-runtime change need not re-run them per-PR (the nightly full run + + perry's integration tests cover that interaction). Excluding them from the + reverse-dep fan-out keeps a foundational change from selecting ~40 crates, and + avoids perry-runtime feature-unification rebuilds. A direct change *to* one of + these crates still selects it (it starts as a seed). + """ + return name == "perry-stdlib" or name.startswith("perry-ext-") + + def _reverse_dep_closure(md, seeds): - """All workspace members that transitively depend on any package in `seeds`.""" + """All workspace members that transitively depend on any package in `seeds`. + + Fan-out skips `_is_fanout_leaf` crates (they are not added as dependents and + are not traversed), so a foundational change does not pull in every FFI shim. + """ members = {p["name"] for p in md["packages"]} # revdeps[x] = packages that directly depend on x revdeps = {} @@ -116,9 +134,10 @@ def _reverse_dep_closure(md, seeds): while stack: cur = stack.pop() for dependent in revdeps.get(cur, ()): - if dependent not in affected: - affected.add(dependent) - stack.append(dependent) + if dependent in affected or _is_fanout_leaf(dependent): + continue + affected.add(dependent) + stack.append(dependent) return affected