mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
Add a GitHub Actions job that runs the virtio-villain suite against cloud-hypervisor, giving continuous coverage of how the virtio device model responds to guest input that violates the driver side rules of the virtio specification. The suite drives the device model from the guest side with out of spec virtqueue input, malformed descriptor chains, transport register abuse, and device specific requests, then checks that each violation is handled without crashing the device or leaving it wedged. This makes a class of guest triggered failures a signal that shows up on every relevant change rather than found by chance. The job builds cloud-hypervisor with the kvm feature, clones virtio-villain at a pinned tag, builds its initramfs, and runs the tests in short lived VMs, rerunning a wedged batch in isolation so a failure is attributed to the offending test. Results reach the run summary page, and the JUnit report plus per test logs upload as an artifact. The compiled harness, the initramfs, and the fetched guest kernel are cached under the resolved villain commit, so an unchanged pin skips the rebuild. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com> Assisted-by: Claude:Opus-4.8 Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
969 lines
39 KiB
YAML
969 lines
39 KiB
YAML
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@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- id: changes
|
|
uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
|
|
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@v7
|
|
- 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@v7
|
|
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@v7
|
|
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@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
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@v7
|
|
- 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@v7
|
|
- 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@v7
|
|
- uses: crate-ci/typos@bee27e3a4fd1ea2111cf90ab89cd076c870fce14 # v1.48.0
|
|
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@v7
|
|
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 (kvm + 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 "kvm,sev_snp" -- -D warnings
|
|
- name: Clippy (mshv + 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 "mshv,sev_snp" -- -D warnings
|
|
- name: Clippy (mshv + igvm + 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 "mshv,igvm,sev_snp" -- -D warnings
|
|
- name: Clippy (kvm + 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 "kvm,igvm" -- -D warnings
|
|
- name: Clippy (mshv + 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 "mshv,igvm" -- -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@v7
|
|
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 + 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 (mshv + igvm)
|
|
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv,igvm"
|
|
- name: Build (mshv + sev_snp)
|
|
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv,sev_snp"
|
|
- name: Build (mshv + igvm + sev_snp)
|
|
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "mshv,igvm,sev_snp"
|
|
- name: Build (kvm + sev_snp)
|
|
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "kvm,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 (kvm + igvm)
|
|
run: cargo build --locked --bin cloud-hypervisor --no-default-features --features "kvm,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)"
|
|
build-riscv64:
|
|
name: build-riscv64
|
|
needs: [preflight]
|
|
if: needs.preflight.outputs.full == 'true'
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
rust:
|
|
- stable
|
|
- "1.89.0" # MSRV — keep quoted.
|
|
env:
|
|
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
|
|
steps:
|
|
- name: Code checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Install riscv64 cross linker
|
|
run: sudo apt-get update && sudo apt-get install -y gcc-riscv64-linux-gnu
|
|
- name: Install Rust toolchain (${{ matrix.rust }})
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
toolchain: ${{ matrix.rust }}
|
|
target: riscv64gc-unknown-linux-gnu
|
|
- name: Build (kvm)
|
|
run: cargo build --locked --package cloud-hypervisor --no-default-features --features "kvm" --target riscv64gc-unknown-linux-gnu
|
|
- 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@v7
|
|
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@v7
|
|
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@v7
|
|
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-25h2-6.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"
|
|
rm -f "$IMG_PATH" "$IMG_GZ_PATH"
|
|
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@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Run VFIO integration tests
|
|
timeout-minutes: 25
|
|
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: 25
|
|
# 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@v7
|
|
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-mshv-x86-64:
|
|
name: integration-mshv-x86-64
|
|
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: 35
|
|
runs-on: mshv
|
|
steps:
|
|
# mshv runner user is "lsgunner"
|
|
- name: Fix workspace and Docker socket permissions
|
|
run: |
|
|
sudo chown -R lsgrunner:lsgrunner ${GITHUB_WORKSPACE}
|
|
sudo chmod 666 /var/run/docker.sock
|
|
- name: Code checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Prepare for VDPA
|
|
run: scripts/prepare_vdpa.sh
|
|
- name: Run integration tests
|
|
timeout-minutes: 30
|
|
run: scripts/dev_cli.sh tests --integration
|
|
# Rate-limiter host is not available
|
|
# 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@v7
|
|
# with:
|
|
# fetch-depth: 0
|
|
# - name: Run rate-limiter integration tests
|
|
# timeout-minutes: 20
|
|
# run: scripts/dev_cli.sh tests --integration-rate-limiter
|
|
integration-sev-snp:
|
|
name: integration-sev-snp
|
|
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: 30
|
|
runs-on: noble-sevsnp
|
|
steps:
|
|
# Self-hosted runners reuse their workdir; a previous privileged
|
|
# container run can leave root-owned files behind.
|
|
- name: Fix workspace permissions
|
|
run: sudo chown -R "$(id -un):$(id -gn)" "${GITHUB_WORKSPACE}"
|
|
- name: Code checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Sanity-check SEV-SNP prerequisites
|
|
run: |
|
|
set -eufo pipefail
|
|
echo "Checking hypervisor device nodes..."
|
|
test -e /dev/kvm || { echo "::error::/dev/kvm missing"; exit 1; }
|
|
test -e /dev/sev || { echo "::error::/dev/sev missing"; exit 1; }
|
|
echo "Checking staged IGVM/kernel artifacts..."
|
|
test -d /usr/share/cloud-hypervisor/cvm \
|
|
|| { echo "::error::/usr/share/cloud-hypervisor/cvm missing"; exit 1; }
|
|
ls -l /usr/share/cloud-hypervisor/cvm
|
|
- name: Run CVM (SEV-SNP) integration tests
|
|
timeout-minutes: 20
|
|
run: scripts/dev_cli.sh tests --integration-cvm --hypervisor kvm
|
|
# Rate-limiter host is not available
|
|
# 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@v7
|
|
# with:
|
|
# fetch-depth: 0
|
|
# - name: Run rate-limiter integration tests
|
|
# timeout-minutes: 20
|
|
# run: scripts/dev_cli.sh tests --integration-rate-limiter
|
|
virtio-villain:
|
|
name: virtio-villain
|
|
needs: [preflight, dco, quality, build]
|
|
if: needs.preflight.outputs.full == 'true'
|
|
timeout-minutes: 60
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
VILLAIN_REPO: https://github.com/weltling/virtio-villain.git
|
|
VILLAIN_REF: v0.5.0
|
|
steps:
|
|
- name: Code checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- name: Verify KVM is available
|
|
run: |
|
|
set -eufo pipefail
|
|
test -e /dev/kvm || { echo "::error::/dev/kvm missing on runner"; exit 1; }
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@stable
|
|
- name: Install dependencies
|
|
run: |
|
|
set -eufo pipefail
|
|
sudo apt-get update
|
|
sudo apt-get install -y musl-tools cpio gzip python3
|
|
sudo apt-get install -y virtiofsd || true
|
|
- name: Build cloud-hypervisor (kvm)
|
|
run: cargo build --locked --release --bin cloud-hypervisor --no-default-features --features kvm
|
|
- name: Clone virtio-villain
|
|
id: villain-src
|
|
run: |
|
|
set -eufo pipefail
|
|
git clone "$VILLAIN_REPO" virtio-villain
|
|
git -C virtio-villain checkout "$VILLAIN_REF"
|
|
echo "sha=$(git -C virtio-villain rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
|
- name: Cache virtio-villain build
|
|
id: villain-cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: virtio-villain/target
|
|
key: virtio-villain-${{ runner.os }}-${{ runner.arch }}-${{ steps.villain-src.outputs.sha }}
|
|
- name: Build virtio-villain initramfs
|
|
if: steps.villain-cache.outputs.cache-hit != 'true'
|
|
run: make -C virtio-villain -j"$(nproc)" initramfs
|
|
- name: Run virtio-villain suite
|
|
working-directory: virtio-villain
|
|
run: |
|
|
set -eufo pipefail
|
|
mkdir -p villain-logs
|
|
sudo ./run \
|
|
--vmm "${GITHUB_WORKSPACE}/target/release/cloud-hypervisor" \
|
|
--blk-queues 2 --net-queues 2 --cpus 2 --memory 256M \
|
|
--order=fast \
|
|
--jobs 4 --batch 10 --timeout 45 --log-dir villain-logs \
|
|
--format junit --output villain-logs/results.xml \
|
|
| tee villain-logs/run.out
|
|
- name: Publish results to run summary
|
|
if: always()
|
|
working-directory: virtio-villain
|
|
run: |
|
|
set -eufo pipefail
|
|
{
|
|
echo '## virtio-villain'
|
|
echo '```'
|
|
if [ -f villain-logs/run.out ]; then
|
|
sed -n '/tests passed/,$p' villain-logs/run.out
|
|
else
|
|
echo 'no results (suite did not produce output)'
|
|
fi
|
|
echo '```'
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
- name: Upload virtio-villain logs
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: virtio-villain-logs
|
|
path: virtio-villain/villain-logs
|
|
if-no-files-found: ignore
|
|
# The single required-status check. Branch protection requires this one job.
|
|
all-green:
|
|
name: all-green
|
|
needs:
|
|
- audit
|
|
- build
|
|
- build-riscv64
|
|
- dco
|
|
- formatting
|
|
- fuzz-build
|
|
- gitlint
|
|
- hadolint
|
|
- integration-arm64
|
|
- integration-sev-snp
|
|
- integration-vfio
|
|
- integration-mshv-x86-64
|
|
- integration-windows
|
|
- integration-x86-64-mq
|
|
- integration-x86-64-pr
|
|
- 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
|