8000 feature: CI refactor — clear build/test phases, three gating targets · Issue #687 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feature: CI refactor — clear build/test phases, three gating targets #687

Closed

Description

@norrietaylor

Summary

Comprehensive CI refactor for gominimal/minimal to eliminate redundant rebuilds, separate build from test phases across lanes, and gate merges on all three production platforms (macOS/HVF, Linux native minimald, Linux/KVM). The work is sequenced as 10 independently landable PRs (PR0–PR9) plus an admin ruleset flip.

Problem

The current CI has structural problems: test jobs rebuild artifacts instead of consuming pre-built ones; monolithic jobs mix setup, build, and e2e; shared steps are duplicated across workflows and release.yml copies rather than reuses CI logic; only lint and unit tests gate merges — no VM boot blocks a bad merge; the ci-docs-skip.yml inverse-path hack creates wedge and silent-ungate failure modes; and the Mac mini's time is spent on unit/build work that could run on GH-hosted runners.

Desired outcome

  • One build per platform per run; test jobs consume pre-built artifacts
  • Every lane reads as clear build → test phases; no monolithic jobs
  • Shared steps (Rust setup, libkrun, guest artifacts, linkage rewrite) in one composite/script; release.yml reuses CI's setup
  • Three required gating targets: ci-macos-success, ci-linux-native-success, ci-linux-kvm-success
  • Robust required-check mechanics via changes-filter jobs + if: always() aggregators; ci-docs-skip.yml deleted
  • Coverage gaps closed: native minimald daemon e2e, crates/minimald/** triggering the mac lane, release artifacts smoke-tested before staging
  • Mac mini handles only hypervisor e2e; unit/build moves to GH-hosted runners
  • Dev scaffolding removed: boop timeouts, split arm64 builds, dead conditionals, redundant verify workflows

Scope notes

Work is sequenced: PR0 (done) → PR1 (in review) → PR2–PR9 + Admin ruleset flip; each PR soaks before the ruleset flip makes lanes required. Key files: .github/workflows/ci.yml, ci-macos.yml, ci-linux-kvm.yml, ci-shell-installer.yml, ci-docs-skip.yml (delete), ci-netns.yml (delete), ci-gvproxy.yml (delete), release.yml, and new ci-linux-native.yml. Networking test surface stays mothballed by decision; arm64-Linux binaries remain untested by decision.


This issue is the tracking issue for this work. A maintainer with write
access starts the SDD pipeline by commenting /spec on this issue or by
flipping the sdd:spec label.

Steer the pipeline with these comment commands. Each is gated to commenters
with write access to the repository.

Command Where Effect
/spec this tracking issue re-run sdd-spec to draft or revise the spec
/fastpath this tracking issue confirm a single-session change; sdd-spec produces a stub spec PR and an execution plan comment in one run (ADR 0012)
/triage this tracking issue, after the spec PR is merged start sdd-triage phase A (architecture)
/approve this tracking issue full path: confirm the proposed plan so sdd-triage creates the Unit and task sub-issues. Fast path: dispatch the execution plan against sdd-execute-{tier}
/dispatch this tracking issue (full path only) arm the cascade across the task tree
/revise <note> a spec, architecture, or implementation PR, or this tracking issue re-run the owning agent with the note as an added instruction
/execute a task sub-issue run sdd-execute for that task ahead of the schedule

Merging the spec PR advances to the architecture phase; merging the
architecture PR advances to a plan-comment phase, where sdd-triage posts
the proposed plan as one comment on this tracking issue. When an agent needs
a human decision it applies the needs-human label and posts one comment;
clear the label once you have answered and the agent resumes.


Routed from #687 by inbox-route (https://github.com/gominimal/inbox/actions/runs/29043264519).
Routing-Key: inbox-route/I_kwDOSUhdos8AAAABIQgrMA

Original request

Tracking issue for the CI refactor of gominimal/minimal. Work is sequenced as independently landable PRs; each soaks before the ruleset flip makes lanes required.

Goals

  • One build per platform per run — each binary/artifact compiled once, test jobs consume it; no rebuilds inside test jobs.
  • Clear phase separation — every lane reads as build → test; no monolithic jobs mixing setup, build, and e2e.
  • Zero duplicated definitions — shared steps live in one composite/script (rust setup, libkrun, guest artifacts, linkage rewrite); release.yml reuses CI's, never copies.
  • Three gating targets — macOS/HVF, Linux native minimald, Linux minvmd/KVM all required for merge (today only lint/unit gates; no VM boot blocks a bad merge).
  • Robust required-check mechanics — always-triggered lanes with path-filter jobs + if: always() aggregators; kills the ci-docs-skip.yml inverse-path hack and the wedge/silent-ungate failure modes.
  • Close coverage holes — native minimald daemon e2e (new), crates/minimald/** triggering the mac lane, release artifacts smoke-tested before staging.
  • Test what ships — CI exercises production variants (own trimmed libkrun, @rpath linkage), not dev-only stand-ins.
  • Protect scarce runners — the Mac mini does hypervisor e2e only; unit/build work moves to GH-hosted; flake reruns cost ~15 min, not an hour.
  • Remove dev scaffolding — boop timeouts, split arm64 builds, dead conditionals, redundant verify workflows, mothballed networking proofs.

Ordered subtasks

  • PR0 — mac e2e consolidation + redundant-step removal — done: gominimal/minimal#678
  • PR1 — shared setup composites + linkage-rewrite script extraction (behavior-preserving) — in review: gominimal/minimal#685
  • PR2 — consolidate macOS libkrun on our own trimmed build — vendor/libkrun.lock pin, scripts/build-libkrun-macos.sh, cached via actions/cache (GH-hosted) + versioned local dir (mini); CI boots the exact dylib that ships; brew tap dropped from CI; release builds via the same script. Merged: gominimal/minimal#694. Bonus catch: the e2e's combined cargo invocation had been feature-unifying libkrun into the CLI all along (masked by brew's absolute install name) — now split + guarded by a system-libraries assert.
  • PR3 — mac lane finish — GH-hosted unit job (clippy/test minvmd, sessions, minimal) replaces the mini's build-macos; krun_smoke moves into e2e; per-PR production-linkage check; add crates/minimald/** to paths (path fix landed on main via feat(minvmd,minimald): per-VM writable /dev/vdb ext4 volume #672). Merged: gominimal/minimal#696. Note: the planned cargo test -p minimal on macOS is impossible today — minimal's dev-deps pull minimald, whose ungated hakoniwa→libcgroups→procfs chain is Linux-only; needs a cfg-gating PR if wanted.
  • PR4 — new ci-linux-native.yml lane — workspace tests (networking feature steps mothballed) + new native minimald daemon e2e (now the unified scripts/session-e2e.sh, shared by all target lanes); delete ci-netns.yml; born with changes-filter + aggregator. Merged: gominimal/minimal#697. The e2e drives minimal activate + attach --command (non-interactive exec); E2E_ACTIVATE_ARGS is the drop-in for --loadout dev once feat(sessions): loadouts, config, and debug commands #686 lands.
  • PR5 — KVM lane gate-ready — changes-filter + aggregator; retries dropped entirely by owner decision — the single recorded apt transient (run 28897380911) doesn't justify the machinery; reintroduce only with fresh evidence; soak 1–2 weeks tracking flake rate. In review: gominimal/minimal#698.
  • PR6 — KVM build/test split — MINVMD_BIN env override in e2e harnesses (falls back to CARGO_BIN_EXE_minvmd), build-linux → test-kvm via testbins.json manifest; flake rerun drops from ~60 to ~15 min. Merged with PR5 via gominimal/minimal#698 (gominimal/minimal#699). Proven on the merge run: test-kvm reran in ~1 min on prebuilt harnesses.
  • N1 — pin the Rust toolchain — rust-toolchain.toml @ stable 1.97.0; surfaced + fixed 6 clippy-1.97 lints across 6 crates. Merged: gominimal/minimal#701
  • N2+N3 + KVM session-e2e — folded into one PR — nextest profiles (ci/vm, no retries — own-ip attach intermittently hangs forever on AF_VSOCK missed wakeup #588 leak-wedge), KVM lane on nextest archives (testbins.json + run-testbin.sh deleted), unified session e2e wired into the KVM lane (DM3), daemon lifecycle extracted to scripts/lifecycle-e2e.sh, krun_add_disk3 assert moved into the setup-libkrun-linux composite (covers release.yml). Merged: gominimal/minimal#702. Proven live: session e2e OK on KVM (1653ms cold activate incl. microVM boot), daemon lifecycle OK.
  • N7 — auto-discovery is the default — *_e2e / *_root_e2e naming convention (rename vsock_relay_e2e→vsock_relay_root_e2e, krun_smoke→krun_smoke_e2e), delete retired bridge_e2e (owner-approved), lanes select by convention filtersets instead of hardcoded test lists, meta-test guards the convention. Adding a test never requires a CI edit. Merged: gominimal/minimal#732 — shipped as the honest integration-vs-e2e taxonomy: harnesses renamed *_integration/*_root_integration (not _e2e), lanes select by binary(/_integration$/), crate meta-tests guard the suffix + document when to use an integration harness vs. a full-CLI e2e script; bonus: removed minvmd's dead host-side tap relay (net/relay.rs, no production caller since the relay moved into the guest) and scrubbed workflow YAMLs of internal plan jargon.
  • N4 — cache discipline — rust-cache shared-key per job class + save-if main-only; big raw caches → restore/save split; CARGO_PROFILE_DEV_DEBUG=line-tables-only; --locked sweep. Before PR7. Merged: gominimal/minimal#703
  • PR7 — trigger flip — ci.yml/ci-macos.yml/ci-shell-installer.yml lose trigger-level path filters, gain changes-jobs + aggregators (incl. the new ci-macos-success); delete ci-docs-skip.yml (atomic) and ci-gvproxy.yml; gvproxy pin coverage moves into the KVM filter. Merged: gominimal/minimal#712 — the soak has started.
  • N5 — nightly-tests.yml (merged: gominimal/minimal#720; +installer suite via workflow_call) — advisories watch, session-e2e soak ×10 (hunts the own-ip attach intermittently hangs forever on AF_VSOCK missed wakeup #588 wedge class), beta + cargo-update canaries (continue-on-error), actionlint/zizmor/cargo-machete hygiene, issue-on-failure notify; scheduled runs keep main's caches warm. Drive-by: timeout-minutes for promote.yml's promote job.
  • N8 — CI strategy + agent/developer contract into repo docs (parked on branch ci/docs-contract, unblocks after N7) — docs/ci-strategy.md (incl. §10), CONTRIBUTING.md contract table (test kind → location → local command; never edit .github/workflows/), CLAUDE.md pointer, .github/CODEOWNERS freeze on /.github/, junit + $GITHUB_STEP_SUMMARY proofs in core-tests. Merged: gominimal/minimal#742 — rebuilt fresh off main (the parked ci/docs-contract had drifted): CONTRIBUTING.md contract + docs/ci-strategy.md updated to the shipped _integration taxonomy, CLAUDE.md pointer, nextest JUnit + $GITHUB_STEP_SUMMARY. Dropped the redundant nightly/soak/promote bits (already on main via ci: add the nightly test tier #720) and the CODEOWNERS (superseded by the repo-wide * @gominimal/minimalists from chore: add CODEOWNERS for the minimalists team #728).
  • Admin — ruleset flip — required checks become ci-success, ci-linux-native-success, ci-linux-kvm-success, ci-macos-success, ci-shell-installer-success (after soak; open PRs need a synchronize to pick up new contexts). Same session: enable "require code owner review" so N8's CODEOWNERS freeze on /.github/ bites. Done 2026-07-14 (ruleset 14240442): required checks flipped from ci-success alone to all five lane aggregators (ci-success, ci-linux-native-success, ci-linux-kvm-success, ci-macos-success, ci-shell-installer-success); require_code_owner_review enabled so the repo-wide CODEOWNERS (* @gominimal/minimalists, chore: add CODEOWNERS for the minimalists team #728) gates .github/. Flipped on 1 green soak night (nightly startup bug fixed in fix(ci): grant the nightly installer job pull-requests: read #750) — watch the nightly soak; revert = restore required_status_checks to [ci-success].
  • PR8 — remove ci.yml test job — redundant once the native lane became required. Merged: gominimal/minimal#755. Removed the workspace test job (core-tests + its bespoke mTLS --features networking-proxy step) and dropped test from ci-success.needs; the native lane's required tests job runs the identical suite. The proxy step was dropped, not moved — those tests are feature-gated in minimald and should run wherever the feature is enabled, not via a hand-wired YAML step.
  • PR9 — release.yml overhaul (core) — Merged: gominimal/minimal#757. verify-ci guard replaces the duplicated test job: it queries the merge commit's five required aggregators and refuses to release unless all are green (skip_ci_verify input bypasses with a warning). Added dry_run (build/sign/assemble but publish nothing — no GCS upload, GitHub release, installer-bucket write, or channel advance). arm64 build recollapsed from three sequential cross builds to one cross build --package minimal --package mip --package minimald + single rename, timeout 90→60. --locked on all five release builds. The mac release build stays on the self-hosted runner — the Developer ID signing keychain lives there (gominimal/minimal#680). Validated post-merge via a dry_run + skip_ci_verify dispatch on main (run 29375276363). Deferred from this PR: the canonical-basename seam (aarch64/arm64) was left as-is — not needed for the gate work.
  • PR9b — smoke the shipped artifacts — download the release binaries and prove them: linux-amd64 daemon e2e + KVM boot + skip-tolerant mac installer-layout boot on the mini. Split out of PR9 core — only validatable via a real release dispatch, so it lands after the release path is exercised.
  • N6 — darwin testbed: the mini stops compiling (last) — hosted unit job also archives -p minvmd -p sessions + builds minvmd/minimal, bundles a @loader_path-layout testbed with libkrun.dylib; the mini downloads, codesigns, runs. Podman pattern; collapses the unit/e2e build divergence.

Notes

  • Networking test surface (netns/mesh/proxy/wg proofs) is mothballed by decision — code stays in-tree, not CI-covered; the KVM gvproxy relay e2e stays.
  • arm64-Linux binaries remain untested by decision — gap documented in-workflow.
  • gs://minimal-shim archive upload + legacy dogfood installer stay (migration owned separately).

🤖 Generated with Claude Code

Activity

  1. gominimal-aw-bot commented on Jul 9, 2026

    @gominimal-aw-bot
    Contributor

    Routing triage — #687

    Kind: epic | Route: sdd | Confidence: 0.95 | Target: gominimal/minimal

    This is an epic tracking a phased CI refactor of gominimal/minimal, sequenced as 10 independently landable PRs (PR0–PR9) plus an admin ruleset flip. Two PRs are already done or in review. The work belongs in gominimal/minimal: the issue explicitly links PRs there, and every CI workflow file named in the plan was confirmed present in .github/workflows/ of that repo.

    Route: sdd — the scope spans 10+ workflow files across multiple PRs and requires design judgment (phase ordering, runner allocation, required-check mechanics); too large for patch.

    Launch: a maintainer comments /spec on the work issue to start the SDD pipeline.

    Evidence

    [inbox-route:decision]

    {"schema":"inbox-route/v1","issue":269,"issue_node_id":"I_kwDOSUhdos8AAAABIQgrMA","dry_run":false,"kind":"epic","route":"sdd","confidence":0.95,"targets":[{"repo":"gominimal/minimal","reason":"all workflow files named in the plan exist in .github/workflows/ of this repo; the issue explicitly links PRs there and CI work is owned here"}],"labels":["kind:epic","priority:should-have"],"patch":null,"work_issue":{"title":"feature: CI refactor — clear build/test phases, three gating targets","body":"## Summary\n\nComprehensive CI refactor for gominimal/minimal to eliminate redundant rebuilds, separate build from test phases across lanes, and gate merges on all three production platforms (macOS/HVF, Linux native minimald, Linux/KVM). The work is sequenced as 10 independently landable PRs (PR0–PR9) plus an admin ruleset flip.\n\n## Problem\n\nThe current CI has structural problems: test jobs rebuild artifacts instead of consuming pre-built ones; monolithic jobs mix setup, build, and e2e; shared steps are duplicated across workflows and `release.yml` copies rather than reuses CI logic; only lint and unit tests gate merges — no VM boot blocks a bad merge; the `ci-docs-skip.yml` inverse-path hack creates wedge and silent-ungate failure modes; and the Mac mini's time is spent on unit/build work that could run on GH-hosted runners.\n\n## Desired outcome\n\n- One build per platform per run; test jobs consume pre-built artifacts\n- Every lane reads as clear build → test phases; no monolithic jobs\n- Shared steps (Rust setup, libkrun, guest artifacts, linkage rewrite) in one composite/script; `release.yml` reuses CI's setup\n- Three required gating targets: `ci-macos-success`, `ci-linux-native-success`, `ci-linux-kvm-success`\n- Robust required-check mechanics via changes-filter jobs + `if: always()` aggregators; `ci-docs-skip.yml` deleted\n- Coverage gaps closed: native minimald daemon e2e, `crates/minimald/**` triggering the mac lane, release artifacts smoke-tested before staging\n- Mac mini handles only hypervisor e2e; unit/build moves to GH-hosted runners\n- Dev scaffolding removed: boop timeouts, split arm64 builds, dead conditionals, redundant verify workflows\n\n## Scope notes\n\nWork is sequenced: PR0 (done) → PR1 (in review) → PR2–PR9 + Admin ruleset flip; each PR soaks before the ruleset flip makes lanes required. Key files: `.github/workflows/ci.yml`, `ci-macos.yml`, `ci-linux-kvm.yml`, `ci-shell-installer.yml`, `ci-docs-skip.yml` (delete), `ci-netns.yml` (delete), `ci-gvproxy.yml` (delete), `release.yml`, and new `ci-linux-native.yml`. Networking test surface stays mothballed by decision; arm64-Linux binaries remain untested by decision.\n\n---\n<!-- SDD command vocabulary. Do not edit this footer. -->\n\nThis issue is the tracking issue for this work. A maintainer with write\naccess starts the SDD pipeline by commenting `/spec` on this issue or by\nflipping the `sdd:spec` label.\n\nSteer the pipeline with these comment commands. Each is gated to commenters\nwith write access to the repository.\n\n| Command | Where | Effect |\n|---|---|---|\n| `/spec` | this tracking issue | re-run `sdd-spec` to draft or revise the spec |\n| `/fastpath` | this tracking issue | confirm a single-session change; `sdd-spec` produces a stub spec PR and an execution plan comment in one run (ADR 0012) |\n| `/triage` | this tracking issue, after the spec PR is merged | start `sdd-triage` phase A (architecture) |\n| `/approve` | this tracking issue | full path: confirm the proposed plan so `sdd-triage` creates the Unit and task sub-issues. Fast path: dispatch the execution plan against `sdd-execute-{tier}` |\n| `/dispatch` | this tracking issue (full path only) | arm the cascade across the task tree |\n| `/revise <note>` | a spec, architecture, or implementation PR, or this tracking issue | re-run the owning agent with the note as an added instruction |\n| `/execute` | a task sub-issue | run `sdd-execute` for that task ahead of the schedule |\n\nMerging the spec PR advances to the architecture phase; merging the\narchitecture PR advances to a plan-comment phase, where `sdd-triage` posts\nthe proposed plan as one comment on this tracking issue. When an agent needs\na human decision it applies the `needs-human` label and posts one comment;\nclear the label once you have answered and the agent resumes."},"related":[{"repo":"gominimal/minimal","number":238,"relation":"related"},{"repo":"gominimal/minimal","number":620,"relation":"related"}],"rationale":"The issue is an epic tracking a phased CI refactor of gominimal/minimal. The target is unambiguous: the issue body links directly to gominimal/minimal PRs (#678, #685), and the repo's .github/workflows/ directory contains every file named in the plan (ci.yml, ci-macos.yml, ci-linux-kvm.yml, ci-docs-skip.yml, ci-netns.yml, ci-gvproxy.yml, release.yml). Distillery confirms prior CI evolution in this project: release workflow split (gominimal/minimal#620, merged), macOS self-hosted runner setup (gominimal/minimal#283, merged), libkrun brew provisioning (gominimal/minimal#508, merged). The work spans 10+ workflow files across multiple PRs and requires design judgment (phase ordering, runner allocation, required-check mechanics), making sdd the correct route over patch. No duplicates or prior routing markers found in either inbox or minimal. No routing-outcome precedents in Distillery inbox project."}

    Generated by inbox-route for issue #687 · ◷

  2. added
    kind:epicWork too large for one change; usually routes to work or multi. Set by the classifier.
    on Jul 9, 2026
  3. gominimal-aw-bot commented on Jul 9, 2026

    @gominimal-aw-bot
    Contributor

    [inbox-route:ledger]
    Routed from gominimal/inbox#269 by inbox-route (https://github.com/gominimal/inbox/actions/runs/29043264519)
    Routing-Key: inbox-route/I_kwDOSUhdos8AAAABIQgrMA
    Route: sdd
    Work-Issue: #687 (in-place — the transferred issue is the work item)
    sdd:spec: withheld — a maintainer launches via /spec or a sdd:spec label flip (a bot actor is refused by the target sdd-spec gate; #226)

  4. changed the title [-]CI refactor (minimal): clear build/test phases, three gating targets[/-] [+]feature: CI refactor — clear build/test phases, three gating targets[/+] on Jul 9, 2026
  5. transferred this issue fromon Jul 9, 2026
  6. removed this from the Minimal Open Source milestone on Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:epicWork too large for one change; usually routes to work or multi. Set by the classifier.priority:should-haveShould be done. Applied by the inbox-route classifier or a human.routed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0