name: CI on: push: # dev -> qa -> main. `dev` is where work lands and is pushed to often: these # are the FAST checks. `qa` runs these plus the expensive suite in qa.yml. # `main` only moves at release time, to a commit 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 jobs: # Single source of truth for the MSRV: `rust-version` in Cargo.toml. Every job # that builds AT the MSRV consumes needs.resolve-msrv.outputs.v so the floor # cannot drift. clippy and `consumers` deliberately run on `stable`. resolve-msrv: runs-on: ubuntu-latest timeout-minutes: 5 outputs: v: ${{ steps.msrv.outputs.v }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - id: msrv shell: bash run: echo "v=$(grep -m1 '^rust-version' Cargo.toml | sed -E 's/.*\"([^\"]+)\".*/\1/')" >> "$GITHUB_OUTPUT" # main may never carry a released version without a CHANGELOG.md entry. Hard- # fails on main (and PRs to main); warns elsewhere so an in-progress branch # whose entry isn't written yet is not blocked. changelog: runs-on: ubuntu-latest timeout-minutes: 5 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - 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)." # fmt on the pinned MSRV; clippy on stable so new-lint debt is a blocking gate # the pinned version would hide. --all-targets lints test code too. lint: needs: resolve-msrv runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@stable with: toolchain: stable components: clippy - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} components: rustfmt - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv - run: cargo fmt --check working-directory: libfreemkv - run: cargo +stable clippy --all-targets -- -D warnings working-directory: libfreemkv - run: cargo +stable clippy --all-targets --features keyproof -- -D warnings working-directory: libfreemkv test: needs: resolve-msrv runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv - run: cargo test --tests working-directory: libfreemkv # `--tests` skips doctests; the keys compile_fail doctests enforce spec LK19. - run: cargo test --doc working-directory: libfreemkv # `keyproof` is off by default, so its oracle compiles and is tested only here. - run: cargo test --lib --features keyproof aacs::content working-directory: libfreemkv # The memory orderings in `scsi::fd_handoff` (and `halt`) compile to the same instructions # on x86_64, so the WRONG ones pass every run; loom enumerates the executions # the C++20 model permits and is the only regression coverage that code has. # Its own job because RUSTFLAGS is part of the rust-cache key. Takes the sibling # checkout even with --no-default-features: features don't exempt an optional # path dep from manifest RESOLUTION. loom: needs: resolve-msrv runs-on: ubuntu-latest timeout-minutes: 30 env: RUSTFLAGS: --cfg loom steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} components: clippy - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv - run: cargo test --lib --no-default-features --features scsi fd_handoff working-directory: libfreemkv # LT3: `Halt`'s cancel Release / observe Acquire pair (stop design ยง2.1, SS-23). - run: cargo test --lib --no-default-features --features scsi loom_halt working-directory: libfreemkv # `lint` runs clippy without --cfg loom, so this is the only place the # model code is linted at all. - run: cargo clippy --lib --no-default-features --features scsi --profile test -- -D warnings working-directory: libfreemkv deny: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@stable with: 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: libfreemkv # dev is the FAST lane: the platform jobs run on qa/main only (qa.yml also # covers macOS/Windows). `if` SKIPS (does not queue) so a never-scheduled job # can't block the release gate. check-macos: needs: resolve-msrv if: github.ref_name == 'qa' || github.ref_name == 'main' runs-on: macos-latest timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv - run: cargo check working-directory: libfreemkv check-windows: needs: resolve-msrv if: github.ref_name == 'qa' || github.ref_name == 'main' runs-on: windows-latest timeout-minutes: 45 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv # Build the tests: forces full codegen of the Windows-only SPTI transport # (src/scsi/windows.rs), which never compiles on Linux/macOS dev hosts. - run: cargo build --tests working-directory: libfreemkv # Did this change break anything downstream? libfreemkv sits below all four # dependents; a break here is worth strictly more than anywhere else. Runs # their suites (not just check) โ€” a behavioural change keeps signatures but # fails a dependent's tests, which is the shape worth catching. This caught an # engine fixture break the moment it landed, so it stays on the dev fast lane. consumers: runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: { path: libfreemkv } - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock freemkv-keysources freemkv-engine freemkv-i18n freemkv bdemu - name: Point every dependent at THIS libfreemkv commit shell: bash run: | for c in freemkv-keysources freemkv-engine freemkv bdemu; do mkdir -p "$c/.cargo" cat > "$c/.cargo/config.toml" <<'EOF' [patch.crates-io] libfreemkv = { path = "../libfreemkv" } freemkv-keysources = { path = "../freemkv-keysources" } freemkv-engine = { path = "../freemkv-engine" } freemkv-i18n = { path = "../freemkv-i18n" } freemkv-unlock = { path = "../freemkv-unlock" } EOF done # freemkv's app build (`--features gui`) compiles a GTK4 + libadwaita # shell, so testing it needs these pkg-config dev headers. - name: Install GTK4 + libadwaita development headers (for freemkv) run: | sudo apt-get update sudo apt-get install -y libgtk-4-dev libadwaita-1-dev build-essential # Dependents track the highest supported Rust โ€” build on stable so this job # satisfies the highest dependent floor (the library's own lower MSRV, which # the `test` job verifies, cannot compile them). - uses: dtolnay/rust-toolchain@stable with: toolchain: stable - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: | freemkv-keysources freemkv-engine freemkv bdemu - run: cargo test --tests working-directory: freemkv-keysources - run: cargo test --tests working-directory: freemkv-engine - run: cargo test --tests working-directory: freemkv - run: cargo test --tests --features gui working-directory: freemkv - run: cargo test --tests working-directory: bdemu # Coverage: non-required, informational only (codecov.yml), no job needs: it. coverage: needs: resolve-msrv runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: path: libfreemkv - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: freemkv-unlock patch: freemkv-unlock self: libfreemkv - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ needs.resolve-msrv.outputs.v }} components: llvm-tools-preview - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 with: workspaces: libfreemkv - uses: taiki-e/install-action@72f835688cb6c15903b796f704b01e652a2b8835 # cargo-llvm-cov - run: cargo llvm-cov --workspace --ignore-run-fail --lcov --output-path lcov.info working-directory: libfreemkv - uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7 with: token: ${{ secrets.CODECOV_TOKEN }} files: libfreemkv/lcov.info slug: freemkv/libfreemkv fail_ci_if_error: false