Skip to content

feat: build and boot what the tool generates for every base, two a night - #759

Merged
Danathar merged 13 commits into
mainfrom
feat/756-variant-boot-test
Oct 18, 2026
Merged

Danathar merged 13 commits into
mainfrom
feat/756-variant-boot-test

Conversation

@Danathar

Copy link
Copy Markdown
Owner

Nothing checked that the projects the tool generates still build into images that boot, so an upstream change that broke them was first found by a user. This adds a nightly job that generates a project for two bases, builds it, boots it in a VM, and keeps one issue per failing base. All 13 bases are covered each week.

What changed

  • variant_boot_test.py:
    • Generates the project the way "Test build locally" does (seed_project_template, then write_project_files), with tmux and podman.socket as the test package and service.
    • Builds it with podman build --no-cache and boots it with bcvk ephemeral run-ssh.
    • Checks that boot settles, the package is installed, the service is enabled and active, and only allowlisted units failed. Each allowlist entry carries its reason.
    • Retries the boot once. If it still fails, boots the unmodified base the same way, to say whose fault it is, never to pass a failure.
  • variant-boot-test.yml: runs the rotation from Nothing checks that the images the tool generates still build and boot on each supported base #756 on ubuntu-26.04 at 02:41 UTC, plus workflow_dispatch for one base. Issues are opened, commented on or closed only from main.
  • Bookkeeping: the permissions policy, risk tiers (Tier 1), the maintainer table, the runbook, the coverage source, the architecture map, and CONTRIBUTING (bcvk v0.19.0 pin).
    • .github/actionlint.yaml declares ubuntu-26.04, because actionlint v1.7.12 predates that label.

Why it is shaped this way

  • Not watched by auto-issues.yml: that workflow's per-workflow issue would be closed by the next night's unrelated bases. The exemption is explicit in tests/test_auto_issues_workflow.py.
  • Units injected into every boot: desktop bases ship sshd.service disabled, and an ephemeral /var lacks /var/roothome, so bcvk never got SSH. Both are injected into the generated image and the unmodified base alike, so they cannot be the difference between them. For the same reason, sshd is not the test service.
  • --no-cache: podman reused a stale build.sh layer locally, and that booted an image without the test service.

Closes #756

How it was checked

  • python3 -m unittest discover -s tests (2574 tests), ruff check, actionlint, python3 maintenance_audit.py --skip-upstream, git diff --check.
  • Local end to end on a workstation with KVM: run --base sway-atomic passed in 84 s with no cache hits. An earlier stale-cache run exercised the failure path: it failed the service check, re-booted, booted upstream, and blamed the tool.
  • Hosted: running on a scratch branch (test/756-hosted, which adds a push trigger and is not for merge) for bazzite and silverblue. Results follow in a comment.

Nothing checked that the projects the tool writes still build into images
that boot. The bases change upstream daily, so a breakage was first found by
a user (#756).

variant_boot_test.py generates a project the way "Test build locally" does
(seed_project_template, write_project_files) with a fixed test configuration
(tmux, podman.socket), builds it with podman, and boots it with bcvk
ephemeral. The guest reports whether boot settled, whether the package is
installed and the service enabled and active, and which units failed; units
that fail only in an ephemeral VM are allowlisted with their reason. A failed
boot is retried once, then the unmodified base is booted the same way to say
whose failure it is, never to pass one.

variant-boot-test.yml runs two bases a night on ubuntu-26.04 (all 13 a
week; Sunday's second slot builds one base with the BlueBuild method) and
keeps one issue per failing base, closed only when that base passes again.
It is exempt from auto-issues.yml, whose per-workflow issue would be closed
by the next night's unrelated bases.

Two things bcvk needs from a desktop base are injected into every boot,
generated and unmodified alike: /var/roothome before tmpfiles runs, so the
SSH key credential lands, and sshd.service, since the desktops ship it
disabled. Builds use --no-cache: podman keyed the build.sh RUN step on its
command line, not the bind-mounted script, and reused a stale layer.

Closes #756
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-17T16:18:58.884639Z 220e06f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@danathar-atomic-hive danathar-atomic-hive Bot added author/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) stage/090-ready-for-human-merge Hive would merge this but trust/role blocks it; a maintainer's /lgtm /approve is all that is missing labels Oct 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 220e06f43c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/variant-boot-test.yml
Comment thread variant_boot_test.py Outdated
@danathar-atomic-hive

Copy link
Copy Markdown
Contributor
220e06f

⚠️ Sentinel alert — maintainer review required

Hive flagged this PR (author @Danathar, head 220e06f43c95) because it matches behaviors that can override security controls, escalate privileges or damage the codebase. This is a heuristic, not an accusation — a maintainer should confirm the change is intended before it merges.

  • sensitive_path — changes 2 sensitive path(s) (Touches a sensitive path (OWNERS, workflows, policies, hive config, security docs, …))
    • .github/policies/workflow-permissions.json
    • .github/workflows/variant-boot-test.yml
  • remote_code_execution — adds a remote-fetch-and-execute, decoded payload, reverse shell or embedded credential (Adds curl|sh, eval of encoded payloads, reverse shells or embedded private keys)
    • variant_boot_test.py

Hive added the sentinel-alert label. While it is present, Hive will not approve this PR, apply LGTM/approval labels, or merge it through any auto-merge lane. Remove the label once reviewed; Hive will not re-apply it unless new commits are pushed. Tune paths and behaviors under sentinel in hive.yaml or the dashboard Security tab.

@danathar-atomic-hive danathar-atomic-hive Bot added sentinel-alert and removed author/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) labels Oct 17, 2026
…gnature, ship the check script as a file

