Skip to content

Fix replay deduplication for wrapped messages #1149

Fix replay deduplication for wrapped messages

Fix replay deduplication for wrapped messages #1149

Workflow file for this run

name: Coverage
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
inputs:
baseline_coverage:
description: "Manual baseline coverage % (for testing)"
required: false
default: "0"
concurrency:
group: coverage-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
jobs:
# ── Change Detection ────────────────────────────────────────────────
changes:
name: Detect Changed Paths
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
outputs:
rust: ${{ steps.filter.outputs.rust }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Detect changes
id: filter
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
with:
filters: |
rust:
- '**/*.rs'
- '**/Cargo.toml'
- 'Cargo.lock'
coverage:
needs: changes
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' || needs.changes.outputs.rust == 'true'
runs-on: ubuntu-latest
name: Test Coverage
permissions:
contents: read
pull-requests: write # Required for PR comments
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # pinned from master
with:
toolchain: stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
key: coverage
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y pkg-config
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
with:
tool: cargo-llvm-cov
- name: Generate coverage
run: |
echo "=== Generating Coverage Report ==="
mkdir -p coverage
# Run tests with coverage for all feature combinations
cargo llvm-cov --all-features --workspace --no-report
cargo llvm-cov --no-default-features --workspace --no-report
cargo llvm-cov --no-default-features --features mip04 --workspace --no-report
# Generate all report formats from the merged coverage data
cargo llvm-cov report --lcov --output-path coverage/lcov.info
cargo llvm-cov report --html --output-dir coverage/html
cargo llvm-cov report | tee coverage/summary.txt
echo ""
echo "=== Coverage Summary ==="
cat coverage/summary.txt
- name: Extract coverage percentage
id: coverage
run: |
chmod +x scripts/extract-coverage.sh
COVERAGE=$(./scripts/extract-coverage.sh coverage/lcov.info)
echo "percentage=$COVERAGE" >> "$GITHUB_OUTPUT"
echo "Current coverage: $COVERAGE%"
- name: Get master branch coverage (for PRs)
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch'
id: master_coverage
env:
BASELINE_INPUT: ${{ github.event.inputs.baseline_coverage }}
GH_TOKEN: ${{ github.token }}
run: |
# Numeric percentage: integer or decimal, no leading sign. Anything else
# is rejected so it can't flow into downstream expression expansions
# (Compare coverage / github-script) as a shell or JS payload.
NUMERIC_RE='^[0-9]+(\.[0-9]+)?$'
# Check if manual baseline provided (for testing).
# The workflow_dispatch input defaults to "0", which is a sentinel meaning
# "not provided" — fall through to the master-artifact path in that case.
if [ -n "$BASELINE_INPUT" ] && [ "$BASELINE_INPUT" != "0" ]; then
if ! [[ "$BASELINE_INPUT" =~ $NUMERIC_RE ]]; then
echo "::error::baseline_coverage must be a numeric percentage (got: $BASELINE_INPUT)"
exit 1
fi
MASTER_COV="$BASELINE_INPUT"
echo "baseline=$MASTER_COV" >> "$GITHUB_OUTPUT"
echo "Using manual baseline: $MASTER_COV%"
else
echo "🔍 Searching for baseline from master branch..."
# Get the latest completed coverage workflow run ID from master
RUN_ID=$(gh run list \
--repo ${{ github.repository }} \
--workflow=coverage.yml \
--branch master \
--status completed \
--limit 1 \
--json databaseId \
--jq '.[0].databaseId')
if [ -z "$RUN_ID" ] || [ "$RUN_ID" = "null" ]; then
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ No completed coverage workflow run found on master"
exit 0
fi
echo "Found workflow run ID: $RUN_ID"
# Download the baseline artifact from that specific run
if gh run download "$RUN_ID" \
--repo ${{ github.repository }} \
--name coverage-baseline \
--dir master-coverage; then
if [ -f master-coverage/coverage-baseline.txt ]; then
MASTER_COV=$(cat master-coverage/coverage-baseline.txt)
if ! [[ "$MASTER_COV" =~ $NUMERIC_RE ]]; then
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ Baseline artifact contained non-numeric data, ignoring"
exit 0
fi
echo "baseline=$MASTER_COV" >> "$GITHUB_OUTPUT"
echo "✓ Master branch coverage: $MASTER_COV%"
else
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ Baseline file not found in artifact"
fi
else
echo "baseline=0" >> "$GITHUB_OUTPUT"
echo "⚠️ Failed to download baseline artifact from run $RUN_ID"
fi
fi
- name: Compare coverage
if: (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch') && steps.master_coverage.outputs.baseline != '0'
run: |
CURRENT=${{ steps.coverage.outputs.percentage }}
BASELINE=${{ steps.master_coverage.outputs.baseline }}
echo "=== Coverage Comparison ==="
echo "Master branch: $BASELINE%"
echo "This PR: $CURRENT%"
DIFF=$(awk "BEGIN {printf \"%.2f\", $CURRENT - $BASELINE}")
if (( $(awk "BEGIN {print ($CURRENT < $BASELINE)}") )); then
echo "❌ Coverage decreased by $DIFF%"
echo "::error::Coverage regression detected. Coverage decreased from $BASELINE% to $CURRENT% (-$DIFF%)"
exit 1
elif (( $(awk "BEGIN {print ($CURRENT > $BASELINE)}") )); then
echo "✅ Coverage improved by $DIFF%"
else
echo "✅ Coverage maintained at $BASELINE%"
fi
- name: Comment PR with coverage change
if: always() && github.event_name == 'pull_request' && steps.master_coverage.outputs.baseline != '0'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const current = parseFloat('${{ steps.coverage.outputs.percentage }}');
const baseline = parseFloat('${{ steps.master_coverage.outputs.baseline }}');
const diff = (current - baseline).toFixed(2);
let emoji = current > baseline ? '✅' : current < baseline ? '❌' : '⚠️';
const commentMarker = '<!-- coverage-comment -->';
// Build the message (with markdown header for comments, without for logs)
const commentBody = `${commentMarker}\n## ${emoji} Coverage: ${baseline}% → ${current}% (${diff > 0 ? '+' : ''}${diff}%)`;
const logMessage = `${emoji} Coverage: ${baseline}% → ${current}% (${diff > 0 ? '+' : ''}${diff}%)`;
// Skip commenting on PRs from forks (they don't have write permissions)
const isFork = context.payload.pull_request.head.repo.full_name !== context.payload.repository.full_name;
if (isFork) {
console.log('Skipping PR comment: PR is from a fork and does not have write permissions');
console.log('Coverage result:', logMessage);
return;
}
try {
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
const existing = comments.find(c =>
c.body.includes(commentMarker) && c.user.type === 'Bot'
);
if (existing) {
await github.rest.issues.updateComment({
comment_id: existing.id,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
} else {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
}
} catch (error) {
// Gracefully handle permission errors (e.g., from forks)
if (error.status === 403) {
console.log('Unable to comment on PR (likely from fork):', error.message);
console.log('Coverage result:', logMessage);
} else {
throw error;
}
}
- name: Save coverage baseline (master only)
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
run: |
mkdir -p coverage-baseline
echo "${{ steps.coverage.outputs.percentage }}" > coverage-baseline/coverage-baseline.txt
echo "Saved baseline: ${{ steps.coverage.outputs.percentage }}%"
- name: Upload coverage baseline (master only)
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-baseline
path: coverage-baseline/coverage-baseline.txt
retention-days: 90
- name: Upload HTML coverage report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report-html
path: coverage/html/
retention-days: 90
- name: Upload lcov coverage report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-report-lcov
path: coverage/lcov.info
retention-days: 90
- name: Upload coverage summary
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-summary
path: coverage/summary.txt
retention-days: 90