Files
freemkv-keysources/.github/workflows/ci.yml
T

263 lines
11 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
# WHY THE SIBLING CHECKOUTS — this is NOT a monorepo.
#
# The committed Cargo.toml resolves libfreemkv from a git TAG (it is off
# crates.io). That means CI was building this branch against the LAST RELEASE
# of libfreemkv, never against libfreemkv's dev tip — so a coordinated change
# across the two repos could not be verified until release-orchestrate.yml cut the tags.
# Green here meant "compiles against last month's library".
#
# The fix mirrors the developer's local layout via the shared composite
# freemkv/.github//actions/checkout-siblings: every sibling repo is checked out
# into a subdirectory of $GITHUB_WORKSPACE, reproducing
# ~/Developer/freemkv/{freemkv-keysources,libfreemkv,freemkv-unlock}, and a
# generated .cargo/config.toml redirects the git-tag patch to those paths.
# Config-level [patch.crates-io] wins over the manifest one, which is the same
# mechanism local development already uses (see the note in Cargo.toml).
#
# Subdirectories rather than `../libfreemkv` because actions/checkout refuses
# any `path:` outside $GITHUB_WORKSPACE, and the workspace IS the repo dir.
# Repos stay separate, tags stay separate, the release cascade is untouched.
#
# libfreemkv path-deps ../freemkv-unlock, which resolves to the workspace root
# under this layout — hence the third checkout.
#
# `--locked` is deliberately absent: Cargo.lock is now committed (issue #15) and
# the `deny` job audits the resolved graph, but CI's sibling path-patch layout
# resolves the freemkv crates as path packages, which `--locked` can't satisfy —
# the same reason the binary crates omit it. The lock that has to hold is the one
# release-orchestrate.yml regenerates in each consuming binary.
jobs:
# Single source of truth for the MSRV — read rust-version from Cargo.toml once
# and export it; MSRV-bound jobs consume needs.resolve-msrv.outputs.v. clippy
# runs on stable instead (see CI.md's Toolchain policy).
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
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
# fmt runs on the MSRV pin; clippy runs on stable so new-lint debt is a
# blocking gate the pinned toolchain would hide. Stable is installed first
# for clippy; the MSRV pin goes second so it stays fmt's default toolchain.
- 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:
workspaces: freemkv-keysources
- run: cargo fmt --check
working-directory: freemkv-keysources
# --all-targets so the test code is linted too, not just the library.
- run: cargo +stable clippy --all-targets -- -D warnings
working-directory: freemkv-keysources
test:
needs: resolve-msrv
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.resolve-msrv.outputs.v }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: freemkv-keysources
- run: cargo test --tests
working-directory: freemkv-keysources
# Dependency-audit gate. Mirrors the `test` job's sibling-checkout layout and
# generated .cargo/config.toml so `cargo metadata` resolves the git-tag patch
# to the local sibling paths; cargo-deny then audits advisories, bans,
# licenses, and sources against the committed deny.toml.
deny:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-deny
# Run from the repo dir so the generated .cargo/config.toml path patches apply.
- run: cargo deny --all-features check advisories bans licenses sources
working-directory: freemkv-keysources
# This crate ships wherever freemkv and autorip ship, and it drags in all of
# libfreemkv's platform code (SCSI transports included). Build the tests, not
# `cargo check` — check skips codegen and never compiles test-only code.
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
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.resolve-msrv.outputs.v }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: freemkv-keysources
- run: cargo build --tests
working-directory: freemkv-keysources
# Additive, NON-REQUIRED coverage. Mirrors the `test` job's sibling-checkout
# layout and pinned toolchain (nothing in the release cascade depends on it,
# and codecov upload is tokenless with fail_ci_if_error:false, so a flaky
# upload never reddens the run). `--locked` is deliberately absent here for
# the same reason it is in `test`: CI's sibling path-patch layout resolves the
# freemkv crates as path packages, which `--locked` can't satisfy.
coverage:
needs: resolve-msrv
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.resolve-msrv.outputs.v }}
components: llvm-tools-preview
- uses: Swatinem/rust-cache@v2
with:
workspaces: freemkv-keysources
- uses: taiki-e/install-action@cargo-llvm-cov
- run: cargo llvm-cov --workspace --ignore-run-fail --lcov --output-path lcov.info
working-directory: freemkv-keysources
- uses: codecov/codecov-action@v7
with:
files: freemkv-keysources/lcov.info
slug: freemkv/freemkv-keysources
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
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
with:
path: freemkv-keysources
- uses: freemkv/.github/.github/actions/checkout-siblings@v1
with:
checkout: libfreemkv freemkv-unlock
patch: libfreemkv freemkv-unlock
self: freemkv-keysources
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ needs.resolve-msrv.outputs.v }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: freemkv-keysources
- run: cargo build --tests
working-directory: freemkv-keysources