A slot could run 90 minutes of build and three 15-minute boots, longer than
the job's 120-minute timeout, so the slowest failures were cancelled before
their logs and issue were written. Build and boot limits are now 45 and 12
minutes, and a test holds the job timeout above the worst case.

Two boot failures were blamed on upstream whenever both were 'boot', even
when one never answered SSH and the other panicked. They now have to match.

The guest script went over SSH base64-encoded, which hid what ran. It is now
a file on a read-only virtiofs mount, kept in the run's artifact.
The hosted runs failed before the VM started (virtiofsd exited), and both
slots were labelled upstream because an unmodified base that also never
starts "failed the same way". A boot with no guest journal is now stage
"host": no upstream comparison, and, like a job that died in setup, it goes
to one shared runner issue instead of opening an issue against each base.
Any passing slot closes that issue.

Also: the issue's log carries the guest console beside bcvk's output, run
refuses a --work directory that already holds a project instead of deleting
it, and the mcelog allowlist reason says only what was observed.
…nd run slots one at a time

An empty guest journal alone also fits a guest that died before its journal
reached the host, which would have filed a base's failure under the runner
issue. A failure is now the runner's only when bcvk's output names a
host-side error (virtiofsd, QEMU or the container failing to start) and the
guest wrote nothing; a silent boot stays the base's.

The matrix runs one slot at a time: both slots can write the shared runner
issue, and find-then-create is not atomic, so two failing together could open
it twice or one's pass could close it under the other's failure.
…rs past terminal queries

The hosted scratch run with bcvk's own CI host setup (bootc-dev/actions
bootc-host-setup at a pinned commit) booted both bases: silverblue passed
every check, and bazzite booted and passed its package and service checks.
The workflow now runs that setup first.

bazzite still came out red for two reasons fixed here:
- its login shell prints terminal colour and cursor queries on the same line
  as the first marker, so the systemd state was never read and the boot was
  reported as never settling. Markers are now found anywhere in a line.
- greenboot-healthcheck.service and bazzite-hardware-setup.service fail on
  every ephemeral boot because rpm-ostree refuses a system not booted via
  libostree. Both are allowlisted with that reason.

Replaying both runs' captured output through the new parser and allowlist
gives a pass for every boot.
@Danathar

Copy link
Copy Markdown
Owner Author

Hosted runs. A scratch branch (now deleted) ran this workflow on ubuntu-26.04 for one Universal Blue base and one Fedora base.

  • Without bcvk's own CI host setup, every boot failed before a VM started. virtiofsd exited with Error entering sandbox: DropSupplementalGroups ... Operation not permitted. With bootc-dev/actions bootc-host-setup at a pinned commit, both bases booted. The workflow now runs that setup first (e49bd1c).

  • silverblue: passed every check in run 39056483790, in 4.5 minutes.

  • bazzite: booted, and its package and service checks passed. It still came out red for two reasons, both fixed in e49bd1c:

    • Its login shell prints terminal queries on the same line as the first result marker, so the systemd state was never read.
    • greenboot-healthcheck.service and bazzite-hardware-setup.service fail on every ephemeral boot, because rpm-ostree refuses a system not booted via libostree. Both are now allowlisted with that reason.

    Replaying that run's captured output through the fixed parser gives a pass for both boot attempts. A run of the final workflow on a hosted runner has not happened yet; the first scheduled night after merge is that run.

Also since the first push:

  • f5c023e: the two Codex findings, and the check script now ships as a readable file.
  • d48557e, 522e9aa: a VM that bcvk never started on the host goes to one shared runner issue, not to each base's issue. It counts as the runner's only when bcvk names a host error and the guest wrote nothing. Matrix slots now run one at a time, so the two jobs cannot race on that shared issue.

…on command

A workstation run cannot reproduce a runner that failed to set up or to start
bcvk, so the shared runner issue names the setup steps to read instead of
the per-base reproduce command.
@danathar-atomic-hive danathar-atomic-hive Bot added the author/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) label Oct 17, 2026
@danathar-atomic-hive

Copy link
Copy Markdown
Contributor

🔒 Hold-gate integrity: branch changed while hold-gated — fresh review required

This PR's branch moved while it sat hold-gated: the head recorded when the hold was applied was 220e06f43c95, but the branch now sits at 48543ae95dca. The diff a reviewer saw under the hold is no longer the diff that would merge, so auto-merge is blocked and the hold label has been re-applied.

