From 21740f6741c63404469584f55ad897adac2c4046 Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 8 Sep 2026 14:45:03 -0700 Subject: [PATCH 1/4] ci(rust): reject stale Cargo lockfiles Signed-off-by: Piotr Mlocek --- .github/workflows/branch-checks.yml | 12 +++++--- tasks/rust.toml | 15 ++++++--- tasks/scripts/check-cargo-lockfiles.sh | 42 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 9 deletions(-) create mode 100755 tasks/scripts/check-cargo-lockfiles.sh diff --git a/.github/workflows/branch-checks.yml b/.github/workflows/branch-checks.yml index 5d4a98c61a..15293454c9 100644 --- a/.github/workflows/branch-checks.yml +++ b/.github/workflows/branch-checks.yml @@ -153,6 +153,10 @@ jobs: cache-bin: "false" cmd-format: nix develop .#devShells.${{ matrix.system }}.default -c {0} + - name: Verify Cargo lockfiles + if: matrix.system == 'x86_64-linux' + run: tasks/scripts/check-cargo-lockfiles.sh + - name: Format run: | cargo fmt --all -- --check @@ -162,10 +166,10 @@ jobs: - name: Lint run: | - cargo clippy --workspace --all-targets -- -D warnings - cargo clippy --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings - cargo check --manifest-path examples/governance-interceptor/Cargo.toml --all-targets - cargo check --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets + cargo clippy --locked --workspace --all-targets -- -D warnings + cargo clippy --locked --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings + cargo check --locked --manifest-path examples/governance-interceptor/Cargo.toml --all-targets + cargo check --locked --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets - name: Test env: diff --git a/tasks/rust.toml b/tasks/rust.toml index e62e22b3cf..2189da2d53 100644 --- a/tasks/rust.toml +++ b/tasks/rust.toml @@ -5,17 +5,22 @@ ["rust:check"] description = "Check all Rust crates for errors" -run = "cargo check --workspace" +run = "cargo check --locked --workspace" run_windows = "powershell -NoProfile -ExecutionPolicy Bypass -File tasks/scripts/windows-msvc.ps1 check native" hide = true +["rust:lockfiles:check"] +description = "Verify all tracked Cargo lockfiles are current" +run = "tasks/scripts/check-cargo-lockfiles.sh" +hide = true + ["rust:lint"] description = "Lint Rust code with Clippy (deny warnings)" run = [ - "cargo clippy --workspace --all-targets -- -D warnings", - "cargo clippy --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings", - "cargo check --manifest-path examples/governance-interceptor/Cargo.toml --all-targets", - "cargo check --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets", + "cargo clippy --locked --workspace --all-targets -- -D warnings", + "cargo clippy --locked --manifest-path e2e/rust/Cargo.toml --all-targets -- -D warnings", + "cargo check --locked --manifest-path examples/governance-interceptor/Cargo.toml --all-targets", + "cargo check --locked --manifest-path examples/supervisor-middleware-content-guard/Cargo.toml --all-targets", ] run_windows = "powershell -NoProfile -ExecutionPolicy Bypass -File tasks/scripts/windows-msvc.ps1 lint native" hide = true diff --git a/tasks/scripts/check-cargo-lockfiles.sh b/tasks/scripts/check-cargo-lockfiles.sh new file mode 100755 index 0000000000..db671270e8 --- /dev/null +++ b/tasks/scripts/check-cargo-lockfiles.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +cd "$(git rev-parse --show-toplevel)" + +found=0 +status=0 + +while IFS= read -r -d '' lockfile; do + found=1 + manifest="${lockfile%Cargo.lock}Cargo.toml" + + if [[ ! -f "$manifest" ]]; then + echo "error: tracked lockfile $lockfile has no adjacent Cargo.toml" >&2 + status=1 + continue + fi + + echo "Checking $lockfile" + if ! cargo metadata \ + --locked \ + --format-version 1 \ + --manifest-path "$manifest" \ + >/dev/null; then + echo "error: $lockfile is out of sync with $manifest" >&2 + status=1 + fi +done < <(git ls-files -z -- ':(glob)**/Cargo.lock') + +if [[ "$found" -eq 0 ]]; then + echo "error: no tracked Cargo.lock files found" >&2 + exit 1 +fi + +if [[ "$status" -ne 0 ]]; then + echo "Refresh the reported lockfiles with Cargo and commit the results." >&2 +fi + +exit "$status" From 0d08e260eec396738a4aefdd67c4f04ebedf757a Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 8 Sep 2026 15:17:01 -0700 Subject: [PATCH 2/4] docs(ci): clarify lockfile validation policy Signed-off-by: Piotr Mlocek --- tasks/scripts/check-cargo-lockfiles.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks/scripts/check-cargo-lockfiles.sh b/tasks/scripts/check-cargo-lockfiles.sh index db671270e8..4a3e584e0d 100755 --- a/tasks/scripts/check-cargo-lockfiles.sh +++ b/tasks/scripts/check-cargo-lockfiles.sh @@ -9,6 +9,9 @@ cd "$(git rev-parse --show-toplevel)" found=0 status=0 +# Policy: every tracked Cargo.lock represents an intentionally reproducible +# Cargo workspace and must resolve against its adjacent Cargo.toml. Manifests +# that intentionally do not own a lockfile are outside this check. while IFS= read -r -d '' lockfile; do found=1 manifest="${lockfile%Cargo.lock}Cargo.toml" From 63777e5e78bf36802204ec143bc3ee8a23b35d1e Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 8 Sep 2026 15:32:02 -0700 Subject: [PATCH 3/4] refactor(ci): structure and test Cargo lockfile validation Signed-off-by: Piotr Mlocek --- .github/workflows/branch-checks.yml | 3 + TESTING.md | 2 + tasks/scripts/check-cargo-lockfiles.sh | 61 +++++++----- tasks/scripts/check_cargo_lockfiles_test.py | 101 ++++++++++++++++++++ tasks/test.toml | 8 +- 5 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 tasks/scripts/check_cargo_lockfiles_test.py diff --git a/.github/workflows/branch-checks.yml b/.github/workflows/branch-checks.yml index 15293454c9..e76a11dca8 100644 --- a/.github/workflows/branch-checks.yml +++ b/.github/workflows/branch-checks.yml @@ -237,6 +237,9 @@ jobs: - name: Test run: mise run test:python + - name: Test Cargo lockfile validation + run: mise run test:cargo-lockfiles + go: name: Go SDK needs: pr_metadata diff --git a/TESTING.md b/TESTING.md index 14ec3c2055..c0fef4e92e 100644 --- a/TESTING.md +++ b/TESTING.md @@ -46,6 +46,8 @@ Run Rust tests only: mise run test:rust # cargo test --workspace ``` +Run `mise run rust:lockfiles:check` to validate every tracked Cargo lockfile against its adjacent manifest. Cargo diagnostics explain whether a failure requires refreshing a lockfile or resolving another problem, such as registry access. Run `mise run test:cargo-lockfiles` for the validator's regression tests, which use temporary Git repositories and a stub Cargo command. These tests also run in `mise run test` and the Python branch CI jobs. + ## Python Unit Tests Python unit tests use the `*_test.py` suffix convention (not `test_*` prefix) diff --git a/tasks/scripts/check-cargo-lockfiles.sh b/tasks/scripts/check-cargo-lockfiles.sh index 4a3e584e0d..bd74d8d647 100755 --- a/tasks/scripts/check-cargo-lockfiles.sh +++ b/tasks/scripts/check-cargo-lockfiles.sh @@ -4,42 +4,51 @@ set -euo pipefail -cd "$(git rev-parse --show-toplevel)" - -found=0 -status=0 - -# Policy: every tracked Cargo.lock represents an intentionally reproducible -# Cargo workspace and must resolve against its adjacent Cargo.toml. Manifests -# that intentionally do not own a lockfile are outside this check. -while IFS= read -r -d '' lockfile; do - found=1 - manifest="${lockfile%Cargo.lock}Cargo.toml" +check_lockfile() { + local lockfile="$1" + local manifest="${lockfile%Cargo.lock}Cargo.toml" if [[ ! -f "$manifest" ]]; then - echo "error: tracked lockfile $lockfile has no adjacent Cargo.toml" >&2 - status=1 - continue + printf 'error: tracked lockfile %s has no adjacent Cargo.toml\n' "$lockfile" >&2 + return 1 fi - echo "Checking $lockfile" + printf 'Checking %s\n' "$lockfile" if ! cargo metadata \ --locked \ --format-version 1 \ --manifest-path "$manifest" \ >/dev/null; then - echo "error: $lockfile is out of sync with $manifest" >&2 - status=1 + printf 'error: validation failed for %s\n' "$lockfile" >&2 + return 1 + fi +} + +main() { + local lockfile + local found=0 + local failed=0 + + cd "$(git rev-parse --show-toplevel)" + + # Policy: every tracked Cargo.lock represents an intentionally reproducible + # Cargo workspace and must resolve against its adjacent Cargo.toml. Manifests + # that intentionally do not own a lockfile are outside this check. + while IFS= read -r -d '' lockfile; do + found=1 + check_lockfile "$lockfile" || failed=1 + done < <(git ls-files -z -- ':(glob)**/Cargo.lock') + + if [[ "$found" -eq 0 ]]; then + echo "error: no tracked Cargo.lock files found" >&2 + return 1 fi -done < <(git ls-files -z -- ':(glob)**/Cargo.lock') -if [[ "$found" -eq 0 ]]; then - echo "error: no tracked Cargo.lock files found" >&2 - exit 1 -fi + if [[ "$failed" -ne 0 ]]; then + echo "Resolve the reported errors. If a lockfile needs updating, refresh it with Cargo and commit the result." >&2 + fi -if [[ "$status" -ne 0 ]]; then - echo "Refresh the reported lockfiles with Cargo and commit the results." >&2 -fi + return "$failed" +} -exit "$status" +main "$@" diff --git a/tasks/scripts/check_cargo_lockfiles_test.py b/tasks/scripts/check_cargo_lockfiles_test.py new file mode 100644 index 0000000000..785c8819a9 --- /dev/null +++ b/tasks/scripts/check_cargo_lockfiles_test.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import os +import subprocess +from pathlib import Path + +import pytest + +SCRIPT = Path(__file__).with_name("check-cargo-lockfiles.sh") + + +@pytest.fixture +def repo(tmp_path): + subprocess.run(["git", "init", "-q", str(tmp_path)], check=True) + return tmp_path + + +def workspace(repo, directory=".", *, manifest=True, tracked=True): + root = repo / directory + root.mkdir(parents=True, exist_ok=True) + lockfile = root / "Cargo.lock" + lockfile.touch() + if manifest: + (root / "Cargo.toml").touch() + if tracked: + subprocess.run(["git", "add", str(lockfile)], cwd=repo, check=True) + + +def run_check(repo, *, failing_manifest="", cwd=None): + bin_dir = repo / "bin" + bin_dir.mkdir() + cargo = bin_dir / "cargo" + cargo.write_text( + "#!/usr/bin/env bash\n" + "set -euo pipefail\n" + '[[ "$#" -eq 6 && "$1" == metadata && "$2" == --locked && ' + '"$3" == --format-version && "$4" == 1 && "$5" == --manifest-path ]]\n' + 'printf "%s\\0" "$6" >> "$CALL_LOG"\n' + 'if [[ "$6" == "$FAILING_MANIFEST" ]]; then\n' + ' echo "registry unavailable" >&2\n' + " exit 1\n" + "fi\n" + 'echo "metadata output"\n' + ) + cargo.chmod(0o755) + log = repo / "calls" + result = subprocess.run( + ["bash", str(SCRIPT)], + cwd=cwd or repo, + env={ + **os.environ, + "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}", + "CALL_LOG": str(log), + "FAILING_MANIFEST": failing_manifest, + }, + capture_output=True, + text=True, + ) + calls = log.read_bytes().split(b"\0")[:-1] if log.exists() else [] + return result, [entry.decode() for entry in calls] + + +def test_no_tracked_lockfiles(repo): + workspace(repo, tracked=False) + result, calls = run_check(repo) + assert result.returncode == 1 + assert "no tracked Cargo.lock files found" in result.stderr + assert calls == [] + + +def test_missing_manifest_does_not_stop_later_checks(repo): + workspace(repo, "a-missing", manifest=False) + workspace(repo, "z-valid") + result, calls = run_check(repo) + assert result.returncode == 1 + assert "a-missing/Cargo.lock has no adjacent Cargo.toml" in result.stderr + assert calls == ["z-valid/Cargo.toml"] + + +@pytest.mark.parametrize("directory", ["nested workspace", "nested\nworkspace"]) +def test_success_from_subdirectory_with_unusual_path(repo, directory): + workspace(repo) + workspace(repo, directory) + workspace(repo, "untracked", tracked=False) + result, calls = run_check(repo, cwd=repo / directory) + assert result.returncode == 0, result.stderr + assert calls == ["Cargo.toml", f"{directory}/Cargo.toml"] + assert "metadata output" not in result.stdout + + +def test_cargo_failure_preserves_diagnostic_and_continues(repo): + workspace(repo, "a-failing") + workspace(repo, "z-valid") + result, calls = run_check(repo, failing_manifest="a-failing/Cargo.toml") + assert result.returncode == 1 + assert calls == ["a-failing/Cargo.toml", "z-valid/Cargo.toml"] + assert "registry unavailable" in result.stderr + assert "validation failed for a-failing/Cargo.lock" in result.stderr + assert "out of sync" not in result.stderr + assert "If a lockfile needs updating" in result.stderr diff --git a/tasks/test.toml b/tasks/test.toml index bb1fa2e9ab..ddca090edc 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -12,6 +12,7 @@ depends = [ "test:sbom", "test:install-sh", "test:build-env", + "test:cargo-lockfiles", "test:packaging-assets", "test:codex-security-release-range", "test:docs-website", @@ -23,6 +24,12 @@ description = "Test the docs-website sync script" # and the script's runtime dependency (PyYAML), which lives outside the project env. run = "uv run --no-project --with pytest --with pyyaml pytest tasks/scripts/sync_docs_website_test.py" +["test:cargo-lockfiles"] +description = "Test Cargo lockfile validation script" +run = "uv run --no-project --with pytest --with pytest-asyncio pytest tasks/scripts/check_cargo_lockfiles_test.py" +run_windows = "echo Skipping test:cargo-lockfiles: the Bash validation script runs on Unix." +hide = true + ["test:sbom"] description = "Run SBOM tooling tests" run = "uv run --no-project --with pytest pytest -o \"python_files=*_test.py\" deploy/sbom/" @@ -267,4 +274,3 @@ description = "Run GPU e2e against a standalone gateway with the Docker compute env = { OPENSHELL_E2E_DOCKER_GPU = "1", OPENSHELL_E2E_DOCKER_TEST = "gpu", OPENSHELL_E2E_DOCKER_FEATURES = "e2e-docker-gpu" } depends = ["e2e:conformance:build"] run = "OPENSHELL_CONFORMANCE_BIN=\"${OPENSHELL_CONFORMANCE_BIN:-$PWD/target/debug/openshell-conformance}\" e2e/rust/e2e-docker.sh" - From 57b65552def3c82ae0c1b65abc9eb24299166eaa Mon Sep 17 00:00:00 2001 From: Piotr Mlocek Date: Tue, 8 Sep 2026 15:41:48 -0700 Subject: [PATCH 4/4] chore(ci): remove lockfile validator regression tests Signed-off-by: Piotr Mlocek --- .github/workflows/branch-checks.yml | 3 - TESTING.md | 2 +- tasks/scripts/check_cargo_lockfiles_test.py | 101 -------------------- tasks/test.toml | 7 -- 4 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 tasks/scripts/check_cargo_lockfiles_test.py diff --git a/.github/workflows/branch-checks.yml b/.github/workflows/branch-checks.yml index e76a11dca8..15293454c9 100644 --- a/.github/workflows/branch-checks.yml +++ b/.github/workflows/branch-checks.yml @@ -237,9 +237,6 @@ jobs: - name: Test run: mise run test:python - - name: Test Cargo lockfile validation - run: mise run test:cargo-lockfiles - go: name: Go SDK needs: pr_metadata diff --git a/TESTING.md b/TESTING.md index c0fef4e92e..ccb183f61f 100644 --- a/TESTING.md +++ b/TESTING.md @@ -46,7 +46,7 @@ Run Rust tests only: mise run test:rust # cargo test --workspace ``` -Run `mise run rust:lockfiles:check` to validate every tracked Cargo lockfile against its adjacent manifest. Cargo diagnostics explain whether a failure requires refreshing a lockfile or resolving another problem, such as registry access. Run `mise run test:cargo-lockfiles` for the validator's regression tests, which use temporary Git repositories and a stub Cargo command. These tests also run in `mise run test` and the Python branch CI jobs. +Run `mise run rust:lockfiles:check` to validate every tracked Cargo lockfile against its adjacent manifest. Cargo diagnostics explain whether a failure requires refreshing a lockfile or resolving another problem, such as registry access. To refresh a stale lockfile, run `cargo metadata --format-version 1 --manifest-path path/to/Cargo.toml > /dev/null` with the adjacent manifest, review and commit the lockfile changes, then rerun `mise run pre-commit`. ## Python Unit Tests diff --git a/tasks/scripts/check_cargo_lockfiles_test.py b/tasks/scripts/check_cargo_lockfiles_test.py deleted file mode 100644 index 785c8819a9..0000000000 --- a/tasks/scripts/check_cargo_lockfiles_test.py +++ /dev/null @@ -1,101 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -import os -import subprocess -from pathlib import Path - -import pytest - -SCRIPT = Path(__file__).with_name("check-cargo-lockfiles.sh") - - -@pytest.fixture -def repo(tmp_path): - subprocess.run(["git", "init", "-q", str(tmp_path)], check=True) - return tmp_path - - -def workspace(repo, directory=".", *, manifest=True, tracked=True): - root = repo / directory - root.mkdir(parents=True, exist_ok=True) - lockfile = root / "Cargo.lock" - lockfile.touch() - if manifest: - (root / "Cargo.toml").touch() - if tracked: - subprocess.run(["git", "add", str(lockfile)], cwd=repo, check=True) - - -def run_check(repo, *, failing_manifest="", cwd=None): - bin_dir = repo / "bin" - bin_dir.mkdir() - cargo = bin_dir / "cargo" - cargo.write_text( - "#!/usr/bin/env bash\n" - "set -euo pipefail\n" - '[[ "$#" -eq 6 && "$1" == metadata && "$2" == --locked && ' - '"$3" == --format-version && "$4" == 1 && "$5" == --manifest-path ]]\n' - 'printf "%s\\0" "$6" >> "$CALL_LOG"\n' - 'if [[ "$6" == "$FAILING_MANIFEST" ]]; then\n' - ' echo "registry unavailable" >&2\n' - " exit 1\n" - "fi\n" - 'echo "metadata output"\n' - ) - cargo.chmod(0o755) - log = repo / "calls" - result = subprocess.run( - ["bash", str(SCRIPT)], - cwd=cwd or repo, - env={ - **os.environ, - "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}", - "CALL_LOG": str(log), - "FAILING_MANIFEST": failing_manifest, - }, - capture_output=True, - text=True, - ) - calls = log.read_bytes().split(b"\0")[:-1] if log.exists() else [] - return result, [entry.decode() for entry in calls] - - -def test_no_tracked_lockfiles(repo): - workspace(repo, tracked=False) - result, calls = run_check(repo) - assert result.returncode == 1 - assert "no tracked Cargo.lock files found" in result.stderr - assert calls == [] - - -def test_missing_manifest_does_not_stop_later_checks(repo): - workspace(repo, "a-missing", manifest=False) - workspace(repo, "z-valid") - result, calls = run_check(repo) - assert result.returncode == 1 - assert "a-missing/Cargo.lock has no adjacent Cargo.toml" in result.stderr - assert calls == ["z-valid/Cargo.toml"] - - -@pytest.mark.parametrize("directory", ["nested workspace", "nested\nworkspace"]) -def test_success_from_subdirectory_with_unusual_path(repo, directory): - workspace(repo) - workspace(repo, directory) - workspace(repo, "untracked", tracked=False) - result, calls = run_check(repo, cwd=repo / directory) - assert result.returncode == 0, result.stderr - assert calls == ["Cargo.toml", f"{directory}/Cargo.toml"] - assert "metadata output" not in result.stdout - - -def test_cargo_failure_preserves_diagnostic_and_continues(repo): - workspace(repo, "a-failing") - workspace(repo, "z-valid") - result, calls = run_check(repo, failing_manifest="a-failing/Cargo.toml") - assert result.returncode == 1 - assert calls == ["a-failing/Cargo.toml", "z-valid/Cargo.toml"] - assert "registry unavailable" in result.stderr - assert "validation failed for a-failing/Cargo.lock" in result.stderr - assert "out of sync" not in result.stderr - assert "If a lockfile needs updating" in result.stderr diff --git a/tasks/test.toml b/tasks/test.toml index ddca090edc..712b91b9a6 100644 --- a/tasks/test.toml +++ b/tasks/test.toml @@ -12,7 +12,6 @@ depends = [ "test:sbom", "test:install-sh", "test:build-env", - "test:cargo-lockfiles", "test:packaging-assets", "test:codex-security-release-range", "test:docs-website", @@ -24,12 +23,6 @@ description = "Test the docs-website sync script" # and the script's runtime dependency (PyYAML), which lives outside the project env. run = "uv run --no-project --with pytest --with pyyaml pytest tasks/scripts/sync_docs_website_test.py" -["test:cargo-lockfiles"] -description = "Test Cargo lockfile validation script" -run = "uv run --no-project --with pytest --with pytest-asyncio pytest tasks/scripts/check_cargo_lockfiles_test.py" -run_windows = "echo Skipping test:cargo-lockfiles: the Bash validation script runs on Unix." -hide = true - ["test:sbom"] description = "Run SBOM tooling tests" run = "uv run --no-project --with pytest pytest -o \"python_files=*_test.py\" deploy/sbom/"