From e5c687dab915ee493d9dbf347142aacf90c2ee55 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 21 Jul 2026 23:05:10 +0200 Subject: [PATCH] Forward a submodules input to the caller checkout in mutation-cargo (#363) Callers that vendor a build-graph dependency as a git submodule (for example a Cargo path dependency under third_party/) fail the unmutated baseline: the caller checkout defaults to no submodules, so the submodule directory is empty and cargo cannot read its Cargo.toml before any mutant runs. Add a submodules input (default false, preserving current behaviour) and forward it to actions/checkout in every job that checks out the caller repository (detect and mutants, including shards). Document it in the caller guide and pin a shape-test invariant that each caller checkout forwards the input. --- .github/workflows/mutation-cargo.yml | 13 ++++++++ docs/mutation-cargo-workflow.md | 8 +++++ .../tests/test_mutation_workflow_shape.py | 33 +++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/.github/workflows/mutation-cargo.yml b/.github/workflows/mutation-cargo.yml index 4935b7c2..a870de5a 100644 --- a/.github/workflows/mutation-cargo.yml +++ b/.github/workflows/mutation-cargo.yml @@ -66,6 +66,17 @@ on: pipefail`; empty means no extra setup. type: string default: "" + submodules: + description: >- + How to check out the caller repository's git submodules, + forwarded verbatim to actions/checkout's `submodules` input: + `false` (default, no submodules), `true` (top-level only) or + `recursive`. Set this for callers that vendor a build-graph + dependency as a git submodule (e.g. a path dependency under + `third_party/`), whose directory must exist for the unmutated + baseline to build. + type: string + default: "false" # Default the token to no scopes; jobs opt in explicitly. permissions: {} @@ -84,6 +95,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 + submodules: ${{ inputs.submodules }} persist-credentials: false - name: Resolve workflow source @@ -183,6 +195,7 @@ jobs: - name: Checkout caller repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: + submodules: ${{ inputs.submodules }} persist-credentials: false - name: Resolve workflow source diff --git a/docs/mutation-cargo-workflow.md b/docs/mutation-cargo-workflow.md index 56473dcb..91b9d1a5 100644 --- a/docs/mutation-cargo-workflow.md +++ b/docs/mutation-cargo-workflow.md @@ -84,9 +84,17 @@ jobs: | `cargo-mutants-version` | pinned | Tool version; the summary parser is validated against it. | | `extra-args` | (empty) | Extra cargo-mutants arguments (shell-lexed), e.g. `--all-features`. | | `setup-commands` | (empty) | Shell commands run before cargo-mutants in each mutants job (e.g. `sudo apt-get install -y mold` when the repo's `.cargo/config.toml` selects that linker). | +| `submodules` | `false` | How to check out the caller's git submodules, forwarded to `actions/checkout` (`false`, `true` or `recursive`). Set for callers that vendor a build-graph dependency as a submodule. | ## Notes +- Set `submodules: recursive` (or `true`) when the caller vendors a + build-graph dependency as a git submodule — for example a Cargo path + dependency under `third_party/`. The default checkout leaves the + submodule directory empty, so the unmutated baseline fails to build + (`failed to read .../Cargo.toml`) before any mutant runs. The input is + a no-op for callers without a `.gitmodules`. + - The `cargo-mutants-version` default is pinned because the `outcomes.json` format is documented as unstable and the summary parser must match it. Override only alongside a parser check. diff --git a/workflow_scripts/tests/test_mutation_workflow_shape.py b/workflow_scripts/tests/test_mutation_workflow_shape.py index 78618042..d4af59eb 100644 --- a/workflow_scripts/tests/test_mutation_workflow_shape.py +++ b/workflow_scripts/tests/test_mutation_workflow_shape.py @@ -55,6 +55,39 @@ def _step_names(steps: list[dict[str, object]]) -> list[object]: return [step.get("name") for step in steps] +CALLER_CHECKOUT_STEP = "Checkout caller repository" +SUBMODULES_EXPR = "${{ inputs.submodules }}" + + +def test_caller_checkout_forwards_submodules_input() -> None: + """Every caller checkout in mutation-cargo forwards the submodules input. + + A caller that vendors a build-graph dependency as a git submodule (a + Cargo path dependency under ``third_party/``, say) needs the submodule + directory populated for the unmutated baseline to build. The checkout + defaults to no submodules, so each ``Checkout caller repository`` step + must forward the ``submodules`` input to ``actions/checkout`` (issue + #363). + """ + checkouts = [ + (job_name, step) + for job_name, job in _jobs("mutation-cargo.yml").items() + for step in _steps(job) + if step.get("name") == CALLER_CHECKOUT_STEP + ] + assert checkouts, "mutation-cargo.yml must check out the caller repository" + for job_name, step in checkouts: + params = step.get("with") + assert isinstance(params, dict), ( + f"mutation-cargo.yml:{job_name} caller checkout must set `with`" + ) + assert params.get("submodules") == SUBMODULES_EXPR, ( + f"mutation-cargo.yml:{job_name} caller checkout must forward " + f"the submodules input (issue #363), got " + f"{params.get('submodules')!r}" + ) + + @pytest.mark.parametrize("workflow_name", WORKFLOW_NAMES) def test_every_workflow_checkout_is_followed_by_relocation( workflow_name: str,