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
7 changes: 7 additions & 0 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ The main `.github/workflows/ci.yml` workflow runs ordinary `make test` without
`WITH_ACT=1` so the slower container-backed checks run in parallel instead of
blocking the main test and coverage path.

The template also renders `.github/workflows/mutation-testing.yml` into
generated projects; its rendered contract is asserted by
`_assert_mutation_workflow_contracts` in
`tests/helpers/tooling_contracts/workflows.py`. Dependabot owns the pinned
reusable-workflow commit SHA, so the contract test asserts the pin format (a
full commit SHA) rather than a hard-coded value.

## Required Tooling

The parent tests expect these tools to be available when validating generated
Expand Down
24 changes: 24 additions & 0 deletions docs/users-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,27 @@ The generated `Makefile` exposes these public targets:

Install `clang`, `lld`, `mold`, `python3`, and `cargo-audit` before running the
full generated workflow locally on Linux.

## Scheduled Mutation Testing

Generated projects include `.github/workflows/mutation-testing.yml`, a scheduled
GitHub Actions workflow that runs mutation testing with `cargo-mutants`. It is a
thin caller of the shared `leynos/shared-actions` `mutation-cargo` reusable
workflow.

Mutation testing measures test-suite quality. It introduces small changes
(mutants) into the source and confirms the tests fail in response. A surviving
mutant marks a code path the tests do not meaningfully exercise, so promote it
into a new test rather than ignoring it.

The workflow runs on a daily schedule (09:15 UTC by default) and can also be
started manually from the **Actions** tab with **Run workflow**. Scheduled runs
mutate only files changed within the detection window, so routine runs stay
fast; a manual dispatch mutates the whole crate, fanned out across shards.
Because the runs are scheduled rather than gating pull requests, they surface
coverage gaps without slowing day-to-day CI, and they do not block merges.

When adopting the workflow in a new repository, stagger the cron slot: pick an
unclaimed daily time to avoid concurrent runs across related repositories. The
`mutation` job runs with a least-privilege token (`contents: read` plus
`id-token: write` for workflow-source resolution).
47 changes: 47 additions & 0 deletions template/.github/workflows/mutation-testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Mutation testing

# Thin caller of the shared mutation-testing reusable workflow; caller
# guide: leynos/shared-actions docs/mutation-cargo-workflow.md.
# Scheduled runs mutate only files changed within the detection window;
# manual dispatch runs mutate everything, fanned out across shards. To
# test a branch, select it in the Actions "Run workflow" control.

'on':
schedule:
# Daily, 09:15 UTC. Stagger this slot when adopting the workflow in a
# new repository: the estate already occupies 03:05-09:05 UTC, so pick
# an unclaimed slot to avoid concurrent runs across repositories.
- cron: "15 9 * * *"
workflow_dispatch:

# Default the token to no scopes; the job opts in explicitly.
permissions: {}

# Serialize runs per ref: a dispatch during a scheduled run (or vice
# versa) queues rather than racing. Runs are informational, so queued
# runs wait instead of cancelling.
concurrency:
group: mutation-testing-${{ github.ref }}
cancel-in-progress: false

jobs:
mutation:
permissions:
contents: read
id-token: write # OIDC workflow-source resolution
uses: leynos/shared-actions/.github/workflows/mutation-cargo.yml@0c8bd605f8966a004decb0b54f717a745f51bc57
with:
# Match the CI baseline (`make test` runs --all-targets
# --all-features) so the test command exercised against mutants
# mirrors what gates pull requests.
extra-args: "--all-features"
# .cargo/config.toml links Linux dev targets with clang and
# -fuse-ld=mold, so the mutants jobs need the same packages that
# ci.yml installs before any cargo command runs on a bare
# ubuntu-latest runner. The reusable workflow executes these under
# bash with `set -euo pipefail`.
setup-commands: |
export DEBIAN_FRONTEND=noninteractive
# mold accelerates dev builds; coverage uses lld for llvm-tools compatibility.
sudo apt-get update \
&& sudo apt-get install --yes --no-install-recommends clang lld mold
Comment thread
coderabbitai[bot] marked this conversation as resolved.
7 changes: 7 additions & 0 deletions template/docs/developers-guide.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ The main `.github/workflows/ci.yml` workflow deliberately does not run
`make test WITH_ACT=1`; the separate Act workflow runs those slower
container-backed checks in parallel.

