From 03af41ddf075cb1c055e4a49fbb03bd9038c14d0 Mon Sep 17 00:00:00 2001 From: leynos Date: Tue, 21 Jul 2026 23:05:04 +0200 Subject: [PATCH] Reference setup-rust remotely so its post hook survives relocation (#365) The mutants job invoked the setup-rust composite via the workspace-relative path ./workflow-src/.github/actions/setup-rust, then relocated workflow-src to $RUNNER_TEMP. At job teardown the runtime re-resolves the local action's action.yml from that same path to run its cache-save post hook, but the directory has moved, so the post step fails and reddens an otherwise green mutation run. Reference the action from its own repository, pinned by SHA. A remote uses is materialised under the runner's managed _actions directory, outside the caller's workspace and surviving teardown, so no relocation touches it. This also removes the only ./workflow-src/ action reference, keeping the caller's tree clean during the run (issue #343) unchanged. Pin a shape-test invariant: no workflow step may reference an action via the relocated ./workflow-src/ path. --- .github/workflows/mutation-cargo.yml | 12 +++++-- .../tests/test_mutation_workflow_shape.py | 31 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mutation-cargo.yml b/.github/workflows/mutation-cargo.yml index 4935b7c2..ad5c1c7e 100644 --- a/.github/workflows/mutation-cargo.yml +++ b/.github/workflows/mutation-cargo.yml @@ -203,8 +203,16 @@ jobs: persist-credentials: false - name: Setup Rust - if: ${{ steps.workflow-source.outputs.checkout == 'true' }} - uses: ./workflow-src/.github/actions/setup-rust + if: ${{ env.ACT != 'true' }} + # Reference the composite action from its own repository, pinned by + # SHA, rather than the workspace-relative `./workflow-src/...` path. + # A remote `uses:` is materialised under the runner's managed + # `_actions` directory, which lives outside the caller's workspace + # and survives job teardown, so setup-rust's cache-save post hook can + # still resolve `action.yml` after "Relocate workflow source" has + # moved workflow-src to $RUNNER_TEMP (issue #365). Bump this pin in + # step with the resolve-workflow-source pin above. + uses: leynos/shared-actions/.github/actions/setup-rust@62733fd653cc577f9858645d9a3e4daf90998c55 - name: Install cargo-mutants if: ${{ env.ACT != 'true' }} diff --git a/workflow_scripts/tests/test_mutation_workflow_shape.py b/workflow_scripts/tests/test_mutation_workflow_shape.py index 78618042..886d9453 100644 --- a/workflow_scripts/tests/test_mutation_workflow_shape.py +++ b/workflow_scripts/tests/test_mutation_workflow_shape.py @@ -25,6 +25,7 @@ CHECKOUT_STEP = "Checkout workflow repository" RELOCATE_STEP = "Relocate workflow source" RELOCATED_DIR_EXPR = "${{ steps.relocate-workflow-source.outputs.workflow_dir }}" +LOCAL_WORKFLOW_SRC_PREFIX = "./workflow-src/" pytestmark = pytest.mark.skipif( not all((WORKFLOWS_DIR / name).exists() for name in WORKFLOW_NAMES), @@ -55,6 +56,36 @@ def _step_names(steps: list[dict[str, object]]) -> list[object]: return [step.get("name") for step in steps] +@pytest.mark.parametrize("workflow_name", WORKFLOW_NAMES) +def test_no_step_references_a_relocated_workflow_src_action( + workflow_name: str, +) -> None: + """No step invokes a composite action via the ``./workflow-src/`` path. + + A workspace-relative ``uses: ./workflow-src/...`` reference breaks at + job teardown: the Actions runtime re-resolves the local action's + ``action.yml`` from that path when running its post hook, but the + "Relocate workflow source" step has by then moved ``workflow-src`` to + ``$RUNNER_TEMP``. The post step fails to find the definition and reddens + an otherwise green run (issue #365). Composite actions with post hooks + must instead be referenced remotely (pinned by SHA); the runner + materialises those under its managed ``_actions`` directory, outside the + caller's workspace and surviving teardown. + """ + for job_name, job in _jobs(workflow_name).items(): + for step in _steps(job): + uses = step.get("uses") + if not isinstance(uses, str): + continue + assert not uses.startswith(LOCAL_WORKFLOW_SRC_PREFIX), ( + f"{workflow_name}:{job_name} step {step.get('name')!r} " + f"references {uses!r}; a workspace-relative workflow-src " + f"action path breaks its post hook once workflow-src is " + f"relocated to $RUNNER_TEMP (issue #365). Reference the " + f"action remotely, pinned by SHA, instead." + ) + + @pytest.mark.parametrize("workflow_name", WORKFLOW_NAMES) def test_every_workflow_checkout_is_followed_by_relocation( workflow_name: str,