From 7e3129a303ae63949f048c42b0b61c5ec0dd2ff7 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 24 Apr 2026 12:42:18 +0100 Subject: [PATCH] ci: Consolidate PR/MQ workflows into a single ci.yaml Replace existing CI files with a consolidated one to delay starting resource intensive CI jobs until after less resource intensive ones have completed: e.g. don't start integration tests if the basic build tests fail. Architecture: - Trigger on [pull_request, merge_group]; concurrency keyed per PR/ref. - preflight job classifies changed paths and exposes a 'full' flag that gates the heavier build/quality/integration suite. Any CI change classifies itself as full so it is exercised. - Leaf jobs gate at the job level using preflight outputs; doc-only, openapi-only, dockerfile-only and similar PRs skip the full suite. - integration-x86-64-pr runs the (garm-jammy, gnu) slice on PR and MQ; integration-x86-64-mq runs the other 3 matrix entries on MQ only. - integration-{arm64, vfio, windows, rate-limiter} are MQ-only. - integration jobs gate on dco/quality/build success. - A single all-green aggregator job is the only required-status check; it folds in every leaf job via `needs`. Assisted-by: Claude:Opus-4.7 Signed-off-by: Rob Bradford --- .github/workflows/ci.yaml | 769 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 000000000..cebcfa17a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,769 @@ +name: CI +on: [pull_request, merge_group] +permissions: + contents: read + pull-requests: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }} + cancel-in-progress: true +jobs: + preflight: + name: preflight + runs-on: ubuntu-latest + outputs: + full: ${{ steps.classify.outputs.full }} + rust: ${{ steps.changes.outputs.rust }} + cargo: ${{ steps.changes.outputs.cargo }} + openapi: ${{ steps.changes.outputs.openapi }} + dockerfile: ${{ steps.changes.outputs.dockerfile }} + shell: ${{ steps.changes.outputs.shell }} + ci: ${{ steps.changes.outputs.ci }} + docs: ${{ steps.changes.outputs.docs }} + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - id: changes + uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + with: + filters: | + rust: + - '**/*.rs' + - 'build.rs' + - '**/Cargo.toml' + - '**/Cargo.lock' + - 'rust-toolchain.toml' + cargo: + - '**/Cargo.toml' + - '**/Cargo.lock' + openapi: + - 'vmm/src/api/openapi/**' + dockerfile: + - 'resources/Dockerfile' + shell: + - '**/*.sh' + - 'scripts/**' + ci: + - '.github/workflows/**' + docs: + - 'docs/**' + - '**/*.md' + - '.github/ISSUE_TEMPLATE/**' + - 'LICENSES/**' + - 'CODEOWNERS' + - id: classify + name: Classify changes + run: | + set -eufo pipefail + full=false + if [[ "${{ steps.changes.outputs.rust }}" == "true" \ + || "${{ steps.changes.outputs.dockerfile }}" == "true" \ + || "${{ steps.changes.outputs.shell }}" == "true" \ + || "${{ steps.changes.outputs.ci }}" == "true" ]]; then + full=true + fi + echo "full=$full" >> "$GITHUB_OUTPUT" + echo "full=$full" + dco: + name: dco + needs: [preflight] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up Python 3.x + uses: actions/setup-python@v6 + with: + python-version: '3.x' + - name: Check DCO + if: github.event_name == 'pull_request' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eufo pipefail + pip3 install -U dco-check + dco-check -e "49699333+dependabot[bot]@users.noreply.github.com" + gitlint: + name: gitlint + needs: [preflight] + # PR-only: gitlint needs GITHUB_BASE_REF, unset on merge_group. + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + # PR head, not the merge ref, so gitlint sees the PR's commits. + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Set up Python 3.10 + uses: actions/setup-python@v6 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade gitlint + - name: Lint git commit messages + run: | + gitlint --commits "origin/$GITHUB_BASE_REF.." + lychee: + name: lychee + needs: [preflight] + if: needs.preflight.outputs.docs == 'true' || needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Get changed files in PR + id: changed-files + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 + with: + base_sha: ${{ github.event.pull_request.base.sha }} + - name: Verify Changed Files + run: | + set -eufo pipefail + echo "--- tj-actions/changed-files Outputs ---" + echo "any_changed: ${{ steps.changed-files.outputs.any_changed }}" + echo "all_changed_files: ${{ steps.changed-files.outputs.all_changed_files }}" + echo "added_files: ${{ steps.changed-files.outputs.added_files }}" + echo "modified_files: ${{ steps.changed-files.outputs.modified_files }}" + echo "deleted_files: ${{ steps.changed-files.outputs.deleted_files }}" + echo "renamed_files: ${{ steps.changed-files.outputs.renamed_files }}" + echo "----------------------------------------" + if [ -n "${{ steps.changed-files.outputs.all_changed_files }}" ]; then + echo "Detected changes: all_changed_files output is NOT empty." + else + echo "No changes detected: all_changed_files output IS empty." + fi + - name: Link Availability Check (Diff Only) + if: ${{ steps.changed-files.outputs.all_changed_files != '' }} + uses: lycheeverse/lychee-action@8646ba30535128ac92d33dfc9133794bfdd9b411 # v2.8.0 + with: + args: --verbose --config .lychee.toml ${{ steps.changed-files.outputs.all_changed_files }} + failIfEmpty: false + fail: true + taplo: + name: taplo + needs: [preflight] + if: needs.preflight.outputs.cargo == 'true' + runs-on: ubuntu-latest + steps: + - name: Code checkout + uses: actions/checkout@v6 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install build dependencies + run: sudo apt-get update && sudo apt-get -yqq install build-essential libssl-dev + - name: Install taplo + run: cargo install taplo-cli --locked + - name: Check formatting + run: taplo fmt --check + audit: + name: audit + needs: [preflight] + if: needs.preflight.outputs.cargo == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions-rust-lang/audit@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + shlint: + name: shlint + needs: [preflight] + if: needs.preflight.outputs.shell == 'true' || needs.preflight.outputs.ci == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Run the shell script checkers + uses: luizm/action-sh-checker@883217215b11c1fabbf00eb1a9a041f62d74c744 # v0.10.0 + env: + SHFMT_OPTS: -i 4 -d + SHELLCHECK_OPTS: -x --source-path scripts + hadolint: + name: hadolint + needs: [preflight] + if: needs.preflight.outputs.dockerfile == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v6 + - name: Lint Dockerfile + uses: hadolint/hadolint-action@2332a7b74a6de0dda2e2221d575162eba76ba5e5 # v3.3.0 + with: + dockerfile: ./resources/Dockerfile + format: tty + no-fail: false + verbose: true + failure-threshold: info + reuse: + name: reuse + needs: [preflight] + if: needs.preflight.outputs.full == 'true' || needs.preflight.outputs.cargo == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: REUSE Compliance Check + uses: fsfe/reuse-action@v6 + formatting: + name: formatting + needs: [preflight] + if: needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + strategy: + matrix: + rust: [nightly] + target: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-musl + env: + RUSTFLAGS: -D warnings + steps: + - name: Code checkout + uses: actions/checkout@v6 + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + components: rustfmt + - name: Formatting (rustfmt) + run: cargo fmt --all -- --check + - name: Formatting (fuzz) (rustfmt) + run: cargo fmt --all --manifest-path fuzz/Cargo.toml -- --check + package-consistency: + name: package-consistency + needs: [preflight] + if: needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install dependencies + run: sudo apt install -y python3 + - name: Install Rust toolchain stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + - name: Check Rust VMM Package Consistency of root Workspace + run: python3 scripts/package-consistency-check.py github.com/rust-vmm + - name: Check Rust VMM Package Consistency of fuzz Workspace + run: | + set -eufo pipefail + pushd fuzz + python3 ../scripts/package-consistency-check.py github.com/rust-vmm + popd + fuzz-build: + name: fuzz-build + needs: [preflight] + if: needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + strategy: + matrix: + rust: [nightly] + target: [x86_64-unknown-linux-gnu] + env: + RUSTFLAGS: -D warnings + steps: + - name: Code checkout + uses: actions/checkout@v6 + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + - name: Install Cargo fuzz + run: cargo install cargo-fuzz + - name: Fuzz Build + run: cargo fuzz build + - name: Fuzz Check + run: cargo fuzz check + openapi: + name: openapi + needs: [preflight] + if: needs.preflight.outputs.openapi == 'true' + runs-on: ubuntu-latest + container: openapitools/openapi-generator-cli + steps: + - uses: actions/checkout@v6 + - name: Validate OpenAPI + run: | + /usr/local/bin/docker-entrypoint.sh validate -i vmm/src/api/openapi/cloud-hypervisor.yaml + typos: + name: typos + needs: [preflight] + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: crate-ci/typos@cf5f1c29a8ac336af8568821ec41919923b05a83 # v1.45.1 + quality: + name: quality + needs: [preflight] + if: needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + # Beta clippy is non-blocking; continue-on-error below keeps the + # aggregated needs.quality.result green when only beta fails. + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + rust: + - beta + - stable + target: + - aarch64-unknown-linux-gnu + - aarch64-unknown-linux-musl + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + include: + - rust: beta + experimental: true + - rust: stable + experimental: false + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + components: clippy + - name: Bisectability Check (default features) + if: ${{ github.event_name == 'pull_request' && matrix.target == 'x86_64-unknown-linux-gnu' }} + run: | + set -eufo pipefail + commits=$(git rev-list origin/${{ github.base_ref }}..${{ github.sha }}) + for commit in $commits; do git checkout $commit; cargo check --tests --examples --all --target=${{ matrix.target }}; done + git checkout ${{ github.sha }} + - name: Clippy (kvm) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "kvm" -- -D warnings + - name: Clippy (mshv) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv" -- -D warnings + - name: Clippy (mshv + kvm) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "mshv,kvm" -- -D warnings + - name: Clippy (default features) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples -- -D warnings + - name: Clippy (default features + guest_debug) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples --features "guest_debug" -- -D warnings + - name: Clippy (default features + pvmemcontrol) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples --features "pvmemcontrol" -- -D warnings + - name: Clippy (default features + tracing) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples --features "tracing" -- -D warnings + - name: Clippy (default features + fw_cfg) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --target=${{ matrix.target }} --locked --all --all-targets --tests --examples --features "fw_cfg" -- -D warnings + - name: Clippy (default features + ivshmem) + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples --features "ivshmem" -- -D warnings + - name: Clippy (sev_snp) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "sev_snp" -- -D warnings + - name: Clippy (igvm) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "igvm" -- -D warnings + - name: Clippy (kvm + tdx) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "tdx,kvm" -- -D warnings + - name: Clippy (kvm + igvm + sev_snp + fw_cfg) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + cross-version: 3e0957637b49b1bbced23ad909170650c5b70635 + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --no-default-features --tests --examples --features "kvm,igvm,sev_snp,fw_cfg" -- -D warnings + - name: Clippy (default features + sev_snp + igvm + fw_cfg) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + uses: houseabsolute/actions-rust-cross@v1 + with: + command: clippy + cross-version: 3e0957637b49b1bbced23ad909170650c5b70635 + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + args: --locked --all --all-targets --tests --examples --features "sev_snp,igvm,fw_cfg" -- -D warnings + - name: Check build did not modify any files + run: test -z "$(git status --porcelain)" + build: + name: build + needs: [preflight] + if: needs.preflight.outputs.full == 'true' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust: + - stable + - beta + - nightly + - "1.89.0" # MSRV — keep quoted. + target: + - x86_64-unknown-linux-gnu + - x86_64-unknown-linux-musl + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install musl-gcc + run: sudo apt install -y musl-tools + - name: Install Rust toolchain (${{ matrix.rust }}) + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + - name: Build (default features) + run: cargo build --locked --bin cloud-hypervisor + - name: Build (kvm) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "kvm" + - name: Build (default features + tdx) + run: cargo build --locked --bin cloud-hypervisor --features "tdx" + - name: Build (default features + dbus_api) + run: cargo build --locked --bin cloud-hypervisor --features "dbus_api" + - name: Build (default features + guest_debug) + run: cargo build --locked --bin cloud-hypervisor --features "guest_debug" + - name: Build (default features + pvmemcontrol) + run: cargo build --locked --bin cloud-hypervisor --features "pvmemcontrol" + - name: Build (default features + fw_cfg) + run: cargo build --locked --bin cloud-hypervisor --features "fw_cfg" + - name: Build (default features + ivshmem) + run: cargo build --locked --bin cloud-hypervisor --features "ivshmem" + - name: Build (mshv) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv" + - name: Build (sev_snp) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "sev_snp" + - name: Build (kvm + igvm + sev_snp + fw_cfg) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "kvm,igvm,sev_snp,fw_cfg" + - name: Build (igvm) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "igvm" + - name: Build (mshv + kvm) + run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv,kvm" + - name: Release Build (default features) + run: cargo build --locked --all --release --target=${{ matrix.target }} + - name: Check build did not modify any files + run: test -z "$(git status --porcelain)" + # garm-jammy + gnu: runs on PR and MQ. Other 3 matrix entries are in + # integration-x86-64-mq (sibling, MQ-only, runs in parallel). + integration-x86-64-pr: + name: integration-x86-64-pr + needs: [preflight, dco, quality, build] + if: >- + needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + timeout-minutes: 80 + env: + # Our runner has 16 cores (nproc). + # We limit parallelism only to avoid exhausting disk space and memory + # resources, not to save CPU resources. + PARALLEL_INTEGRATION_TESTS_NUM: 12 + runs-on: garm-jammy-16 + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install Docker + run: | + set -eufo pipefail + sudo apt-get update + sudo apt-get -y install ca-certificates curl gnupg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt install -y docker-ce docker-ce-cli + - name: Prepare for VDPA + run: scripts/prepare_vdpa.sh + - name: Run unit tests + run: scripts/dev_cli.sh tests --unit --libc gnu + - name: Load openvswitch module + run: sudo modprobe openvswitch + - name: Run integration tests + timeout-minutes: 60 + run: scripts/dev_cli.sh tests --integration --libc gnu + # MQ-only: the 3 matrix entries that integration-x86-64-pr does not cover. + integration-x86-64-mq: + name: integration-x86-64-mq + needs: [preflight, dco, quality, build] + if: >- + github.event_name == 'merge_group' && needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + timeout-minutes: 80 + env: + # Our runner has 16 cores (nproc). + # We limit parallelism only to avoid exhausting disk space and memory + # resources, not to save CPU resources. + PARALLEL_INTEGRATION_TESTS_NUM: 12 + strategy: + fail-fast: false + matrix: + include: + - {runner: garm-jammy, libc: musl} + - {runner: garm-jammy-amd, libc: gnu} + - {runner: garm-jammy-amd, libc: musl} + # format() because `${{ matrix.runner }}-16` is not valid in runs-on. + runs-on: ${{ format('{0}-16', matrix.runner) }} + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install Docker + run: | + set -eufo pipefail + sudo apt-get update + sudo apt-get -y install ca-certificates curl gnupg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt install -y docker-ce docker-ce-cli + - name: Prepare for VDPA + run: scripts/prepare_vdpa.sh + - name: Run unit tests + run: scripts/dev_cli.sh tests --unit --libc ${{ matrix.libc }} + - name: Load openvswitch module + run: sudo modprobe openvswitch + - name: Run integration tests + timeout-minutes: 60 + run: scripts/dev_cli.sh tests --integration --libc ${{ matrix.libc }} + integration-arm64: + name: integration-arm64 + needs: [preflight, dco, quality, build] + if: >- + github.event_name == 'merge_group' && needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + timeout-minutes: 120 + env: + # Our runner has 80 cores (nproc). + # We limit parallelism only to avoid exhausting disk space and memory + # resources, not to save CPU resources. + PARALLEL_INTEGRATION_TESTS_NUM: 25 + runs-on: bookworm-arm64 + steps: + # arm64 runner user is "runner" (vfio's is "github-runner"). + - name: Fix workspace permissions + run: sudo chown -R runner:runner ${GITHUB_WORKSPACE} + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Run unit tests (musl) + run: scripts/dev_cli.sh tests --unit --libc musl + - name: Load openvswitch module + run: sudo modprobe openvswitch + - name: Run integration tests (musl) + timeout-minutes: 60 + run: scripts/dev_cli.sh tests --integration --libc musl + - name: Install Azure CLI + run: | + set -eufo pipefail + sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg + curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null + echo "deb [arch=arm64] https://packages.microsoft.com/repos/azure-cli/ bookworm main" | sudo tee /etc/apt/sources.list.d/azure-cli.list + sudo apt update + sudo apt install -y azure-cli + - name: Download Windows image + shell: bash + run: | + set -eufo pipefail + IMG_BASENAME=windows-11-iot-enterprise-aarch64.raw + IMG_PATH=$HOME/workloads/$IMG_BASENAME + IMG_GZ_PATH=$HOME/workloads/$IMG_BASENAME.gz + IMG_GZ_BLOB_NAME=windows-11-iot-enterprise-aarch64-9-min.raw.gz + cp "scripts/$IMG_BASENAME.sha1" "$HOME/workloads/" + pushd "$HOME/workloads" + if sha1sum "$IMG_BASENAME.sha1" --check; then + exit + fi + popd + mkdir -p "$HOME/workloads" + az storage blob download --container-name private-images --file "$IMG_GZ_PATH" --name "$IMG_GZ_BLOB_NAME" --connection-string "${{ secrets.CH_PRIVATE_IMAGES }}" + gzip -d "$IMG_GZ_PATH" + - name: Run Windows guest integration tests + timeout-minutes: 30 + run: scripts/dev_cli.sh tests --integration-windows --libc musl + integration-vfio: + name: integration-vfio + needs: [preflight, dco, quality, build] + if: >- + github.event_name == 'merge_group' && needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + runs-on: vfio-nvidia + env: + AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }} + steps: + # vfio-nvidia runner user is "github-runner" (not "runner" like arm64). + - name: Fix workspace permissions + run: sudo chown -R github-runner:github-runner "${GITHUB_WORKSPACE}" + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Run VFIO integration tests + timeout-minutes: 15 + run: scripts/dev_cli.sh tests --integration-vfio + # Most tests are failing with musl, see #6790 + # - name: Run VFIO integration tests for musl + # timeout-minutes: 15 + # run: scripts/dev_cli.sh tests --integration-vfio --libc musl + integration-windows: + name: integration-windows + needs: [preflight, dco, quality, build] + if: >- + github.event_name == 'merge_group' && needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + runs-on: garm-jammy-16 + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Install Docker + run: | + set -eufo pipefail + sudo apt-get update + sudo apt-get -y install ca-certificates curl gnupg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + sudo apt-get update + sudo apt install -y docker-ce docker-ce-cli + - name: Install Azure CLI + run: | + set -eufo pipefail + sudo apt install -y ca-certificates curl apt-transport-https lsb-release gnupg + curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null + echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ jammy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list + sudo apt update + sudo apt install -y azure-cli + - name: Download Windows image + run: | + set -eufo pipefail + mkdir $HOME/workloads + az storage blob download --container-name private-images --file "$HOME/workloads/windows-server-2025-amd64-1.raw" --name windows-server-2025-amd64-1.raw --connection-string "${{ secrets.CH_PRIVATE_IMAGES }}" + - name: Run Windows guest integration tests + timeout-minutes: 15 + run: scripts/dev_cli.sh tests --integration-windows + - name: Run Windows guest integration tests for musl + timeout-minutes: 15 + run: scripts/dev_cli.sh tests --integration-windows --libc musl + integration-rate-limiter: + name: integration-rate-limiter + needs: [preflight, dco, quality, build] + if: >- + github.event_name == 'merge_group' && needs.preflight.outputs.full == 'true' && needs.dco.result == 'success' && needs.quality.result == 'success' && needs.build.result == 'success' + runs-on: bare-metal-9950x + env: + AUTH_DOWNLOAD_TOKEN: ${{ secrets.AUTH_DOWNLOAD_TOKEN }} + steps: + - name: Code checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Run rate-limiter integration tests + timeout-minutes: 20 + run: scripts/dev_cli.sh tests --integration-rate-limiter + # The single required-status check. Branch protection requires this one job. + all-green: + name: all-green + needs: + - audit + - build + - dco + - formatting + - fuzz-build + - gitlint + - hadolint + - integration-arm64 + - integration-vfio + - integration-windows + - integration-x86-64-mq + - integration-x86-64-pr + - lychee + - openapi + - package-consistency + - preflight + - quality + - reuse + - shlint + - taplo + - typos + if: always() + runs-on: ubuntu-latest + steps: + - name: Verify all dependencies succeeded or were skipped + env: + NEEDS_JSON: ${{ toJson(needs) }} + run: | + set -eufo pipefail + echo "$NEEDS_JSON" | jq . + # success or skipped = pass; failure or cancelled = red. + echo "$NEEDS_JSON" | jq -e ' + to_entries + | map(select(.value.result != "success" and .value.result != "skipped")) + | length == 0 + ' >/dev/null