name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
check:
uses: ./.github/workflows/check.yml
cross-build:
name: Build + test (${{ matrix.target }})
needs: [check]
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
- uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f with:
toolchain: 1.99
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 with:
key: ${{ matrix.target }}
- name: Install cross-compilation tools
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
- name: Build
run: cargo build --locked --release --target ${{ matrix.target }} --all-targets --all-features
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
- name: Test
if: matrix.target != 'aarch64-unknown-linux-gnu'
run: cargo test --locked --target ${{ matrix.target }} --all-targets --all-features
coverage:
name: Coverage
needs: [check]
runs-on: ubuntu-latest
env:
COVERAGE_MIN_TOTAL: "80"
COVERAGE_MIN_DIFF: "80"
DIFF_COVER_VERSION: "10.6.0"
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@e2a55d2ffb04f378e9626c28d38b36d230d1e12f with:
toolchain: 1.99
components: llvm-tools-preview
- name: Cache cargo build artifacts
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 with:
key: coverage
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@1168f6810a3cb30a3514bf5756a3b7f0422c6df8 with:
tool: cargo-llvm-cov@0.9.1
- name: Test with coverage
run: cargo llvm-cov --no-report --locked --all-targets --all-features
- name: Generate reports
run: |
cargo llvm-cov report --lcov --output-path lcov.info
cargo llvm-cov report --html --output-dir coverage
cargo llvm-cov report --json --summary-only --output-path coverage-summary.json
lines=$(jq '.data[0].totals.lines.percent' coverage-summary.json)
printf '## Coverage\n\nLine coverage: **%.2f%%** (minimum %s%%)\n\n' \
"$lines" "$COVERAGE_MIN_TOTAL" >> "$GITHUB_STEP_SUMMARY"
- name: Check coverage of changed lines
if: github.event_name == 'pull_request'
env:
BASE_REF: ${{ github.base_ref }}
run: |
status=0
pipx run "diff-cover==${DIFF_COVER_VERSION}" lcov.info \
--compare-branch="origin/${BASE_REF}" \
--format markdown:diff-cover.md,json:diff-cover.json \
--fail-under="$COVERAGE_MIN_DIFF" || status=$?
cat diff-cover.md >> "$GITHUB_STEP_SUMMARY"
exit "$status"
- name: Check total coverage
if: ${{ !cancelled() }}
run: cargo llvm-cov report --summary-only --fail-under-lines "$COVERAGE_MIN_TOTAL"
- name: Upload coverage reports
id: upload-reports
if: ${{ !cancelled() }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with:
name: coverage
path: |
lcov.info
coverage/html
diff-cover.md
if-no-files-found: ignore
- name: Write pull request comment
if: ${{ github.event_name == 'pull_request' && !cancelled() && hashFiles('coverage-summary.json') != '' }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
REPORT_URL: ${{ steps.upload-reports.outputs.artifact-url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
# Prints a pass or fail mark for a percentage against a minimum,
# using the same comparison as the checks above.
mark() { awk -v v="$1" -v m="$2" 'BEGIN { print (v < m) ? ":x:" : ":white_check_mark:" }'; }
total=$(jq '.data[0].totals.lines.percent' coverage-summary.json)
changed=0
{
printf '## Coverage\n\n'
printf '| | Line coverage | Minimum | |\n|---|---:|---:|:-:|\n'
printf '| Whole crate | %.2f%% | %s%% | %s |\n' \
"$total" "$COVERAGE_MIN_TOTAL" "$(mark "$total" "$COVERAGE_MIN_TOTAL")"
if [ -f diff-cover.json ]; then
changed=$(jq '.total_num_lines' diff-cover.json)
missing=$(jq '.total_num_violations' diff-cover.json)
if [ "$changed" -eq 0 ]; then
printf '| Changed lines | none with coverage data | %s%% | |\n' "$COVERAGE_MIN_DIFF"
else
diff=$(jq '.total_percent_covered' diff-cover.json)
printf '| Changed lines (%s of %s covered) | %.2f%% | %s%% | %s |\n' \
"$((changed - missing))" "$changed" "$diff" "$COVERAGE_MIN_DIFF" \
"$(mark "$diff" "$COVERAGE_MIN_DIFF")"
fi
fi
printf '\n'
# A comment body is limited to 65536 characters; the per-file
# listing goes to the job summary only when it is longer.
if [ -f diff-cover.md ] && [ "$changed" -gt 0 ]; then
if [ "$(wc -c < diff-cover.md)" -le 50000 ]; then
printf '<details><summary>Changed lines by file</summary>\n\n'
cat diff-cover.md
printf '\n</details>\n\n'
else
printf 'The list of changed lines by file is too long for a comment; it is in the workflow run summary.\n\n'
fi
fi
if [ -n "$REPORT_URL" ]; then
printf '[Coverage report (HTML and LCOV, zip)](%s) ยท ' "$REPORT_URL"
fi
printf '[Workflow run](%s)\n' "$RUN_URL"
} > comment.md
mkdir -p coverage-comment
mv comment.md coverage-comment/comment.md
echo "$PR_NUMBER" > coverage-comment/pr-number
- name: Upload pull request comment
if: ${{ github.event_name == 'pull_request' && !cancelled() && hashFiles('coverage-comment/comment.md') != '' }}
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 with:
name: coverage-comment
path: coverage-comment
retention-days: 7