feat: pre-seed snap packages for offline install copy#2
Open
feat: pre-seed snap packages for offline install copy#2
Conversation
added 8 commits
May 2, 2026 23:06
Adds Firefox and LibreOffice as pre-seeded snaps so they are available
in the live session and copied to the installed system by fisherman.
How it works
1. ISO build (this PR):
- install-snaps.sh downloads Firefox, LibreOffice, core22, and
core24 via `snap download` (no daemon required) and writes a
valid seed.yaml into /var/lib/snapd/seed/.
- The seed persists in the live squashfs untouched.
2. Live session boot:
- snapd reads /var/lib/snapd/seed/seed.yaml on first boot and
installs the snaps offline (no network needed for seeded snaps;
content snaps like gnome-46-2404 are downloaded if needed).
3. Install (fisherman PR tuna-os/fisherman#25):
- fisherman sees `"snaps": [...]` in recipe.json and calls
CopySnaps(), which tars /var/lib/snapd/ from the live session
into the installed system's writable /var.
- On first boot of the installed system, snapd finds its state
already populated and re-mounts the loop devices — no download.
Files changed
src/snaps new — list of app snaps (one per line)
src/install-snaps.sh new — download + seed.yaml generation script
Containerfile new snap pre-seed layer after flatpak layer;
uses --mount=type=cache for fast rebuilds
src/etc/bootc-installer/recipe.json
add "snaps": ["firefox", "libreoffice"] so
fisherman opts into CopySnaps()
Depends on: tuna-os/fisherman#25 (CopySnaps implementation)
output/ contains the built ISO and build log which are too large for git and should never have been tracked. Ignoring going forward.
Sources the snap list from the official Ubuntu 26.04 germinate output
(ubuntu.resolute/desktop) so the live ISO ships the same apps as the
regular Ubuntu 26.04 Desktop ISO.
src/snaps — updated snap list with correct per-snap channels:
Platform/base: bare, core24, gnome-46-2404, gtk-common-themes,
mesa-2404, snapd-desktop-integration
Apps: firefox (stable/ubuntu-26.04)
thunderbird (stable/ubuntu-26.04)
snap-store (2/stable/ubuntu-26.04)
firmware-updater (1/stable/ubuntu-26.04)
Excluded: snapd (apt-managed), ubuntu-desktop-bootstrap
(replaced by gnome-initial-setup), desktop-security-center
and prompting-client (AppArmor prompting, masked in live)
install-snaps.sh — parse 'name channel' format from snaps file;
drop hardcoded base-snap downloads (now explicit in snaps file);
channel is recovered per-snap when writing seed.yaml entries
src/flatpaks — add org.libreoffice.LibreOffice
LibreOffice ships as debs in the official Ubuntu 26.04 ISO but we
deliver it as a Flatpak so it installs cleanly onto the composefs
read-only sysroot without touching the deb layer. CopyFlatpaks()
copies it to the installed system at install time.
recipe.json — expand snaps list to match the full pre-seeded set
Replaces the single org.libreoffice.LibreOffice entry with the full app set that ubuntu-desktop adds on top of ubuntu-desktop-minimal, using Flatpaks in place of debs throughout. Apps added as Flatpaks (Ubuntu ships as debs, no deb layer needed): org.libreoffice.LibreOffice office suite org.gnome.DejaDup backups org.gnome.FileRoller archive manager org.gnome.Calendar calendar org.gnome.SimpleScan document scanner org.gnome.Shotwell photo manager org.gnome.Showtime video player org.gnome.Music music (Rhythmbox has no Flatpak) com.transmissionbt.Transmission BitTorrent client org.remmina.Remmina remote desktop org.gnome.Snapshot camera Thunderbird and Firefox are already covered by src/snaps. Skipped (no Flatpak, Ubuntu-specific or system services): usb-creator-gtk, gst-audio-thumbnailer, gst-video-thumbnailer Already in ubuntu-desktop-minimal (base image) — not re-added: baobab, gnome-calculator, gnome-characters, gnome-clocks, gnome-control-center, gnome-disk-utility, gnome-font-viewer, gnome-text-editor, papers, ptyxis, resources, seahorse
snap/snapd is installed as part of ubuntu-desktop-minimal in the bootc base image. apt-get cannot be used in the ISO live Containerfile anyway (bootc-rootfs.sh wipes /var/lib/dpkg). snap download works without the daemon so no additional package installation is needed.
ls *.snap exits non-zero when no files match; set -e kills the script on a cold cache. Use find which always exits 0 with empty output.
snap download calls api.snapcraft.io which was unreachable without host network access in the podman build container.
Adapts the dakota-iso e2e LUKS test pattern for a plain Ubuntu install
(no LUKS, no passphrase unlock step).
justfile additions:
test-live — boots ISO headlessly, waits for UBUNTU26_LIVE_READY
serial marker from live-ready.service; no debug=1 needed
e2e — build + full QEMU lifecycle; requires debug=1 for SSH
e2e-qemu — run QEMU e2e against an already-built ISO (no rebuild)
e2e-boot-live — daemonized QEMU, waits for marker + SSH
e2e-install — SSH fisherman install with CONTAINERS_STORAGE_CONF
pointing at /usr/lib/bootc/storage (where the bootc
payload image is embedded in the squashfs), patches
BLS entries for ttyS0, powers down live QEMU
e2e-boot-installed — boots installed disk, waits for login: prompt
.github/workflows/e2e.yml:
- Runs on every PR and weekly
- Steps: pull base image → build debug ISO → test-live → e2e-qemu
- continue-on-error on e2e so serial logs are always uploaded
- Uploads /tmp/ubuntu-26.04-e2e-*.log as artifacts (14-day retention)
- Posts a PR comment table with per-step result + timing
- Fails the job if e2e did not pass
Verified: just test-live ubuntu-26.04 passes in 18s against the
locally-built ISO.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
ghcr.io/hanthor/ubuntu-26.04-desktop-bootc:latest does not yet exist in the registry (the bootc repo CI has not published it). Build it directly from source in the e2e workflow instead of pulling. Also bump timeout to 180 min to accommodate the Rust compilation step (bootc builds from source; first run takes 20-40 min on GH Actions).
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
When just calls a recipe with `just e2e-boot-live`, it spawns a new
just invocation without inheriting command-line variable overrides.
output_dir was resetting to the default 'output/' causing e2e-boot-live
to look for the ISO in the wrong place.
Fix: explicitly thread output_dir={{output_dir}} through e2e-qemu and e2e.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
The e2e test passed end-to-end: ✓ Build base bootc image (hanthor/ubuntu-26.04-desktop-bootc, ~25 min) ✓ Build debug ISO (flatpaks + snap seed, ~15 min) ✓ Boot live ISO (smoke test) — UBUNTU26_LIVE_READY in ~18s ✓ Full install end-to-end — fisherman install + login: on installed disk Only failure was the artifact upload (Node.js 20 deprecation / exit 127). Adding continue-on-error so that cosmetic issue doesn't fail the job.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
configure-live.sh runs before install-flatpaks.sh so the /usr/local/bin/fisherman symlink is never created at build time. Find the fisherman binary directly from the installed Flatpak: find /var/lib/flatpak/app/org.bootcinstaller.Installer -name fisherman Falls back to /usr/local/bin/fisherman if Flatpak path not found.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
…perms - ubuntu-26.04/src/e2e-install.sh: new file with the fisherman invocation logic — SCP'd to the guest during e2e-install, no inline bash quoting needed. Finds fisherman in the Flatpak store, sets CONTAINERS_STORAGE_CONF to /usr/lib/bootc/storage, runs fisherman. - justfile: e2e-install now SCPs the repo-committed script and runs it via SSH. Removes all the fragile nested quoting. - e2e.yml: add 'Fix serial log permissions' step before artifact upload. QEMU runs as root so the serial logs are root-owned; the runner user can't read them without the chmod.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
fisherman only accepts xfs/btrfs/zfs — ext4 is rejected at validation. The e2e-install.sh script now writes its own recipe directly (avoiding the recipe generation in the justfile) with filesystem=xfs.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
The apt-installed just on ubuntu-24.04 runners is too old to support the [group()] attribute syntax used in our Justfile. setup-just installs the latest stable release.
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
… status
Full pipeline verified locally with just e2e-qemu ubuntu-26.04:
1. Live ISO boots (UBUNTU26_LIVE_READY in 27s) ✓
2. SSH ready in 5s after marker ✓
3. Fisherman installs (patched binary, composeFsBackend=true,
oci:/usr/lib/bootc/storage → OCI export fixed by PR #25) ✓
4. 12 Flatpak apps copied ✓
5. BLS patch: vda1 (EFI FAT32, not vda2 root XFS) — added
console=ttyS0,115200 and systemd.wants=ssh.service ✓
6. Installed system boots in 54s to BOOTC_STATUS_END ✓
7. bootc status parsed from serial log ✓
Key fixes in this commit:
- e2e-install.sh: mount vda1 (EFI) not vda2 (root) for BLS patching
- e2e-install.sh: (( ++COUNT )) not (( COUNT++ )) to avoid set-e exit
when COUNT=0 (bash arithmetic exit status = old value = 0 = false)
- justfile: strip kernel timestamp prefixes from serial log lines
before grep '^{' so the bootc status JSON is found
- justfile: e2e-boot-installed waits for BOOTC_STATUS_END instead
of login: (more precise verification)
bootc status output confirms:
- Booted image: oci:/usr/lib/bootc/storage (composefs, BLS boot)
- composefs verity hash: 84d178...
- bootloader: systemd (systemd-boot)
- imageDigest: sha256:85c71e...
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
❌ ISO End-to-End Test — FAILED
Serial logs are attached to the workflow run. |
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.
What
Pre-seeds Firefox and LibreOffice as snap packages in the live ISO squashfs so they are:
/var/lib/snapd/from the live environment to the installed system, so snaps are present without re-downloadingHow it works
Changes
src/snapssrc/install-snaps.shseed.yamlContainerfile--mount=type=cachefor fast rebuildssrc/etc/bootc-installer/recipe.json"snaps": ["firefox", "libreoffice"]— opts fisherman intoCopySnaps().gitignoreoutput/(build artefacts, too large for git)Notes on content snap dependencies
Firefox and LibreOffice depend on content snaps (
gnome-46-2404,gtk-common-themes) that are not included in the seed. snapd will download these on first live boot if the machine has internet access. For a fully offline demo, those content snaps can be added tosrc/snapsand they will be included in the seed automatically.Depends on
CopySnaps()implementation