A scheduled `.github/workflows/mutation-testing.yml` workflow also runs
`cargo-mutants` via the shared reusable workflow, daily and on manual
dispatch. It is informational and does not gate pull requests. Dependabot
keeps its pinned reusable-workflow SHA current. See the user guide's
"Scheduled Mutation Testing" section for behaviour, and promote surviving
mutants into new tests.

## Tooling

Development builds use Cranelift for debug code generation. On Linux targets,
Expand Down
3 changes: 3 additions & 0 deletions template/docs/repository-layout.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ compact and omits build output such as `target/`.
│ └── workflows/
│ ├── act-validation.yml
│ ├── ci.yml
│ ├── mutation-testing.yml
{% if flavour == 'app' %}
│ └── release.yml
{% endif %}
Expand Down Expand Up @@ -55,6 +56,8 @@ compact and omits build output such as `target/`.
validation through `act` separately from main CI.
- `.github/workflows/ci.yml`: Runs the generated project's continuous
integration checks.
- `.github/workflows/mutation-testing.yml`: Runs scheduled mutation testing
through the shared `mutation-cargo` reusable workflow.
{% if flavour == 'app' %}
- `.github/workflows/release.yml`: Builds and publishes binary release
artefacts for the application flavour.
Expand Down
24 changes: 24 additions & 0 deletions template/docs/users-guide.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ The generated `Makefile` exposes these public targets:

Install `clang`, `lld`, `mold`, `python3`, and `cargo-audit` before running the
full generated workflow locally on Linux.

## Scheduled Mutation Testing

Generated projects include `.github/workflows/mutation-testing.yml`, a scheduled
GitHub Actions workflow that runs mutation testing with `cargo-mutants`. It is a
thin caller of the shared `leynos/shared-actions` `mutation-cargo` reusable
workflow.

Mutation testing measures test-suite quality. It introduces small changes
(mutants) into the source and confirms the tests fail in response. A surviving
mutant marks a code path the tests do not meaningfully exercise, so promote it
into a new test rather than ignoring it.

The workflow runs on a daily schedule (09:15 UTC by default) and can also be
started manually from the **Actions** tab with **Run workflow**. Scheduled runs
mutate only files changed within the detection window, so routine runs stay
fast; a manual dispatch mutates the whole crate, fanned out across shards.
Because the runs are scheduled rather than gating pull requests, they surface
coverage gaps without slowing day-to-day CI, and they do not block merges.

When adopting the workflow in a new repository, stagger the cron slot: pick an
unclaimed daily time to avoid concurrent runs across related repositories. The
`mutation` job runs with a least-privilege token (`contents: read` plus
`id-token: write` for workflow-source resolution).
5 changes: 5 additions & 0 deletions tests/helpers/tooling_contracts/orchestration.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from tests.helpers.tooling_contracts.workflows import (
_assert_audit_workflow_contracts,
_assert_ci_workflow_contracts,
_assert_mutation_workflow_contracts,
_assert_release_workflow_contracts,
)

Expand All @@ -32,6 +33,7 @@ def assert_generated_tooling_contracts(
ci_workflow: str,
audit_workflow: str,
act_workflow: str,
mutation_workflow: str,
docs_contents: str,
repository_layout: str,
readme: str,
Expand Down Expand Up @@ -64,6 +66,8 @@ def assert_generated_tooling_contracts(
Rendered generated scheduled dependency audit workflow text.
act_workflow
Rendered generated act-validation workflow text.
mutation_workflow
Rendered generated mutation-testing workflow text.
docs_contents
Rendered ``docs/contents.md`` text.
repository_layout
Expand Down Expand Up @@ -99,6 +103,7 @@ def assert_generated_tooling_contracts(
parsed_ci_workflow, ci_workflow, act_workflow, test_stub
)
_assert_audit_workflow_contracts(audit_workflow)
_assert_mutation_workflow_contracts(mutation_workflow)
assert_documentation_navigation_contracts(docs_contents, repository_layout, flavour)
assert "[Documentation contents](docs/contents.md)" in readme, (
"expected generated README to link to the documentation contents"
Expand Down
Loading
Loading