Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ updates:
directory: /
schedule:
interval: weekly
# A release has a week to be yanked or flagged before it is proposed.
# Security updates ignore the cooldown.
cooldown:
default-days: 7
groups:
actions:
patterns: ["*"]
Expand All @@ -18,6 +22,10 @@ updates:
directory: /
schedule:
interval: weekly
# A release has a week to be yanked or flagged before it is proposed.
# Security updates ignore the cooldown.
cooldown:
default-days: 7
groups:
cargo:
update-types: [minor, patch]
29 changes: 17 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@ on:
push:
branches: [main]
pull_request:
# Advisories land without a code change; surface them on main rather than
# on whichever unrelated pull request happens to run next.
schedule:
- cron: "0 6 * * 1"

permissions:
contents: read

# A newer run cancels an older one, except on main, where a run may be holding
# the release window below open with the previous release already deleted, and
# cancelling it would leave no release at all. A newer pending run still replaces an older pending one, so
# only the newest push waiting on main survives. The event is in the key so
# the weekly scheduled run, which never releases, cannot be what replaces it.
# only the newest push waiting on main survives.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

# Every action is pinned to a commit, with its release in a comment for
Expand All @@ -45,6 +40,8 @@ jobs:
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: ${{ env.PINNED_RUST }}
Expand Down Expand Up @@ -72,11 +69,19 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: ${{ env.PINNED_RUST }}
components: clippy, rustfmt
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
# Workflow security audit (unpinned actions, excess permissions, template
# injection). Offline because the online audits need a token.
- uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30
with:
tool: zizmor@1.31.1
- run: zizmor --offline .github
- run: cargo fmt --check
- run: python3 tests/coverage.py
- uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30
Expand All @@ -91,13 +96,13 @@ jobs:

coverage:
needs: test
if: github.event_name != 'schedule'
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: coverage-*
Expand Down Expand Up @@ -145,6 +150,8 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: 1.88.0
Expand All @@ -156,6 +163,8 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
# cargo audit itself rather than rustsec/audit-check, whose releases
# still run on Node.js 20. Pinned like the lint toolchain.
- uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30
Expand All @@ -168,12 +177,8 @@ jobs:
# Prebuilt binaries, one per platform the test matrix above runs on. A binary
# for a platform whose suite never ran there is a promise nobody checked, so
# widen that matrix before adding a row here.
#
# Not on the weekly schedule: that run exists for the audit and never
# releases, so binaries built there would go nowhere.
build:
name: Binary ${{ matrix.target }}
if: github.event_name != 'schedule'
strategy:
fail-fast: false
matrix:
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Security

# Advisories land without a code change, so this runs on a clock rather than
# on pushes. ci.yml still audits every push and pull request, and gates the
# release on it; this is the run that notices a new advisory between them.
on:
schedule:
- cron: "17 6 * * *"
workflow_dispatch:

permissions:
contents: read

jobs:
audit:
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: taiki-e/install-action@a4d3f3f761dcd95cce1b72cb61cb6d1963438177 # v2.87.30
with:
tool: cargo-audit@0.22.2
# Same flags as ci.yml: unsound and yanked crates fail like vulnerabilities.
- run: cargo audit --deny warnings
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,21 @@ TTL alone never supplies content for conflict detection.

## Continuous integration

`.github/workflows/ci.yml` runs on pushes to `main`, on pull requests, and
weekly:
`.github/workflows/ci.yml` runs on pushes to `main` and on pull requests:

| Job | What it checks |
|-----|----------------|
| `test` | the full suite with line coverage on Linux, macOS, and Windows |
| `lint` | formatting, coverage-script tests, unused dependencies, duplicate reporting, and clippy on a pinned toolchain |
| `coverage` | merged platform reports and the changed-line coverage ratchet; skipped on the weekly run |
| `lint` | formatting, workflow security (zizmor), coverage-script tests, unused dependencies, duplicate reporting, and clippy on a pinned toolchain |
| `coverage` | merged platform reports and the changed-line coverage ratchet |
| `msrv` | the build on the `rust-version` in `Cargo.toml` |
| `audit` | RustSec advisories against `Cargo.lock` |
| `build` | release binaries for each tested platform, Linux against glibc 2.17; skipped on the weekly run |
| `build` | release binaries for each tested platform, Linux against glibc 2.17 |
| `release` | on pushes to `main` only, after every job above passes, replaces the `latest` GitHub release |

`.github/workflows/security.yml` runs the same `cargo audit` daily and on demand,
because advisories land without a code change; Dependabot proposes the fix.

The release scripts live in `.ci/`; their header comments explain the
ordering that keeps a failed run from leaving no release behind.

Expand Down
Loading