Commits not present in the hold-time snapshot (8):

  • 84c5dd513960 by danathar-atomic-hive[bot] — Merge branch 'main' into feat/756-variant-boot-test
  • f5c023e52edf by Danathar — fix: bound a slot within the job timeout, compare boot failures by signature, ship the check script as a file
  • d48557ed097b by Danathar — fix: keep host and runner failures out of the per-base issues
  • 522e9aac196a by Danathar — fix: call a failure the runner's only when bcvk names a host error, and run slots one at a time
  • 9b9a85e0e9fc by Danathar — Merge remote-tracking branch 'origin/feat/756-variant-boot-test' into feat/756-variant-boot-test
  • e49bd1cc828a by Danathar — fix: boot on the hosted runner with bcvk's host setup, and read markers past terminal queries
  • 7e1f516a8355 by Danathar — fix: point the runner issue at the job's setup steps, not a workstation command
  • 48543ae95dca by Danathar — docs: say when each of the boot test's two pins moves

Authors not in the hold-time author set: danathar-atomic-hive[bot]

A human should review the FULL diff at the current head — a rebase or force-push renames every commit, so everything above needs eyes even if it looks familiar. Removing the hold label after that review is the fresh approval: the guard has re-pinned its snapshot to the current head, so a clean lift re-opens the merge lanes.

@danathar-atomic-hive danathar-atomic-hive Bot added hold and removed author/trusted Author is trusted for unattended merge (trusted-author role or registry/claim tier in require_tiers) labels Oct 17, 2026
@danathar-atomic-hive

Copy link
Copy Markdown
Contributor
48543ae

⚠️ Sentinel alert — maintainer review required

Hive flagged this PR (author @Danathar, head 48543ae95dca) because it matches behaviors that can override security controls, escalate privileges or damage the codebase. This is a heuristic, not an accusation — a maintainer should confirm the change is intended before it merges.

  • sensitive_path — changes 2 sensitive path(s) (Touches a sensitive path (OWNERS, workflows, policies, hive config, security docs, …))
    • .github/policies/workflow-permissions.json
    • .github/workflows/variant-boot-test.yml

Hive added the sentinel-alert label. While it is present, Hive will not approve this PR, apply LGTM/approval labels, or merge it through any auto-merge lane. Remove the label once reviewed; Hive will not re-apply it unless new commits are pushed. Tune paths and behaviors under sentinel in hive.yaml or the dashboard Security tab.

Two paths still turned a runner failure into a verdict about an image:
- When bcvk never started the unmodified base's VM, a generated image that
  failed to boot was blamed on the tool ("does not fail this way"), though
  the base was never compared. That comparison now reports Unknown.
- When the first boot reached a real failure and the retry hit a host
  failure, the retry was reported: the base's failure went to the shared
  runner issue, which the next passing slot closes. The first attempt's
  failure now stands.

The runbook also says how to re-check a "(BlueBuild)" issue: only a
BlueBuild pass of that base closes it, and the rotation reaches a base
that way once in as many weeks as there are bases.
@danathar-atomic-hive

Copy link
Copy Markdown
Contributor
9bc571e

⚠️ Sentinel alert — maintainer review required

Hive flagged this PR (author @Danathar, head 9bc571e1ed18) because it matches behaviors that can override security controls, escalate privileges or damage the codebase. This is a heuristic, not an accusation — a maintainer should confirm the change is intended before it merges.

  • sensitive_path — changes 2 sensitive path(s) (Touches a sensitive path (OWNERS, workflows, policies, hive config, security docs, …))
    • .github/policies/workflow-permissions.json
    • .github/workflows/variant-boot-test.yml

Hive added the sentinel-alert label. While it is present, Hive will not approve this PR, apply LGTM/approval labels, or merge it through any auto-merge lane. Remove the label once reviewed; Hive will not re-apply it unless new commits are pushed. Tune paths and behaviors under sentinel in hive.yaml or the dashboard Security tab.

@danathar-atomic-hive danathar-atomic-hive Bot added author/clanker A contributor's own hive authored this PR under their login no-dco A commit is missing Signed-off-by — run `git rebase --signoff` stage/050-review-pending Review swarm requested; awaiting a verdict for the current head and removed stage/090-ready-for-human-merge Hive would merge this but trust/role blocks it; a maintainer's /lgtm /approve is all that is missing labels Oct 18, 2026
The boot job holds issues: write and then runs bootc-dev/actions' host
setup scripts. actions/checkout persists its token for later git commands
by default, and nothing in this job runs git with credentials: only the
report step needs a token, and it gets one through GH_TOKEN.

Signed-off-by: Douglas Baggett <doug.baggett@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author/clanker A contributor's own hive authored this PR under their login no-dco A commit is missing Signed-off-by — run `git rebase --signoff` stage/050-review-pending Review swarm requested; awaiting a verdict for the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nothing checks that the images the tool generates still build and boot on each supported base

1 participant