mirror of
https://github.com/freemkv/freemkv-unlock.git
synced 2026-10-07 18:13:11 +00:00
209 lines
9.2 KiB
YAML
209 lines
9.2 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
# dev -> qa -> main. `dev` is where work lands and is meant to be pushed
|
|
# to often: these are the FAST checks, so a mistake surfaces in minutes.
|
|
# `qa` is the release candidate — it runs these too, plus the expensive
|
|
# suite in qa.yml. `main` only ever moves at release time, to a tagged
|
|
# commit that was already green on qa.
|
|
branches: [main, dev, qa]
|
|
pull_request:
|
|
|
|
# Rapid dev pushes supersede their own in-progress runs instead of stacking.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# freemkv-unlock is the BASE of the whole ecosystem: libfreemkv path-deps it,
|
|
# bdemu path-deps it with the `emulation` feature, and every other crate pulls
|
|
# it in transitively. It has no freemkv dependencies of its own, so no sibling
|
|
# checkout is needed here — but it is also the crate whose breakage takes all
|
|
# seven others down with it, which is why it gets the full OS matrix rather
|
|
# than a token Linux run.
|
|
#
|
|
# Cargo.lock is now committed (issue #15), so the resolved dependency graph is
|
|
# recorded and the `deny` job audits it. `--locked` is still deliberately NOT
|
|
# used, matching the binary crates' ci.yml: CI's sibling path-patch layout
|
|
# resolves the freemkv crates as path packages, which `--locked` can't satisfy.
|
|
|
|
jobs:
|
|
# ── Single source of truth for the MSRV ─────────────────────────────────────
|
|
#
|
|
# `rust-version` in Cargo.toml is the ONE place the MSRV is declared. This job
|
|
# reads it; every job that must build/test AT the MSRV consumes
|
|
# `needs.resolve-msrv.outputs.v` instead of a hardcoded pin, so the floor
|
|
# cannot drift out of sync across workflow files. The `test` job building
|
|
# green at this value IS the verification that the declared MSRV is real.
|
|
# clippy deliberately does NOT use it — it runs on `stable` (lint debt is
|
|
# caught against current Rust).
|
|
resolve-msrv:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
v: ${{ steps.msrv.outputs.v }}
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- id: msrv
|
|
shell: bash
|
|
run: echo "v=$(grep -m1 '^rust-version' Cargo.toml | sed -E 's/.*\"([^\"]+)\".*/\1/')" >> "$GITHUB_OUTPUT"
|
|
|
|
# CI owns the changelog: main may never carry a released version without a
|
|
# CHANGELOG.md entry for it. The release orchestrator dates an existing
|
|
# `## [X.Y.Z]` heading — it does NOT invent one — so a version bump that
|
|
# forgot its changelog entry must fail here rather than ship a blank release.
|
|
# Hard-fails on main (and PRs targeting main); warns elsewhere so an
|
|
# in-progress dev/qa branch whose entry isn't written yet is not blocked.
|
|
changelog:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- name: Require a CHANGELOG entry for the crate version on main
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
version=$(grep -m1 '^version = ' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
|
|
if grep -qE "^## \[${version}\]" CHANGELOG.md; then
|
|
echo "CHANGELOG.md has an entry for ${version}."
|
|
exit 0
|
|
fi
|
|
msg="CHANGELOG.md has no '## [${version}]' entry"
|
|
if [ "${GITHUB_REF_NAME:-}" = "main" ] || [ "${GITHUB_BASE_REF:-}" = "main" ]; then
|
|
echo "::error::${msg} — main requires a changelog entry for the released version."
|
|
exit 1
|
|
fi
|
|
echo "::warning::${msg} (allowed on ${GITHUB_REF_NAME:-unknown}; required before it can reach main)."
|
|
|
|
lint:
|
|
needs: resolve-msrv
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
# fmt runs on the pinned MSRV toolchain; clippy runs on stable. Linting on
|
|
# stable makes new-lint debt a BLOCKING gate the pinned version would
|
|
# otherwise hide. The MSRV pin is installed second so it stays the default
|
|
# toolchain (fmt uses it implicitly); clippy is invoked with `+stable`.
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
components: clippy
|
|
- uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: ${{ needs.resolve-msrv.outputs.v }}
|
|
components: rustfmt
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: freemkv-unlock-ci
|
|
- run: cargo fmt --check
|
|
# --all-targets so the test code is linted too, not just the library.
|
|
- run: cargo +stable clippy --all-targets -- -D warnings
|
|
|
|
test:
|
|
needs: resolve-msrv
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: ${{ needs.resolve-msrv.outputs.v }}
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: freemkv-unlock-ci
|
|
- run: cargo test --tests
|
|
# `emulation` is off by default and is the face bdemu builds against. A
|
|
# feature nothing compiles is a feature that breaks at release time.
|
|
- run: cargo test --tests --features emulation
|
|
|
|
# Coverage is additive and NON-REQUIRED: it uploads to Codecov but never
|
|
# gates a merge (fail_ci_if_error is false, and it is not wired into any
|
|
# release gate). It mirrors the `test` job's environment — same pinned
|
|
# toolchain, ubuntu-latest, no `if:` so it runs on every branch and PR just
|
|
# as `test` does. `--locked` is intentionally omitted to match this crate's
|
|
# policy (Cargo.lock is committed but --locked can't satisfy the path-patch layout).
|
|
coverage:
|
|
needs: resolve-msrv
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: ${{ needs.resolve-msrv.outputs.v }}
|
|
components: llvm-tools-preview
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: freemkv-unlock-coverage
|
|
- uses: taiki-e/install-action@cargo-llvm-cov
|
|
- run: cargo llvm-cov --workspace --ignore-run-fail --lcov --output-path lcov.info
|
|
- uses: codecov/codecov-action@v7
|
|
with:
|
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
files: lcov.info
|
|
slug: freemkv/freemkv-unlock
|
|
fail_ci_if_error: false
|
|
|
|
# cargo-deny gates the RESOLVED dependency graph: RUSTSEC advisories, a
|
|
# permissive licence allow-list, and source/ban hygiene. It mirrors the
|
|
# `test` job's checkout (single root checkout, no siblings) and runs on
|
|
# `stable` — advisory/licence debt is caught against current Rust, not the
|
|
# MSRV pin — so no `needs: resolve-msrv`.
|
|
deny:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
|
with:
|
|
command: check advisories bans licenses sources
|
|
manifest-path: Cargo.toml
|
|
|
|
# The crate ships on macOS and Windows through its consumers, so it has to
|
|
# compile there. Build the tests (not just `cargo check`) — that forces
|
|
# codegen and catches errors in test-only code, which `check` skips.
|
|
check-macos:
|
|
# dev is the FAST lane: this job still runs, but on the release-candidate
|
|
# branches rather than on every push to dev. Nothing is deleted and no
|
|
# platform stops being checked before a release -- qa.yml independently
|
|
# covers macOS and Windows, and the jobs unique to this file (the Intel
|
|
# macOS build, the Windows release build) run here on qa and main. A push
|
|
# to dev is meant to be cheap and frequent; waiting on three runner pools
|
|
# to agree is what a release candidate is for.
|
|
#
|
|
# `if` SKIPS the job (it does not queue). A queued job would be far worse
|
|
# than a slow one: a never-scheduled job leaves the run pending forever, so
|
|
# the branch's required checks never report complete.
|
|
needs: resolve-msrv
|
|
if: github.ref_name == 'qa' || github.ref_name == 'main'
|
|
runs-on: macos-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: ${{ needs.resolve-msrv.outputs.v }}
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: freemkv-unlock-ci
|
|
- run: cargo build --tests --all-features
|
|
|
|
check-windows:
|
|
# dev is the FAST lane: this job still runs, but on the release-candidate
|
|
# branches rather than on every push to dev. Nothing is deleted and no
|
|
# platform stops being checked before a release -- qa.yml independently
|
|
# covers macOS and Windows, and the jobs unique to this file (the Intel
|
|
# macOS build, the Windows release build) run here on qa and main. A push
|
|
# to dev is meant to be cheap and frequent; waiting on three runner pools
|
|
# to agree is what a release candidate is for.
|
|
#
|
|
# `if` SKIPS the job (it does not queue). A queued job would be far worse
|
|
# than a slow one: a never-scheduled job leaves the run pending forever, so
|
|
# the branch's required checks never report complete.
|
|
needs: resolve-msrv
|
|
if: github.ref_name == 'qa' || github.ref_name == 'main'
|
|
runs-on: windows-latest
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@master
|
|
with:
|
|
toolchain: ${{ needs.resolve-msrv.outputs.v }}
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
shared-key: freemkv-unlock-ci
|
|
- run: cargo build --tests --all-features
|