Repository navigation
feat: read core flatpaks from the image label, and preview them in the wizard (ADR-0018) - #111
Conversation
With core_flatpaks, firn installed first-setup's GNOME list for every image, read from an ISO copy because composefs hides the deployment's /usr. Sundog got GNOME apps and Floe would have got a desktop set. Firn now reads each image's org.frostyard.core-flatpaks label from the same inspection preflight uses to select the source, so the set describes the image being installed and a malformed label stops the install before any disk write. Saved Sundog and Floe recipes with core_flatpaks = true now install that image's set, or none with a no_core_set warning, instead of the GNOME list. A locally seeded ISO still copies its whole seed. Do not tag a release until the published snosi images carry the label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
The wizard offered the core-app toggle for every image without saying what it would install, including Floe, which publishes no set. The flatpaks page now inspects the chosen image's label with preflight's local-first selection: it lists a published set by name, explains instead of offering the toggle when an image has no set or a malformed one, and keeps the toggle when inspection fails, since preflight reads the label again at install. Hiding the toggle clears it, so a recipe never asks for a set the wizard did not offer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A signed digest can pass cosign verification while its registry inspection failed and no matching local copy exists. Its labels were then treated as absent, so a core_flatpaks install reported no_core_set and skipped the requested apps, and label validation never ran. Source now records whether its labels came from a successful inspection, and preflight stops when core_flatpaks needs labels it could not read. encoding/json matches field names case-insensitively, so the parser accepted VERSION, FLATPAKS, ID and NAME despite DisallowUnknownFields, unlike the spec and snosi's producer. Field names are now checked exactly at both object levels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bketelsen
left a comment
There was a problem hiding this comment.
Overall this looks good. The selection logic in CheckAndPinImage is unchanged: the new branches only decide where the labels come from. The parser matches the spec table and snosi's check_set, and failing closed when Inspected == false is the right call. go test passes for flatpak, bootcimg, steps and tui at ed55c0d.
Release gate: the published :latest images now carry the label. I just ran skopeo inspect: snow and snowfield have 23 apps, sundog has 10, and floe has no key. All four parse cleanly with this branch's ParseCoreLabelApps, so the "do not tag until…" condition looks met for latest.
Inline comments, roughly by priority:
- While it inspects, the wizard blocks for up to 30 s with nothing on screen (
wizard.go). - The two inspections share one timeout, so a hanging registry starves the local inspect (
core_preview.go). - Display names accept control characters and terminal escapes, and they render before verification (
label.go). {"version":1}gets a misleading error message (label.go).- Nits: wrapping counts bytes, not characters; the uninspectable case has no stable error code.
One doc nit: under Fail before writing, ADR-0018 says only headless recipes can reach core_flatpaks_label_invalid. A wizard recipe can reach it too: when the preview's inspection failed and the toggle stayed, or when the tag moved between the preview and the install. Worth correcting now, while the ADR is still being edited in this PR.
Review drafted with Claude Code.
The flatpaks page inspected the image with nothing on screen for up to 30 seconds on a network that drops packets, and keys pressed meanwhile reached the form. A spinner now shows while it inspects and swallows those keys. The registry inspect ran first and shared that deadline, so a hanging registry also hid an image already present locally; CheckAndPinImage now inspects the local copy first. Names render on the installer console before the image is verified, so names with control or format characters are malformed. A label with version but no flatpaks reported a JSON error instead of the spec's message, preview wrapping counted bytes rather than display width, and unreadable labels failed preflight without a stable code; they now have core_flatpaks_label_unreadable. ADR-0018 no longer claims only headless recipes can reach a label error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, all addressed in 351ae37 (inline replies above). The ADR-0018 Fail before writing bullet now says a wizard recipe can also reach the label errors: when the preview's inspection failed and the toggle stayed, or when the tag moved between preview and install. Validation at 351ae37: |
Summary
Implements ADR-0018 (now Accepted): all of roadmap Phase 10's firn work (steps 3 and 4), install path and wizard together.
With
core_flatpaks = true, firn installed first-setup's GNOME list for every image. It read the list from the deployment, or, on composefs images (all snosi desktops), from the ISO's copy at/usr/share/firn/core-flatpaks.json. Sundog got GNOME apps, and Floe would have got a desktop set.Firn now reads the
org.frostyard.core-flatpakslabel that each image publishes (frostyard/snosi#1054). It reads the label inpreflight-image, from the same inspection that selects the image to install, so:Release note: saved Sundog and Floe recipes with
core_flatpaks = truenow install that image's set, or none with ano_core_setwarning, instead of the GNOME list. A locally seeded ISO still copies its whole seed (offline seeding is out of scope).Do not tag a release from this until the published snosi images carry the label. The tag reaches the ISO immediately (ADR-0017). snosi#1054's
mainimage build is running now.Changes
internal/bootcimg/verify.go:CheckAndPinImagereturnsSource{Ref, Labels}. The labels come from whichever inspection selected the image:@sha256:ref, the local copy only if its digest matches.The selection logic itself is unchanged.
internal/flatpak/label.go(new):ParseCoreLabelimplements the ADR's table fail-closed:versionmust be the integer 1;name;internal/flatpak/flatpak.go: removesCoreSet,InstallerCoreSet,coreJSONPath,InstallerCoreJSONPathandparseCoreJSON.internal/steps/preflight.go: withcore_flatpaks = true, parses the label after selecting the image:core_flatpaks_label_invalid, in dry-run and real runs alike;no_core_set(warning plus summary item) and the install continues;Env.CoreFlatpaks.The label is never parsed when
core_flatpaks = false.internal/steps/bootc.go:runFlatpaksinstalls the explicit apps, then the core set, each ID once. It no longer reads the deployment or the installer medium.internal/progress,internal/pipeline: the new code constant and theCoreFlatpaksfield.Docs:
docs/specs/core-flatpaks-label.md, indexed;core_flatpaksrow inrecipe-schema.md;progress-protocol.mdaddscore_flatpaks_label_invalidand updatesno_core_set;architecture.md;/etc/firn/catalog.json).Wizard (
internal/tui/core_preview.go,wizard_pages.go,wizard.go): the flatpaks page inspects the chosen image when it opens (30s timeout), using preflight's local-first selection without signature verification. It inspects again whenever the chosen image changes, including after "Start over". Four states:Hiding the toggle clears
core_flatpaks. The preview never enters the recipe.flatpak.ParseCoreLabelAppskeeps display names for the preview, andParseCoreLabelwraps it.test/e2e-tui.sh: the driver no longer accepts a core-app toggle for Floe. It waits for "publishes no core Flatpak apps" and fails if the toggle appears. The guest already pulls Floe from GHCR, so this adds no new registry dependency.Docs: label spec rule 7 (the wizard preview), the recipe-schema TUI note, the architecture wizard paragraph, and roadmap Phase 10 step 4.
Review fixes (Codex)
Source.Inspectedis true only when the labels came from a successful, parsed inspection of the selected image. A signed@sha256:ref can verify while its registry inspection failed and no matching local copy exists. Withcore_flatpaks = true, preflight now stops ("could not inspect image …") instead of reportingno_core_setand installing without the requested set. The wizard treats such a result as "inspection failed", keeps the toggle, and retries on the next visit.encoding/jsonmatchedVERSION,FLATPAKS,IDandNAMEcase-insensitively despiteDisallowUnknownFields. The parser now checks exact field names at both object levels, matching the spec and snosi's producer.TestParseCoreLabel;Inspectedasserted in every label-selection row, including the offline pinned digest and an unparseable registry inspection;TestCoreFlatpaksUninspectableImageFailsPreflight(mutation-checked: it fails with the guard removed);TestCorePreviewUnreadableInspectionKeepsToggle. Spec rules 1, 3 and 7 updated.Docs housekeeping
docs/specs/recipe-schema.mdin the same commit; progress-event changes updatedocs/specs/progress-protocol.mddocs/design/*anddocs/plans/roadmap.mdupdated for behavior changesTEMPLATE.mdand indexed indocs/README.md, cross-linked both ways (ADR ↔ design ↔ spec ↔ plan)verify.gounchanged in that respect;label.gois firn-original)Verification
make check(fmt, lint, test) greenmake verifygreenrunnerfakemake e2e-tuirun locally against the re-synced driver: PASS (Floe installed in a nested VM, disk booted, hostname and user verified over SSH, generated recipe validated, with nocore_flatpakskey)New tests:
TestParseCoreLabel: 32 rows covering every ADR table case plus snosi's producer rejection cases.TestCheckAndPinImageLabelsFollowSelectedImage: unsigned and signed, local vs registry, a pinned digest with a matching or mismatched local copy, offline, and an inspection with no labels.TestCorePreviewStates(each state, plus caching per ref and retrying after a failure),TestCorePreviewFollowsImageChangesAndClearsHiddenToggle(Snow → Floe → malformed → Sundog),TestFlatpaksFormRendersEachPreviewState,TestFlatpaksFormWithoutCoreSetCompletes.preflight-imagewithcore_flatpaks_label_invalid, with no destructive command, in dry-run and real runs;core_flatpaks = false;Risk tier
Rationale:
core_flatpaksinstalls and adds a new preflight failure, but touches no protected path (.github/workflows/**,.goreleaser.yaml,internal/install/**,cmd/**). Image selection logic is unchanged; only its return type grew labels.🤖 Generated with Claude Code