name: qa # ── The qa gate: "is this production worth?" ──────────────────────────────── # # dev -> qa -> main. # # `dev` is for committing often. ci.yml answers "is it green" in minutes with # fmt, clippy and the unit suite, so a mistake surfaces while it is still cheap # to fix. `qa` is the release-candidate branch, and THIS workflow is the claim # that a commit is production worth: everything expensive that can run without # physical media. `main` only ever receives a qa that went green here. # # Sibling repos are checked out at `qa`, NOT `dev`. A qa run that resolved its # dependencies from dev tips would be validating a combination that is not the # one being released, which is the exact failure this branch exists to prevent. # The shared checkout-siblings composite resolves qa->qa from the caller branch. # # What this gate CANNOT cover: `disc://` and real `iso://` need physical media, # and no hosted runner has an optical drive or the image hoard. Those run on a # self-hosted runner (see the media job at the end) and are the one leg that # stays on hardware. on: push: branches: [qa] workflow_dispatch: # Supersede an in-progress qa run when a newer candidate lands on the branch. concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # Single source of truth for the MSRV — see ci.yml. Jobs that build/test AT # the MSRV read needs.resolve-msrv.outputs.v from Cargo.toml's rust-version; # clippy (cross-lint) runs on stable instead. 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" # ── Name the candidate ──────────────────────────────────────────────── # # Every push to `qa` is a release candidate, so every push gets a tag: # v-rc, N incrementing. That is the answer to "which build is on # qa right now, and is it the one I tested?" — a question that otherwise gets # answered from memory. # # This runs FIRST and does not depend on the gates, deliberately. A red # candidate needs a name more than a green one does: "rc3 failed # release-tests on windows" is a sentence you can act on; "qa is red" is not. # Red on qa is a working gate, not an incident — it is the branch saying this # is not production worth yet. Fix on dev, get dev green, push qa again. # # release.yml excludes v*-rc* so a candidate never publishes a release. rc-tag: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - name: Stamp the next rc shell: bash run: | set -euo pipefail v=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1) [ -n "$v" ] || { echo "no version in Cargo.toml" >&2; exit 1; } # Numeric sort on the rc ordinal: -rc10 must beat -rc9, and a plain # lexical sort gets that backwards from the tenth candidate on. n=$(git tag -l "v$v-rc*" | sed "s|^v$v-rc||" | sort -n | tail -1) tag="v$v-rc$(( ${n:-0} + 1 ))" git tag "$tag" git push origin "$tag" echo "### Candidate \`$tag\`" >> "$GITHUB_STEP_SUMMARY" # The debug suite runs on every dev push. Release is a DIFFERENT build: # overflow checks are off, debug_assert! is compiled out, and inlining # changes what the optimiser can prove. A test that only passes in debug is # a test that never guarded the binary anyone actually ships. release-tests: needs: resolve-msrv strategy: fail-fast: false matrix: os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 with: path: freemkv-keysources - uses: freemkv/.github/.github/actions/checkout-siblings@v1 with: checkout: libfreemkv freemkv-unlock patch: libfreemkv 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 --release --tests working-directory: freemkv-keysources # clippy's output is target-dependent: cfg-gated code only gets linted on # the target it compiles for. Linting solely on the dev machine's host # target is how a lint that CI rejects reaches a push. cross-lint: 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 self: freemkv-keysources - uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 with: workspaces: freemkv-keysources - run: rustup target add x86_64-unknown-linux-gnu - run: cargo clippy --all-targets --target x86_64-unknown-linux-gnu -- -D warnings working-directory: freemkv-keysources # Windows compiles the tests but does not run them, matching the policy # ci.yml already set. The value here is codegen: the #[cfg(windows)] halves # of the SCSI transport and platform layers compile on no other runner, so # without this they are first built at release time. windows-build: needs: resolve-msrv 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 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 --release --tests working-directory: freemkv-keysources