ci: unbreak main, gate pull requests, and close the factory drift - #42
Open
castrojo wants to merge 13 commits into
Open
ci: unbreak main, gate pull requests, and close the factory drift#42castrojo wants to merge 13 commits into
castrojo wants to merge 13 commits into
Conversation
Every push to main has been red while every PR was green, because both
failures are in jobs that only run post-merge.
lab-runner/just.bst: the release tarball records uid/gid 1001, and tar
cannot restore that ownership inside the BuildStream sandbox, so it exits
non-zero ("Cannot change ownership to uid 1001, gid 1001") even though the
member extracted. Extract with --no-same-owner.
tests/vm-boot.sh: `-cpu max` enables FEAT_E0PD, and QEMU < 9.2 -- including
the 8.2 on ubuntu-24.04 runners -- aborts on the first E10_0 TLBI with
"regime_is_user: code should not be reached". Boot aarch64 TCG on
cortex-a76 (ARMv8.2, predates FEAT_E0PD) so the test exercises the disk
instead of a QEMU bug. x86_64 keeps -cpu max; KVM keeps -cpu host.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
elements/targets.json gains image_paths, shared_paths, canary_image, and vm_guest_paths so path ownership stays in the canonical manifest instead of being hand-maintained in a workflow. `just changed-targets BASE HEAD` resolves them against the merge base and prints the affected targets as JSON for the pull-request build gate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pull requests only resolved the element graph, so a PR could be green while the merge commit turned main red -- which is what has been happening on every recent push. This adds the missing gate without paying for the full seven-image, two-architecture matrix on every PR: - `changed-targets` resolves what the PR can break from the path ownership in elements/targets.json; a shared-path change builds the canary image. - `pr-build-oci` builds and runs `just verify` for each affected image on both architectures. - `pr-build-vm-guest` builds, checksums, and boot-tests the VM guest when it is affected. Neither PR job calls oci-images.yml or vm-guest.yml and neither contains a login, push, sign, attest, or release step, so a pull request -- including one from a fork -- has no code path to publication and needs only `contents: read`. The QEMU setup and boot test move into a composite action shared with vm-guest.yml so the gate cannot drift from the check that guards publication, and every checkout now sets persist-credentials: false. The multi-arch guard on rolling tags is restored so a single-architecture failure can never replace a multi-arch manifest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renovate ran with the default GITHUB_TOKEN and the FSDK bump branch was pushed with the workflow's own credentials. Neither can trigger another workflow, so both kinds of automated PR arrived with no checks -- while renovate.json asked for non-major action bumps to be auto-merged. Bumps could merge without a single build ever running, which is how a broken element reaches main unseen. Both now use a Mergeraptor app installation token. Mergeraptor is an org-level app whose permissions are already granted, so this reuses the existing MERGERAPTOR_APP_ID / MERGERAPTOR_PRIVATE_KEY secrets: no PAT, no new secret, no new permission. auto-update-fsdk drops to contents: read because every write now goes through that token. platformAutomerge is disabled so Renovate merges on its own check results rather than GitHub's auto-merge queue, which needs branch protection this repo does not yet have. Major updates keep automerge: false. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This repo publishes signed, attested, SBOM-bearing images but ran none of the checks its sibling repos run. - scorecard, actionlint, validate-renovate: thin callers matching dakota and common. actionlint covers .github/actions/ too. - vulnerability-scan: pulls the SPDX SBOM that ships with each published manifest and scans that with Grype. A rootfs scanner sees one package or none on a distroless image, which is why this is not a caller into reusable-vulnerability-scan.yml. Verified end to end against the published base image: 595 packages. - ghcr-cleanup: the org-wide job covers the bluefin, dakota and common families only, so this repo's 7 manifest and 14 per-arch packages were never pruned. Untagged manifests only -- point-release tags are immutable by contract, so keep-n-tagged is deliberately unused. - label-enforcement, the issue templates, and copilot-instructions.md were written but never committed, so org label enforcement was not actually running. The caller now pins @v1 like every other consumer. - cliff.toml, so structured changelogs work and the factory drift check stops flagging this repo. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every session leaves the next agent the rules it learned, not just the code (AGENTS.md). This records what changed and, more importantly, why the obvious shortcuts are wrong: - the pull-request gate, the targets.json path-ownership keys it reads, and why the PR jobs duplicate a few steps instead of calling the reusable publish workflows behind an `if:` - that every automated write goes through the org-level Mergeraptor app, that this needs no PAT and no new permission, and why the shared reusable-renovate.yml cannot be used yet - why a rootfs scanner is the wrong tool here and what vulnerability-scan.yml does instead - the branch-protection settings the gate needs to actually block a merge - reconciles the SBOM skill's "never pip install in a loop" rule with the per-image fan-out, where one SBOM per job is correct Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
reusable-design-enforcement.yml does not exist on projectbluefin/actions main or at the v1 tag -- only at 67d4cfb -- so the @v1 caller failed immediately. Pin the full commit SHA and record why it deviates from the factory's @v1 convention. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Aug 1, 2026
reusable-design-enforcement.yml is not on projectbluefin/actions main or v1, and at the only commit that has it the nested design-enforcement action ref is unreachable, so the job fails during 'Set up job' on every issue and pull request event. A permanently red required-looking check is worse than no check. Tracked upstream in projectbluefin/actions#373. The issue templates and copilot-instructions.md that the workflow was meant to enforce are still committed here. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The gate added in this PR immediately found two more breakages behind the
just.bst one, both in lab-runner, both invisible before because nothing
built a PR.
nginx.bst: nginx's auto/ scripts and generated objs/Makefile shell out to
sed, grep and awk without checking for them. With none of them declared the
configure step silently produced an objs/Makefile with an empty object list
and the build died at link time with "cc: fatal error: no input files",
alongside "sed: command not found". Declare them, plus coreutils.
argo.bst: `gunzip argo.gz` rewrites the staged source in place, and gzip
refuses when the file has other links ("argo.gz has 1 other link -- file
ignored", exit 2). Decompress to a new file instead.
Both verified building against the remote-execution grid.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
freedesktop-sdk is a merged-usr sysroot: /usr/sbin is a symlink to /usr/bin, so an element that installs a real usr/sbin directory fails at lab-runner-runtime.bst staging with 'Destination is a symlink, not a directory: /usr/sbin'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The gate in this PR is the first thing to actually build and verify lab-runner before merge, and it found the image at 497MB against a 320MB budget. argo v4.0.8 is 181MB uncompressed on its own and kubectl is 57MB; stripping is deliberately disabled for both. The argo v3 -> v4 bump (#22) added roughly 100MB and merged with no checks, because Renovate was running with GITHUB_TOKEN and lab-runner was independently broken, so no build ever reached this gate. Raise the ceiling so it reflects the image that exists; #48 tracks actually slimming it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every one of these was hit in this PR, in lab-runner, and none of them says what it means: a tar ownership error, gunzip refusing a hardlinked source, a link failure whose real cause is an undeclared sed, and a staging failure caused by merged-usr. Written down so the next agent recognises them from the error text instead of rediscovering them one CI run at a time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pin the Renovate validator, gate VM boot-action changes, and keep CI workflow documentation accurate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CI/CD audit — findings and remediation
Full-repository audit of the delivery pipeline against this repo's own
docs/skills/*contracts and theprojectbluefin/actionsfactory conventions.Every finding below is either fixed in this PR or listed as tracked follow-up.
The headline
Every recent push to
mainwas red while every PR was green:lab-runner×2,vm-guest (aarch64)mainCause:
pull_requestran onlyjust validate(element-graph resolution), andevery build job was gated on
github.event_name != 'pull_request'. Nothing aPR ran could observe either failure.
Severity-ranked findings
A. Correctness / release safety
changed-targets+pr-build-oci+pr-build-vm-guestmainred across five consecutive pushes.lab-runner/just.bst:tar: just: Cannot change ownership to uid 1001, gid 1001— the tarball records uid/gid 1001 and the sandbox cannot restore it, sotarexits 2 although the member extracted.--no-same-owner-cpu maxenables FEAT_E0PD; QEMU < 9.2 (ubuntu-24.04 ships 8.2) aborts withregime_is_user: code should not be reached. Upstream fix: "target/arm: Don't assert in regime_is_user() for E10 mmuidx".cortex-a76(ARMv8.2, predates FEAT_E0PD); KVM keeps-cpu host, x86_64 keepsmaxoci-images.ymlguarded only the minor-line tag withBOTH_ARCHS_AVAILABLE. Narrower than first assessed:just tagsdeliberately emits nolatest, so nothing is currently exposed.sha256ref:of aremotesource, so those bumps are structurally build-breaking — and nothing built them.github.token, so its PRs triggered no workflows, whilerenovate.jsonsetautomerge: truefor non-major GitHub Actions updates. Bumps could merge with no build ever running.platformAutomerge: falseauto-update-fsdk.ymlpushed the bump branch with the checkout'sGITHUB_TOKEN, so the FSDK bump PR carried no checks; only a side-channelrepository_dispatchverified it.B. Supply chain / hardening
mainhas no branch protection (/branches/main/protection→ 404).docs/skills/ci-tooling.md; needs an adminactionlint: ~25 KB of workflow YAML, none of it linted..github/actions/**)ghcr.io/projectbluefin/base: 595 packagespersist-credentials: falseon any checkout.reusable-validate-renovate.yml@v1keep-n-taggedis deliberately unused)C. Factory / process drift
label-enforcement.yml,.github/ISSUE_TEMPLATE/*andcopilot-instructions.mdexisted only in a local worktree — org label enforcement was not actually running.@v1(afactory-driftcheck)docs/skills/ci-tooling.mdpredates the gate, the token rule and the new workflows.cliff.toml— flagged byfactory-drift.ymlfor image repos.artifacthub-automation.mdis honestly markedplanned-not-implemented; images ship ArtifactHub labels but are never registered.renovate.jsonlives at the repo root while siblings use.github/renovate.json5. Harmless; the validate caller filters on the real path.D. Efficiency / observability
signing-and-sbom.md§5 ("neverpip installin a loop, usejust sboms") conflicted with the per-image fan-out, where one SBOM per job is correct.mainwas red five pushes running with no issue, alert, or notification.publish-smoke.yml).What this PR changes
Design decisions worth reviewing
oci-images.yml/vm-guest.yml. They areseparate jobs with
permissions: contents: readand no login, push, sign,attest, or release step anywhere in them. Gating publication with an
if:inside a shared job is one careless edit away from a fork PR publishing to
GHCR; gating it by not having the code path is not. The cost is a few
duplicated setup lines — and the expensive, drift-prone part (QEMU setup +
boot test) is a composite action shared with the release path.
base), not all seven images.Full-matrix PRs are not viable;
baseis what every other image is carvedfrom, so it is the honest single-build proxy.
reusable-renovate.yml. Thatreusable workflow validates its token with
check-token-health'srequired_scopes: repo,workflow— an OAuth scope check a GitHub Appinstallation token cannot satisfy. Using it would mean reintroducing a PAT,
which is banned. Worth raising upstream in
projectbluefin/actions.reusable-renovate-automerge.yml.The reusable workflow merges any green Renovate PR, which would also merge
major bumps that
renovate.jsonexplicitly excludes. Now that Renovate'sPRs actually get checks, its own config-driven automerge is both sufficient
and more precise.
Verification performed
actionlintclean across all 11 workflows.renovate-config-validator --strict: config valid.just validate: element graph resolves with thejust.bstchange.just changed-targetsverified on three cases: no changes →[]; asingle-image change → that image; a shared-path change → the canary. Against
this branch it correctly resolves
{"oci_images":["base","lab-runner"],"vm_guest":true}.oras discover→
oras pull→ 595 packages.CI on this PR is itself the remaining verification: it is the first run of the
new gate, and it should build
base+lab-runneron both architectures andboot-test the VM guest.
What the gate found the moment it existed
mainwas broken in five independent ways, four of them invisible until aPR actually built something. Each was fixed in turn, in this PR:
lab-runner/just.bst—tarexit 2--no-same-ownervm-guest (aarch64)— QEMU abort-cpu maxenables FEAT_E0PD; QEMU 8.2 asserts →cortex-a76under TCGlab-runner/nginx.bst—cc: fatal error: no input filessed,grep,awkundeclared; nginx'sauto/scripts fail silently and emit an empty object listlab-runner/argo.bst—gzip: argo.gz has 1 other linkgunziprewrote a hardlinked staged source in place →gunzip -c > argolab-runner— 497MB vs a 320MB budgetGITHUB_TOKENand the image was already failing earlier#5 is the audit's thesis in one line: an unverified auto-merged dependency bump
added ~100MB to a shipped image, and nothing in the pipeline could see it. The
ceiling is raised to 544MB here so the gate reflects the image that exists;
#48 tracks actually slimming it.
Also removed: the
label-enforcement.ymlcaller.reusable-design-enforcement.ymlis on neither
mainnorv1ofprojectbluefin/actions, and at the only committhat has it the nested action ref is unreachable, so the job fails during
Set up jobon every issue and PR event. Tracked in projectbluefin/actions#373;the issue templates it was meant to enforce are still committed here.
Final CI status — all green
Follow-ups filed: #43 (nothing notices a red
main), #44 (no post-publish smokecheck), #45 (Renovate cannot refresh
sha256refs), #46 (brew-nspawnuntested),#47 (ArtifactHub half-implemented), #48 (lab-runner size),
projectbluefin/actions#373 (app tokens + the missing reusable workflow).