From 605a658b1b1551394233d73bb1b8381827783c8f Mon Sep 17 00:00:00 2001 From: ArtemBo Date: Fri, 22 May 2026 15:31:34 -0500 Subject: [PATCH 1/5] build: test kvm ci runner --- .github/workflows/rust-ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index d98ad63c6..2f9d1eb27 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -2,7 +2,7 @@ name: Rust CI on: push: - branches: [main] + branches: [build/kvm-ci-runner, main] # Documentation-only edits do not affect the Rust artifact graph; skipping # avoids spinning ~30min nextest lanes and cancelling in-flight runs via # extra pushes (see paths-ignore semantics for bundled PR diffs). @@ -43,7 +43,7 @@ env: jobs: changes: name: Detect sandbox lane changes - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 outputs: sandbox_changed: ${{ steps.filter.outputs.sandbox }} steps: @@ -66,7 +66,7 @@ jobs: doctor: name: Workspace integrity (doctor) - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 needs: changes steps: - uses: actions/checkout@v4 @@ -104,7 +104,7 @@ jobs: nextest: name: Nextest (workspace) - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 needs: changes steps: - uses: actions/checkout@v4 @@ -312,7 +312,7 @@ jobs: sandbox-adapter: name: Sandbox adapter (stdio E2E + protocol) - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 needs: [changes, nextest] if: needs.changes.outputs.sandbox_changed == 'true' steps: @@ -386,7 +386,7 @@ jobs: sandbox-adapter-image: name: Sandbox adapter image (no-publish) - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 needs: [changes, nextest] if: needs.changes.outputs.sandbox_changed == 'true' env: @@ -575,7 +575,7 @@ jobs: sandbox-adapter-image-handoff: name: Sandbox image metadata handoff validation - runs-on: arc-runner-set + runs-on: agentium-os-ci-1 needs: [changes, nextest, sandbox-adapter-image] if: needs.changes.outputs.sandbox_changed == 'true' steps: From 0d167ced09e4099944e84afa6adeec177f265e11 Mon Sep 17 00:00:00 2001 From: ArtemBo Date: Fri, 22 May 2026 15:34:03 -0500 Subject: [PATCH 2/5] build: test kvm ci runner --- .github/workflows/rust-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 2f9d1eb27..360917557 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -2,7 +2,7 @@ name: Rust CI on: push: - branches: [build/kvm-ci-runner, main] + branches: [build/kvm-ci-runner] # Documentation-only edits do not affect the Rust artifact graph; skipping # avoids spinning ~30min nextest lanes and cancelling in-flight runs via # extra pushes (see paths-ignore semantics for bundled PR diffs). From 1dd05a2b5c0b9120ecbbac834cdcf66bb8a3557e Mon Sep 17 00:00:00 2001 From: ArtemBo Date: Fri, 22 May 2026 15:35:53 -0500 Subject: [PATCH 3/5] build: test kvm ci runner --- .github/workflows/rust-ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 360917557..bdadbb164 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -43,7 +43,7 @@ env: jobs: changes: name: Detect sandbox lane changes - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp outputs: sandbox_changed: ${{ steps.filter.outputs.sandbox }} steps: @@ -66,7 +66,7 @@ jobs: doctor: name: Workspace integrity (doctor) - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp needs: changes steps: - uses: actions/checkout@v4 @@ -104,7 +104,7 @@ jobs: nextest: name: Nextest (workspace) - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp needs: changes steps: - uses: actions/checkout@v4 @@ -312,7 +312,7 @@ jobs: sandbox-adapter: name: Sandbox adapter (stdio E2E + protocol) - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp needs: [changes, nextest] if: needs.changes.outputs.sandbox_changed == 'true' steps: @@ -386,7 +386,7 @@ jobs: sandbox-adapter-image: name: Sandbox adapter image (no-publish) - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp needs: [changes, nextest] if: needs.changes.outputs.sandbox_changed == 'true' env: @@ -575,7 +575,7 @@ jobs: sandbox-adapter-image-handoff: name: Sandbox image metadata handoff validation - runs-on: agentium-os-ci-1 + runs-on: kvm-gcp needs: [changes, nextest, sandbox-adapter-image] if: needs.changes.outputs.sandbox_changed == 'true' steps: From e5e72c5b14560507cc83c5909d2fc3d109eb0ced Mon Sep 17 00:00:00 2001 From: ArtemBo Date: Fri, 29 May 2026 13:10:22 -0500 Subject: [PATCH 4/5] build: test kvm ci runner --- .github/workflows/rust-ci.yml | 11 +++++++++++ .github/workflows/sandbox-e2e-kvm.yml | 11 +++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index bdadbb164..e9ab3f434 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -41,9 +41,20 @@ env: OTEL_EXPORTER_OTLP_ENDPOINT: "" jobs: + # Wakes the self-hosted GCP runner (auto-stops after idle). All kvm-gcp + # jobs gate on this transitively via `changes`. + # id-token: write is required for the reusable workflow's WIF auth; it + # has to be granted at the caller side too (intersection semantics). + start-runner: + permissions: + id-token: write + contents: read + uses: ./.github/workflows/start-kvm-runner.yml + changes: name: Detect sandbox lane changes runs-on: kvm-gcp + needs: start-runner outputs: sandbox_changed: ${{ steps.filter.outputs.sandbox }} steps: diff --git a/.github/workflows/sandbox-e2e-kvm.yml b/.github/workflows/sandbox-e2e-kvm.yml index 9dcbecf66..df4b2db01 100644 --- a/.github/workflows/sandbox-e2e-kvm.yml +++ b/.github/workflows/sandbox-e2e-kvm.yml @@ -18,9 +18,16 @@ env: ROOTFS_TAR: sandbox-echo-rootfs.tar jobs: + start-runner: + permissions: + id-token: write + contents: read + uses: ./.github/workflows/start-kvm-runner.yml + prepare-fixture: name: Prepare local sandbox fixture bind rootfs artifact - runs-on: kvm-enabled + runs-on: kvm-gcp + needs: start-runner steps: - uses: actions/checkout@v4 @@ -127,7 +134,7 @@ jobs: kvm-e2e: name: KVM E2E (baml-rt-tools) needs: prepare-fixture - runs-on: kvm-enabled + runs-on: kvm-gcp steps: - uses: actions/checkout@v4 From 5feb7c7f11077e3cfa90b58d427610d8709cf5dc Mon Sep 17 00:00:00 2001 From: ArtemBo Date: Fri, 29 May 2026 13:12:03 -0500 Subject: [PATCH 5/5] build: test kvm ci runner --- .github/workflows/start-kvm-runner.yml | 85 ++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 .github/workflows/start-kvm-runner.yml diff --git a/.github/workflows/start-kvm-runner.yml b/.github/workflows/start-kvm-runner.yml new file mode 100644 index 000000000..3f2e7f306 --- /dev/null +++ b/.github/workflows/start-kvm-runner.yml @@ -0,0 +1,85 @@ +name: Start KVM Runner + +# Reusable workflow that wakes the self-hosted GCP runner (auto-stopped after +# 20m idle) before jobs that need `runs-on: kvm-gcp`. Authenticates to GCP via +# Workload Identity Federation — no long-lived keys. +# +# Repo-level vars consumed (set via `gh variable set`): +# GCP_WIF_PROVIDER e.g. projects/123456789/locations/global/workloadIdentityPools/github/providers/github-oidc +# GCP_RUNNER_SA e.g. ci-agentium-os-ci-1@agentium-os.iam.gserviceaccount.com + +on: + workflow_call: + inputs: + instance: + description: GCE instance name + type: string + default: agentium-os-ci-1 + zone: + description: GCE zone + type: string + default: us-central1-a + project: + description: GCP project ID + type: string + default: agentium-os + wait_seconds: + description: Seconds to wait after `start` returns, so the actions-runner service has time to register with GitHub. + type: number + default: 60 + workflow_dispatch: + inputs: + instance: + type: string + default: agentium-os-ci-1 + zone: + type: string + default: us-central1-a + project: + type: string + default: agentium-os + +permissions: + id-token: write + contents: read + +# Two concurrent CI runs both call this — the second blocks on the first +# rather than racing the gcloud start API. +concurrency: + group: start-kvm-runner-${{ inputs.instance }} + cancel-in-progress: false + +jobs: + start: + name: Start ${{ inputs.instance }} + runs-on: ubuntu-latest + steps: + - name: Authenticate to GCP + uses: google-github-actions/auth@v2 + with: + workload_identity_provider: ${{ vars.GCP_WIF_PROVIDER }} + service_account: ${{ vars.GCP_RUNNER_SA }} + + - name: Set up gcloud + uses: google-github-actions/setup-gcloud@v2 + + - name: Start instance if not running + run: | + set -euo pipefail + status=$(gcloud compute instances describe "${{ inputs.instance }}" \ + --zone="${{ inputs.zone }}" --project="${{ inputs.project }}" \ + --format='value(status)') + echo "current status: $status" + if [ "$status" = "RUNNING" ]; then + echo "instance already running, nothing to do" + exit 0 + fi + gcloud compute instances start "${{ inputs.instance }}" \ + --zone="${{ inputs.zone }}" --project="${{ inputs.project }}" + + - name: Wait for actions-runner to register + # Cold-boot + idempotent startup-script + runner service ≈ 60s on + # resume. Listing self-hosted runners would need admin perms on the + # repo; instead we sleep and let GitHub queue dependent jobs until + # the runner re-registers itself. + run: sleep ${{ inputs.wait_seconds }}