Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/mutation-cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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' }}
Expand Down
31 changes: 31 additions & 0 deletions workflow_scripts/tests/test_mutation_workflow_shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
Loading