From d37868fb2cb6f7adde68b299873d28d1fa06c7cd Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 22 Jun 2025 20:02:32 +0100 Subject: [PATCH 1/4] Improve ratchet coverage action documentation --- .github/actions/ratchet-coverage/CHANGELOG.md | 5 ++ .github/actions/ratchet-coverage/README.md | 40 ++++++++++++ .github/actions/ratchet-coverage/action.yml | 65 +++++++++++++++++++ CODEOWNERS | 5 ++ README.md | 1 + 5 files changed, 116 insertions(+) create mode 100644 .github/actions/ratchet-coverage/CHANGELOG.md create mode 100644 .github/actions/ratchet-coverage/README.md create mode 100644 .github/actions/ratchet-coverage/action.yml create mode 100644 CODEOWNERS diff --git a/.github/actions/ratchet-coverage/CHANGELOG.md b/.github/actions/ratchet-coverage/CHANGELOG.md new file mode 100644 index 00000000..b999a6db --- /dev/null +++ b/.github/actions/ratchet-coverage/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## v1.0.0 + +- Initial version with caching and baseline ratcheting. diff --git a/.github/actions/ratchet-coverage/README.md b/.github/actions/ratchet-coverage/README.md new file mode 100644 index 00000000..d1741ef7 --- /dev/null +++ b/.github/actions/ratchet-coverage/README.md @@ -0,0 +1,40 @@ +# Ratchet coverage + +Generate code coverage using `cargo tarpaulin` and fail the workflow if the +coverage percentage falls below a stored baseline. + +## Inputs + +| Name | Description | Required | Default | +| --- | --- | --- | --- | +| baseline-file | File used to persist the baseline coverage percentage between runs | no | `.coverage-baseline` | +| args | Additional arguments passed to `cargo tarpaulin` | no | *(none)* | + +## Outputs + +| Name | Description | +| --- | --- | +| percent | Coverage percentage reported by `cargo tarpaulin` | + +## Example + +```yaml +- uses: ./.github/actions/setup-rust@v1 +- uses: ./.github/actions/ratchet-coverage@v1 + with: + baseline-file: .cache/coverage-baseline + args: --workspace +``` + +`cargo tarpaulin` only runs on Linux hosts, so use this action on +`ubuntu-latest` runners. + +### How it works + +The action restores the previous coverage baseline using `actions/cache` and +installs `cargo-tarpaulin` if necessary. After running the coverage command, it +compares the new percentage with the stored baseline. The job fails if coverage +has dropped. On success, the baseline file is updated and saved back to the +cache for future runs. + +Release history is available in [CHANGELOG](CHANGELOG.md). diff --git a/.github/actions/ratchet-coverage/action.yml b/.github/actions/ratchet-coverage/action.yml new file mode 100644 index 00000000..5a7cc33e --- /dev/null +++ b/.github/actions/ratchet-coverage/action.yml @@ -0,0 +1,65 @@ +name: Ratchet coverage +description: Run cargo tarpaulin and fail if coverage decreases +inputs: + baseline-file: + description: Path to store the coverage baseline + required: false + default: .coverage-baseline +outputs: + percent: + description: Coverage percentage reported by cargo tarpaulin + value: ${{ steps.cov.outputs.percent }} +runs: + using: composite + steps: + - name: Restore baseline + uses: actions/cache@v4 + with: + path: ${{ inputs.baseline-file }} + key: ratchet-baseline-${{ runner.os }} + - name: Cache cargo artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/cargo-tarpaulin + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-tarpaulin-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-tarpaulin- + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + shell: bash + - name: Run coverage + id: cov + run: | + set -euo pipefail + output=$(cargo tarpaulin --out lcov) + echo "$output" + percent=$(echo "$output" | grep -oE '[0-9]+\.[0-9]+(?=%)' | head -n1) + echo "percent=$percent" >> "$GITHUB_OUTPUT" + shell: bash + - name: Ratchet coverage + run: | + set -euo pipefail + file="${{ inputs.baseline-file }}" + current="${{ steps.cov.outputs.percent }}" + baseline=0 + if [ -f "$file" ]; then + baseline=$(cat "$file") + fi + echo "Current coverage: $current%" + echo "Baseline coverage: $baseline%" + if [ "$(echo "$current < $baseline" | bc -l)" = "1" ]; then + echo "Coverage decreased" >&2 + exit 1 + fi + printf '%.2f' "$current" > "$file" + shell: bash + - name: Save baseline + if: success() + uses: actions/cache/save@v4 + with: + path: ${{ inputs.baseline-file }} + key: ratchet-baseline-${{ runner.os }} diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..39a02310 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,5 @@ +.github/actions/export-postgres-url/ @leynos +.github/actions/generate-coverage/ @leynos +.github/actions/setup-rust/ @leynos +.github/actions/upload-codescene-coverage/ @leynos +.github/actions/ratchet-coverage/ @leynos diff --git a/README.md b/README.md index 2ebf87ca..4e3b69f6 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,4 @@ GitHub Actions | Generate coverage | `.github/actions/generate-coverage` | v1 | | Setup Rust | `.github/actions/setup-rust` | v1 | | Upload CodeScene Coverage | `.github/actions/upload-codescene-coverage` | v1 | +| Ratchet coverage | `.github/actions/ratchet-coverage` | v1 | From 3863ee98c4c79e9ec8c9b1af0d6fd670f3a349c6 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 22 Jun 2025 20:45:37 +0100 Subject: [PATCH 2/4] Add args input and validate coverage numbers --- .github/actions/ratchet-coverage/action.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/actions/ratchet-coverage/action.yml b/.github/actions/ratchet-coverage/action.yml index 5a7cc33e..df027b26 100644 --- a/.github/actions/ratchet-coverage/action.yml +++ b/.github/actions/ratchet-coverage/action.yml @@ -5,6 +5,10 @@ inputs: description: Path to store the coverage baseline required: false default: .coverage-baseline + args: + description: Additional arguments passed to cargo tarpaulin + required: false + default: '' outputs: percent: description: Coverage percentage reported by cargo tarpaulin @@ -35,7 +39,7 @@ runs: id: cov run: | set -euo pipefail - output=$(cargo tarpaulin --out lcov) + output=$(cargo tarpaulin ${{ inputs.args }} --out lcov) echo "$output" percent=$(echo "$output" | grep -oE '[0-9]+\.[0-9]+(?=%)' | head -n1) echo "percent=$percent" >> "$GITHUB_OUTPUT" @@ -51,6 +55,11 @@ runs: fi echo "Current coverage: $current%" echo "Baseline coverage: $baseline%" + if ! [[ "$current" =~ ^[0-9]+(\.[0-9]+)?$ ]] || \ + ! [[ "$baseline" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then + echo "Invalid coverage values" >&2 + exit 1 + fi if [ "$(echo "$current < $baseline" | bc -l)" = "1" ]; then echo "Coverage decreased" >&2 exit 1 From cfef83b118d9f8e9192705b80ca6a835ae794123 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 22 Jun 2025 23:43:56 +0100 Subject: [PATCH 3/4] fix coverage extraction --- .github/actions/ratchet-coverage/action.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/actions/ratchet-coverage/action.yml b/.github/actions/ratchet-coverage/action.yml index df027b26..c87dba06 100644 --- a/.github/actions/ratchet-coverage/action.yml +++ b/.github/actions/ratchet-coverage/action.yml @@ -41,7 +41,11 @@ runs: set -euo pipefail output=$(cargo tarpaulin ${{ inputs.args }} --out lcov) echo "$output" - percent=$(echo "$output" | grep -oE '[0-9]+\.[0-9]+(?=%)' | head -n1) + percent=$(echo "$output" | grep -oP '[0-9]+(?:\.[0-9]+)?(?=%)' | head -n1) + if [ -z "$percent" ]; then + echo "Unable to extract coverage percentage" >&2 + exit 1 + fi echo "percent=$percent" >> "$GITHUB_OUTPUT" shell: bash - name: Ratchet coverage From 2a0fc545e313e80ac1fe71b5f2e7072fc7b031c6 Mon Sep 17 00:00:00 2001 From: Leynos Date: Mon, 23 Jun 2025 00:24:33 +0100 Subject: [PATCH 4/4] Refine ratchet coverage action --- .github/actions/ratchet-coverage/README.md | 2 +- .github/actions/ratchet-coverage/action.yml | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/actions/ratchet-coverage/README.md b/.github/actions/ratchet-coverage/README.md index d1741ef7..2387c701 100644 --- a/.github/actions/ratchet-coverage/README.md +++ b/.github/actions/ratchet-coverage/README.md @@ -35,6 +35,6 @@ The action restores the previous coverage baseline using `actions/cache` and installs `cargo-tarpaulin` if necessary. After running the coverage command, it compares the new percentage with the stored baseline. The job fails if coverage has dropped. On success, the baseline file is updated and saved back to the -cache for future runs. +cache using a branch-specific key for future runs. Release history is available in [CHANGELOG](CHANGELOG.md). diff --git a/.github/actions/ratchet-coverage/action.yml b/.github/actions/ratchet-coverage/action.yml index c87dba06..3cdc26d8 100644 --- a/.github/actions/ratchet-coverage/action.yml +++ b/.github/actions/ratchet-coverage/action.yml @@ -20,7 +20,7 @@ runs: uses: actions/cache@v4 with: path: ${{ inputs.baseline-file }} - key: ratchet-baseline-${{ runner.os }} + key: ratchet-baseline-${{ runner.os }}-${{ github.ref_name }} - name: Cache cargo artifacts uses: actions/cache@v4 with: @@ -39,7 +39,7 @@ runs: id: cov run: | set -euo pipefail - output=$(cargo tarpaulin ${{ inputs.args }} --out lcov) + output=$(cargo tarpaulin ${{ inputs.args }} --out lcov 2>&1) echo "$output" percent=$(echo "$output" | grep -oP '[0-9]+(?:\.[0-9]+)?(?=%)' | head -n1) if [ -z "$percent" ]; then @@ -64,6 +64,12 @@ runs: echo "Invalid coverage values" >&2 exit 1 fi + # Ensure we're on Linux where `bc` is guaranteed to be installed + if [[ "$RUNNER_OS" != "Linux" ]]; then + echo "This action only supports Linux runners for float comparisons." >&2 + exit 1 + fi + if [ "$(echo "$current < $baseline" | bc -l)" = "1" ]; then echo "Coverage decreased" >&2 exit 1 @@ -72,7 +78,7 @@ runs: shell: bash - name: Save baseline if: success() - uses: actions/cache/save@v4 + uses: actions/cache@v4 with: path: ${{ inputs.baseline-file }} - key: ratchet-baseline-${{ runner.os }} + key: ratchet-baseline-${{ runner.os }}-${{ github.ref_name }}