diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 0ac2ec1fd6..58f11efc90 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -675,6 +675,20 @@ jobs: "$coverage_build_dir/install-base-python-locks.py" install -m 0755 "$trusted_vcs_import_root_resolver" \ "$coverage_build_dir/resolve-opencode-base-vcs-import-root.sh" + trusted_cargo_fixture="${GITHUB_WORKSPACE}/tests/fixtures/coverage-cargo" + if [ -L "$trusted_cargo_fixture" ] || [ -L "$trusted_cargo_fixture/src" ]; then + echo "::error::Trusted Cargo coverage fixture directories must not be symlinks." + exit 1 + fi + for fixture_file in Cargo.toml Cargo.lock src/lib.rs; do + if [ ! -f "$trusted_cargo_fixture/$fixture_file" ] || + [ -L "$trusted_cargo_fixture/$fixture_file" ]; then + echo "::error::Trusted Cargo coverage fixture is missing or not a regular file." + exit 1 + fi + install -D -m 0644 "$trusted_cargo_fixture/$fixture_file" \ + "$coverage_build_dir/coverage-cargo-fixtures/$fixture_file" + done python_change_files="${RUNNER_TEMP}/opencode-python-change-files" if ! git -C "$COVERAGE_SOURCE_WORKDIR" diff \ --name-only --diff-filter=ACMRTUXBD -z "$PR_BASE_SHA" HEAD \ @@ -783,6 +797,11 @@ jobs: vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* + COPY coverage-cargo-fixtures /tmp/coverage-cargo-fixtures + RUN CARGO_HOME=/opt/coverage-cargo-home cargo fetch --locked \ + --manifest-path /tmp/coverage-cargo-fixtures/Cargo.toml \ + && rm -rf /tmp/coverage-cargo-fixtures \ + && chmod -R a+rX /opt/coverage-cargo-home ENV LLVM_COV=/usr/bin/llvm-cov-19 ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 ENV COREPACK_HOME=/opt/corepack @@ -973,6 +992,7 @@ jobs: chown -R root:root /work/.git chmod -R go-w /work/.git fi + rm -rf -- /work/.opencode-sandbox-home mkdir -p "$RUNNER_TEMP" /work/.opencode-sandbox-home /work/.opencode-sandbox-cache chown "$OPENCODE_SANDBOX_UID:$OPENCODE_SANDBOX_GID" /work/.opencode-sandbox-home /work/.opencode-sandbox-cache # `run_and_capture`/`run_and_capture_advisory` below pin CARGO_HOME to @@ -980,12 +1000,15 @@ jobs: # the read-only image -- so the baked offline vendor config from # /opt/base-rust-dependencies (see materialize_base_rust_dependencies.py) has to be # copied there explicitly rather than set as an image ENV default. + mkdir -p /work/.opencode-sandbox-home/.cargo + cp -a /opt/coverage-cargo-home/. /work/.opencode-sandbox-home/.cargo/ if [ -s /opt/base-rust-dependencies/cargo-config.toml ]; then - mkdir -p /work/.opencode-sandbox-home/.cargo - install -m 0444 /opt/base-rust-dependencies/cargo-config.toml \ + install -m 0644 /opt/base-rust-dependencies/cargo-config.toml \ /work/.opencode-sandbox-home/.cargo/config.toml - chown -R "$OPENCODE_SANDBOX_UID:$OPENCODE_SANDBOX_GID" /work/.opencode-sandbox-home/.cargo fi + printf '\n[net]\noffline = true\n' >>/work/.opencode-sandbox-home/.cargo/config.toml + chmod 0444 /work/.opencode-sandbox-home/.cargo/config.toml + chown -R "$OPENCODE_SANDBOX_UID:$OPENCODE_SANDBOX_GID" /work/.opencode-sandbox-home/.cargo chmod 0700 "$RUNNER_TEMP" : >"$GITHUB_OUTPUT" chmod 0600 "$GITHUB_OUTPUT" diff --git a/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md b/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md new file mode 100644 index 0000000000..1b5d0b65fd --- /dev/null +++ b/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md @@ -0,0 +1,55 @@ +# OpenCode coverage Cargo fixture intake (2026-09-28) + +## Incident + +The current-head `.github#1026` OpenCode dispatch run `36348910783`, job +`108722448709`, reached the isolated coverage sandbox. Its full suite reported +four failures in `test_materialize_base_rust_dependencies.py` and +`test_maturin_offline_build_contract.py`, each before the tested behavior at +`cargo generate-lockfile` (3,538 passed, 4 failed, 4 skipped). The PR does not +change either test file. Both files construct registry-backed crates (`itoa`, +`ryu`, and PyO3) during the test, while the sandbox runs with `--network=none` +and its base-repository Rust materializer finds no Cargo lock in this PR's +validated base tree. + +With a fresh `CARGO_HOME` and `CARGO_NET_OFFLINE=true`, the two representative +tests failed at the same command. A direct `cargo generate-lockfile` on the +`itoa` fixture reported `no matching package named itoa found` in the offline +crates.io index. This establishes missing registry fixture input, rather than +a product-code assertion failure. The original hosted test helper captures +Cargo stderr, so the hosted log alone does not identify the missing crate. + +## Repair and trust boundary + +A trusted, lockfile-pinned fixture manifest covers the three registry crates +used by these tests. The networked coverage image build fetches that exact +closure with `cargo fetch --locked`. The PR tree never enters that build +context. The later untrusted test container still has `--network=none` and +receives only the trusted image's cached registry artifacts, copied into its +isolated `CARGO_HOME` after removing any PR-supplied sandbox home. Its trusted +Cargo config enables offline registry resolution, so a lockfile generated by a +test cannot attempt an index refresh against the disconnected network. Tests +that intentionally create a separate Cargo home can still resolve local Git +fixtures before their own offline build step. Existing base-repository Rust +vendor configuration and the sandbox's credentials and network restrictions +remain in force. + +The image build fails if the trusted fixture files are absent, symbolic links, +or cannot be fetched against their checksummed lock. It does not turn a failed +test into a pass. + +## Verification boundary + +An initial PyO3 extension build exceeded its 600-second limit on a loaded +macOS host. A later run with the project-local pinned maturin completed that +test in 32 seconds. With a fresh `CARGO_HOME` populated only by the locked +fixture and its trusted offline config, the complete two-file Rust dependency +and PyO3 suite passed all 29 tests with `GITHUB_ACTIONS=true`. The first +attempt at global `CARGO_NET_OFFLINE=true` failed one local Git dependency +fixture; scoping offline resolution to the default trusted Cargo home fixed +that failure. A terminal hosted rerun is still required before claiming the +coverage gate repaired. The targeted workflow contract suite passed 77 tests +with `GITHUB_ACTIONS=true` after this change. +The fixture crate's own `cargo llvm-cov --all-features --fail-under-lines 100` +run passed locally with one test and 100% line coverage, exercising its cached +`itoa` and `ryu` dependencies. diff --git a/tests/fixtures/coverage-cargo/Cargo.lock b/tests/fixtures/coverage-cargo/Cargo.lock new file mode 100644 index 0000000000..9a54521941 --- /dev/null +++ b/tests/fixtures/coverage-cargo/Cargo.lock @@ -0,0 +1,194 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "coverage-cargo-fixtures" +version = "0.0.0" +dependencies = [ + "itoa", + "pyo3", + "ryu", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" diff --git a/tests/fixtures/coverage-cargo/Cargo.toml b/tests/fixtures/coverage-cargo/Cargo.toml new file mode 100644 index 0000000000..dbe13ab8ab --- /dev/null +++ b/tests/fixtures/coverage-cargo/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "coverage-cargo-fixtures" +version = "0.0.0" +edition = "2021" + +[dependencies] +itoa = "=1.0.15" +ryu = "=1.0.20" +pyo3 = { version = "=0.22.6", features = ["extension-module", "abi3-py310"] } diff --git a/tests/fixtures/coverage-cargo/src/lib.rs b/tests/fixtures/coverage-cargo/src/lib.rs new file mode 100644 index 0000000000..475903b7b4 --- /dev/null +++ b/tests/fixtures/coverage-cargo/src/lib.rs @@ -0,0 +1,13 @@ +pub fn fixture() -> String { + let mut integer = itoa::Buffer::new(); + let mut float = ryu::Buffer::new(); + format!("{}:{}", integer.format(42), float.format(1.5)) +} + +#[cfg(test)] +mod tests { + #[test] + fn cached_formatters_link() { + assert_eq!(super::fixture(), "42:1.5"); + } +} diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index df17a35688..57ff68b271 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -841,6 +841,11 @@ def test_opencode_target_coverage_materializes_only_after_authorized_dispatch(): in measure_step ) assert "CARGO_HOME=/work/.opencode-sandbox-home/.cargo" in measure_step + assert "printf '\\n[net]\\noffline = true\\n' >>/work/.opencode-sandbox-home/.cargo/config.toml" in measure_step + assert "CARGO_NET_OFFLINE=true \\" not in measure_step + assert measure_step.index('rm -rf -- /work/.opencode-sandbox-home') < measure_step.index( + 'cp -a /opt/coverage-cargo-home/. /work/.opencode-sandbox-home/.cargo/' + ) assert "docker run --rm --init --network=none" in measure_step sandbox_runtime = measure_step.split( " export OPENCODE_SANDBOX_UID=65532", 1 diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 4709af94f1..1161602a53 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -17,7 +17,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "0ac2ec1fd69e60b228ac30b0b797403988d1a7ce" +REVIEW_DISPATCH_BLOB_SHA = "58f11efc9072e35db6ec6365630db9f97d16f652" def _workflow_text(path: Path) -> str: