Dosu LogoDosu Logo
Ask
Join our Discord
Organization avatar
bluefinPublic
Bluefin
Documentsbluefin
CI_CD_GUIDE
CI_CD_GUIDE
Type
External
Status
Published
Created
Jun 13, 2026
Updated
Jun 13, 2026
Source
.github/CI_CD_GUIDE.md

CI/CD Guide — bootc-installer#

Complete reference for all CI workflows, release process, and the release qualification runbook.


Workflows#

python-test.yml — Python Tests#

Triggers: push/PR to dev/prod, merge queue, manual dispatch

JobWhat it does
unitpytest tests/unit/ with coverage (--cov-fail-under=47) — no display required
uipytest tests/ui/ via Xvfb + compiled GResources (meson/ninja)

Coverage gate is a ratchet — never lower --cov-fail-under. Measure with pytest tests/unit/ -q --cov=bootc_installer 2>&1 | tail -5 before raising it.


go-test.yml — Go Tests (fisherman)#

Triggers: push/PR to dev/prod, merge queue, manual dispatch

Runs inside fisherman/fisherman/:

  1. go vet ./...
  2. go test -v -count=1 -timeout=60s -coverprofile=coverage.out ./...
  3. Coverage gate: 20%
  4. go test -race -count=1 -timeout=60s ./... (race detector)

flatpak.yml — GNOME Flatpak Build#

Triggers: push to dev/prod, v* tags, PRs to dev/prod

JobConditionOutput
productionPR, prod push, or v* tagorg.bootcinstaller.Installer.flatpak → continuous release
develPR or dev pushorg.bootcinstaller.Installer.Devel.flatpak → continuous-dev release

Uses ghcr.io/flathub-infra/flatpak-github-actions:gnome-50 container with --privileged. Requires permissions: contents: write on the release job.


build-flatpaks.yml — Multi-Variant Flatpak Build#

Triggers: push to dev/prod, v* tags, manual dispatch

Builds all three variants in parallel via a matrix:

VariantApp IDManifest
GNOMEorg.bootcinstaller.Installerflatpak/org.bootcinstaller.Installer.json
XFCEorg.xfceinstaller.Installerflatpak/org.xfceinstaller.Installer.json
KDEorg.kdeinstaller.Installerflatpak/org.kdeinstaller.Installer.json

Publishes Flatpaks as GitHub release assets under the same continuous / continuous-dev / v* tags.


validate-flatpak.yml — Validate Flatpak Manifests#

Triggers: PRs/pushes that touch flatpak/*.json, meson.build, meson_options.txt

  • Validates all manifests are well-formed JSON
  • Checks required fields: app-id, runtime, command
  • Verifies app-id consistency per variant (GNOME / XFCE / KDE)
  • Posts a ✅ comment on the PR when all pass

nightly.yml — Nightly Tests#

Triggers: 06:00 UTC daily, manual dispatch

Runs fisherman tests on both dev and prod branches:

  1. go vet ./...
  2. go test -v -count=1 -timeout=60s ./...
  3. go test -race -count=1 -timeout=60s ./...

This catches race conditions and test drift that only appear under extended runs.


Branch Strategy#

feature/xyz ──► dev ──► prod
  • All feature PRs target dev
  • prod is promoted wholesale from dev when dev is shippable
  • Never open PRs directly against prod
  • Merge queue is enabled on dev — use gh pr merge --squash <N> to enqueue

Release Process#

Continuous (pre-release)#

Automatic on every push to dev or prod:

  • dev → continuous-dev pre-release
  • prod → continuous pre-release

Tagged release#

git tag v0.3.0
git push origin v0.3.0

Both flatpak.yml and build-flatpaks.yml attach their Flatpak artifacts to the tagged release.


Release Qualification Runbook#

Software-only (automated — run locally)#

./QUALIFY_SOFTWARE.sh

This validates Flatpak JSON, runs all unit + UI tests, runs fisherman Go tests, and builds the production and devel Flatpaks. All steps must pass green before promoting dev → prod.

Hardware-only checks (manual — not automated in CI)#

These require real hardware and cannot be gated in CI:

CheckHow to test
TPM2 LUKS enrolmentInstall with tpm2-luks on real hardware; verify no password prompt on reboot
Recovery key displayInstall with tpm2-luks-passphrase; confirm key shown and copyable in GUI
Passphrase fallbackInstall with luks-passphrase; verify passphrase prompt on reboot
GRUB boot (XFS root)Install with filesystem=xfs; verify GRUB loads from ext4 /boot
systemd-boot (btrfs/composefs)Install with composeFsBackend=true; verify bootctl status clean
Windows slurpRun on machine with Windows NTFS partition; verify wallpapers + data migrated
Offline ISOBoot from live ISO with embedded OCI; verify install completes without internet
Post-reboot WiFiInstall on machine with saved WiFi; verify auto-reconnect after reboot
OEM first-bootInstall on ASUS/Framework hardware; verify OEM packages queued

For the full E2E integration tests (QEMU-backed, requires root):

go build -o /tmp/fisherman-test ./fisherman/fisherman/cmd/fisherman/
sudo FISHERMAN_BIN=/tmp/fisherman-test pytest tests/integration/test_e2e_install.py -v -s
# With QEMU boot verification (~5 min/image):
sudo FISHERMAN_BIN=/tmp/fisherman-test BOOT_VERIFY=1 pytest tests/integration/test_e2e_install.py -v -s

Common CI Failures#

SymptomCauseFix
"No checks reported" on PRBranch has merge conflicts — GitHub silently skips pull_request eventsgh pr view N --json mergeable → rebase onto dev
Coverage gate failsNew code not coveredAdd tests or measure actual floor before lowering gate
ModuleNotFoundError in Flatpak but not sourceNew .py not in meson.build sources = [...]Add the file to its subpackage's meson.build
fisherman submodule not updatedParent repo pointer not bumped after submodule pushgit add fisherman && git commit -m "chore: update fisherman submodule"
safe.bareRepository Flatpak build failureUsed "type": "git" in manifestSwitch to "type": "archive" with SHA256

Triggers:

  • Pushes to dev or prod branches
  • Any tag matching v*
Documents
Changelogs
Bluefin LTS Errata
Pin Log
2024-12-30-ublue-2024-wrapup
2025-01-01-announcements-archive
2025-02-23-february-update
2025-05-14-bluefin-f42
2025-06-23-bazaar-default
2025-07-06-fresh-bluefin-isos-and-bazaar
2025-07-11-four-years-of-ublue
2025-07-15-bold-brew
2025-07-17-ask-projectbluefin
2025-08-01-august-wallpapers
2025-08-08-bluefin-on-lfx
2025-08-29-github-oss-fund
2025-09-17-color-with-bluefin
2025-09-18-bluefin-lts-and-gdx
2025-09-23-LTS-at-indiafoss
2025-10-28-bluefin-autumn
2025-10-31-fastfetch-metrics
2025-11-15-cloud-native-milestone-15-million
2025-11-16-coreos-desktop
2025-11-24-new-just-and-bbrew
2025-11-27-bluefin-and-paleoartists
2025-11-30-report
2025-12-04-flatpak-support-in-brewfiles
2025-12-06-interview-with-michael-tunnell
2025-12-07-documentation-updates
2025-12-14-huntress-holiday-wallpapers
2025-12-15-easier-homebrew-installation-for-custom-images
2025-12-27-upcoming-homebrew-cli-changes
2025-12-30-bluefin-2025-wrapup
2025-12-31-report
2026-01-03-modernizing-custom-images
2026-01-31-report
2026-02-01-automated-reports-changelogs
2026-02-28-report
2026-03-13-bluefin-cli-mac-wsl
2026-03-27-bluefin-lts-gnome-49-50
2026-03-29-composefs-fisherman
2026-03-29-composefs-fisherman-design
2026-03-30-boot-verification
2026-03-30-boot-verification-design
2026-03-30-proportional-progress-tracking
2026-03-30-proportional-progress-tracking-design
2026-03-31-report
2026-04-02-state-of-ecosystem
2026-04-19-dakota-alpha-1
2026-04-30-report
2026-05-05-bluefin-spring
2026-05-12-bluefin-spring-2026
2026-05-13-bluefin-spring-2026-2
2026-05-14-making-our-own-fate
2026-05-15-hummingbird
2026-05-23-gradia-capture-bluefin
2026-05-28-knuckle
2026-05-31-report
2026-06-11-promotion-pr-body
AGENTS
AGENTS
AGENTS
AGENTS
AGENTS
AGENTS
Building a Custom Bluefin Image
CI_CD_GUIDE
CLAUDE
CLAUDE
CONTRIBUTING
CONTRIBUTING
CONTRIBUTING
CONTRIBUTING
CONTRIBUTING
FAQ
How can I install Double Commander on Bluefin (an immutable Linux OS), coming from Arch Linux?
How can you fix random GPU artifacts and hard freezes on an AMD Framework 16 laptop running Linux (Bluefin/Fedora)?
How do I install the Cloudflare WARP (One) client on Bluefin?
How to Reset a User's Home Directory to Defaults on Bluefin
INDEX
MAINTAINERS
MULTI_VARIANT_BUILD
PITFALLS
PULL_REQUEST_TEMPLATE
Plex Media Server
README
README
README
README
README
README
README
README
README
SECURITY
SECURITY
SECURITY
SKILL
SKILL
SKILL
SKILL
TRANSLATING
Update Process
Which Bluefin ISO is recommended for a Lenovo Yoga Pro 9i with an NVIDIA RTX 4050 for Python data science and Machine Learning with CUDA?
about-monthly-reports
action-reference
actionadon
add-package
administration
agentic-contributing
ai
analytics
architect
architecture
artwork
blog-poster.agent
bluefin-dx
bluefin-gdx
bonedigger-agent-donation
bonedigger-lifecycle
bonedigger-overview
bonedigger-templates
bonedigger-ujust
bst-overrides
build
build
buildstream
ci
ci
ci
code-of-conduct
command-line
composite-actions
consumer-guide
consumer-validation
contributing
contributors
conventional-commit.prompt
copilot-instructions
copilot-instructions
copilot-instructions
copilot-instructions
dakota-tpm2-validation
debugging
demo-mode-e2e
determinism
dinosaurs
downloads
downloads-testing
driver-versions
e2e-ci
e2e-feature-verification
encryption-matrix
factory-operations
failure-paths
feedback-loop
gstreamer-codec-validation
human-gates
images
index
index
installation
installer
introduction
knuckle
label-workflow
libpastry-integration
live-iso
local-ota
lore
lts
luks-testing
merge-queue
mission
multi-arch
music
not-bluefin
oci-assembly
oci-layers
onboarding
overview
packaging-binaries
packaging-gnome-extensions
packaging-go
packaging-rust
packaging-zig
patch-junctions
patches
pr-checklist
pr-review
press-kit
projects
pull_request_template
pull_request_template
pull_request_template
qr-phone-companion
quickstart
r2-promotion
release-qualification
remove-package
reports
reusable-workflow
skill-improvement
supply-chain
t2-mac
testing
tips
troubleshooting
ujust-recipes
update-refs
upgrade-and-migration
ux-ubuntu-provision
values
variants
vm-stack
workflow