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: jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: path: libfreemkv # libfreemkv path-deps ../freemkv-unlock on the BRANCH tip (release.sh # swaps it to a git tag only inside the tagged commit, then restores the # path dep). CI checks out one repo, so the branch tip has never been # buildable here — every green run you have ever seen was a tag build, # and Windows/Linux were first compiled at release time. # # Both repos go into subdirectories because actions/checkout refuses a # `path:` outside $GITHUB_WORKSPACE, and `../freemkv-unlock` is outside. # With this layout the path dep resolves exactly as it does locally. - uses: actions/checkout@v7 with: repository: freemkv/freemkv-unlock ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}" path: freemkv-unlock - uses: dtolnay/rust-toolchain@1.97.0 with: components: clippy, rustfmt - uses: Swatinem/rust-cache@v2 with: workspaces: libfreemkv - run: cargo fmt --check working-directory: libfreemkv # libfreemkv is a library — Cargo.lock is gitignored. --locked # would always fail on a fresh runner because there's no committed # lockfile to lock against. The binary crates (freemkv, autorip, # bdemu) track Cargo.lock and DO use --locked. # --all-targets so TEST code is linted too. Without it this crate — the # reference implementation for the other seven — was the only one whose # tests had never been linted at all, and it was hiding 74 findings. - run: cargo clippy --all-targets -- -D warnings working-directory: libfreemkv test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: path: libfreemkv # libfreemkv path-deps ../freemkv-unlock on the BRANCH tip (release.sh # swaps it to a git tag only inside the tagged commit, then restores the # path dep). CI checks out one repo, so the branch tip has never been # buildable here — every green run you have ever seen was a tag build, # and Windows/Linux were first compiled at release time. # # Both repos go into subdirectories because actions/checkout refuses a # `path:` outside $GITHUB_WORKSPACE, and `../freemkv-unlock` is outside. # With this layout the path dep resolves exactly as it does locally. - uses: actions/checkout@v7 with: repository: freemkv/freemkv-unlock ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}" path: freemkv-unlock - uses: dtolnay/rust-toolchain@1.97.0 - uses: Swatinem/rust-cache@v2 with: workspaces: libfreemkv - run: cargo test --tests working-directory: libfreemkv 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: release.sh's CI gate refuses while any run for the # commit is still in progress, so a never-scheduled job blocks releases # silently -- see the note on real-media in qa.yml. if: github.ref_name == 'qa' || github.ref_name == 'main' runs-on: macos-latest steps: - uses: actions/checkout@v7 with: path: libfreemkv # libfreemkv path-deps ../freemkv-unlock on the BRANCH tip (release.sh # swaps it to a git tag only inside the tagged commit, then restores the # path dep). CI checks out one repo, so the branch tip has never been # buildable here — every green run you have ever seen was a tag build, # and Windows/Linux were first compiled at release time. # # Both repos go into subdirectories because actions/checkout refuses a # `path:` outside $GITHUB_WORKSPACE, and `../freemkv-unlock` is outside. # With this layout the path dep resolves exactly as it does locally. - uses: actions/checkout@v7 with: repository: freemkv/freemkv-unlock ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}" path: freemkv-unlock - uses: dtolnay/rust-toolchain@1.97.0 - uses: Swatinem/rust-cache@v2 with: workspaces: libfreemkv - run: cargo check working-directory: libfreemkv 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: release.sh's CI gate refuses while any run for the # commit is still in progress, so a never-scheduled job blocks releases # silently -- see the note on real-media in qa.yml. if: github.ref_name == 'qa' || github.ref_name == 'main' runs-on: windows-latest steps: - uses: actions/checkout@v7 with: path: libfreemkv # libfreemkv path-deps ../freemkv-unlock on the BRANCH tip (release.sh # swaps it to a git tag only inside the tagged commit, then restores the # path dep). CI checks out one repo, so the branch tip has never been # buildable here — every green run you have ever seen was a tag build, # and Windows/Linux were first compiled at release time. # # Both repos go into subdirectories because actions/checkout refuses a # `path:` outside $GITHUB_WORKSPACE, and `../freemkv-unlock` is outside. # With this layout the path dep resolves exactly as it does locally. - uses: actions/checkout@v7 with: repository: freemkv/freemkv-unlock ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}" path: freemkv-unlock - uses: dtolnay/rust-toolchain@1.97.0 - uses: Swatinem/rust-cache@v2 with: workspaces: libfreemkv # Build the tests (not just `cargo check`): catches errors in test # code and forces full codegen of the Windows-only SPTI transport # (src/scsi/windows.rs), which never compiles on the Linux/macOS dev # hosts. We don't `cargo test` here — the suite needs no drive but the # extra build is the value; running tests is covered by the Linux job. - run: cargo build --tests working-directory: libfreemkv # ── Did this change break anything downstream? ────────────────────────────── # # Every job above proves libfreemkv builds. None proved its DEPENDENTS do, # and that gap is real: an engine signature change broke autorip today and # went unnoticed because consumer CI only fires on a push to that consumer. # libfreemkv sits below all five of them, so a break here is worth strictly # more than a break anywhere else in the project. # # `cargo check --all-targets` only — each dependent owns its own behaviour # and has its own suite. The question here is just "does everything built on # me still compile against this commit". consumers: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: { path: libfreemkv } - uses: actions/checkout@v7 with: { repository: freemkv/freemkv-unlock, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: freemkv-unlock } - uses: actions/checkout@v7 with: { repository: freemkv/freemkv-keysources, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: freemkv-keysources } - uses: actions/checkout@v7 with: { repository: freemkv/freemkv-engine, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: freemkv-engine } - uses: actions/checkout@v7 with: { repository: freemkv/freemkv-i18n, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: freemkv-i18n } - uses: actions/checkout@v7 with: { repository: freemkv/freemkv, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: freemkv } - uses: actions/checkout@v7 with: { repository: freemkv/autorip, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: autorip } - uses: actions/checkout@v7 with: { repository: freemkv/bdemu, ref: "${{ github.ref_name == 'qa' && 'qa' || 'dev' }}", path: bdemu } - name: Point every dependent at THIS libfreemkv commit shell: bash run: | for c in freemkv-keysources freemkv-engine freemkv autorip 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" } EOF done - uses: dtolnay/rust-toolchain@1.97.0 - uses: Swatinem/rust-cache@v2 with: workspaces: | freemkv-keysources freemkv-engine freemkv autorip bdemu # `cargo check` alone only proves the dependents still COMPILE against # this commit. It cannot see a behavioural change — the library keeps its # signatures and a dependent's tests start failing. That is the shape of # every defect worth catching here, so run their suites too. - 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 working-directory: autorip - run: cargo test --tests working-directory: bdemu