2026-09-26 01:25:14 -08:00
|
|
|
version: '3'
|
|
|
|
|
|
|
|
|
|
vars:
|
|
|
|
|
IMAGE: quine-linux
|
2026-09-26 03:11:10 -08:00
|
|
|
INSTALLER_IMAGE: quine-installer
|
2026-09-28 00:47:44 -08:00
|
|
|
# Stage 0: our Aurora fork (the stage-0/ submodule), built with its own
|
|
|
|
|
# Justfile. AURORA_TAG picks the stream (stable/latest/testing) and is also
|
|
|
|
|
# the local tag the image gets.
|
|
|
|
|
AURORA_TAG: stable
|
|
|
|
|
STAGE0_IMAGE: localhost/aurora:{{.AURORA_TAG}}
|
|
|
|
|
# What stage 1 (Containerfile) builds FROM. Defaults to stage 0; override to
|
|
|
|
|
# skip stage 0 and build on a prebuilt image, e.g.
|
|
|
|
|
# `task build-image BASE_IMAGE=ghcr.io/ublue-os/aurora:stable` (the only
|
|
|
|
|
# option on macOS, where stage 0's buildah build can't run).
|
|
|
|
|
BASE_IMAGE: '{{.STAGE0_IMAGE}}'
|
2026-09-27 20:34:46 -08:00
|
|
|
OUTPUT_DIR: '{{.ROOT_DIR}}/output'
|
|
|
|
|
# Root is needed for the container builds and bootc; skip {{.SUDO}} when already
|
|
|
|
|
# root (e.g. on a CI runner, which may not have {{.SUDO}} at all).
|
|
|
|
|
SUDO:
|
|
|
|
|
sh: '[ "$(id -u)" = 0 ] || echo sudo'
|
2026-09-27 02:41:52 -08:00
|
|
|
# Any docker-CLI-compatible runtime works for plain builds. Prefers podman,
|
|
|
|
|
# falls back to docker; override with CONTAINER_RUNTIME=... (env or task var).
|
|
|
|
|
CONTAINER_RUNTIME:
|
|
|
|
|
sh: echo "${CONTAINER_RUNTIME:-$(command -v podman >/dev/null 2>&1 && echo podman || echo docker)}"
|
|
|
|
|
# bootc-image-builder now lives in github.com/osbuild/image-builder and is
|
|
|
|
|
# published here; quay.io/centos-bootc/bootc-image-builder is frozen.
|
|
|
|
|
BIB_IMAGE: ghcr.io/osbuild/bootc-image-builder:latest
|
2026-09-27 02:45:36 -08:00
|
|
|
# Aurora is amd64-only (and the kmod stage hardcodes x86_64), so pin it
|
|
|
|
|
# explicitly; otherwise an arm64 host (e.g. Apple Silicon) builds for arm64.
|
|
|
|
|
PLATFORM: linux/amd64
|
|
|
|
|
TARGET_ARCH: amd64
|
2026-09-27 02:41:52 -08:00
|
|
|
SKOPEO_IMAGE: quay.io/skopeo/stable:latest
|
|
|
|
|
# bootc-image-builder reads images from a containers-storage mounted at
|
|
|
|
|
# /var/lib/containers/storage. Under podman that's root's real store; docker
|
|
|
|
|
# has no such store, so images get copied (via skopeo) into this volume.
|
|
|
|
|
DOCKER_STORAGE_VOLUME: quine-containers-storage
|
|
|
|
|
STORAGE: '{{if eq .CONTAINER_RUNTIME "podman"}}/var/lib/containers/storage{{else}}{{.DOCKER_STORAGE_VOLUME}}{{end}}'
|
|
|
|
|
PULL_FLAG: '{{if eq .CONTAINER_RUNTIME "podman"}}--pull=newer{{else}}--pull=always{{end}}'
|
2026-09-26 01:25:14 -08:00
|
|
|
|
|
|
|
|
tasks:
|
|
|
|
|
default:
|
|
|
|
|
desc: List available tasks
|
|
|
|
|
cmds:
|
|
|
|
|
- task --list
|
|
|
|
|
silent: true
|
|
|
|
|
|
2026-09-28 00:47:44 -08:00
|
|
|
build-base:
|
|
|
|
|
desc: >-
|
|
|
|
|
Stage 0: build our Aurora fork from the stage-0/ submodule with its own
|
|
|
|
|
`just build`, as root, into {{.STAGE0_IMAGE}}. Needs a Linux host with
|
|
|
|
|
buildah, cpp, just (>= 1.46), skopeo, jq, yq and cosign. Skipped when
|
|
|
|
|
stage-0/ is unchanged and the image exists; use --force to rebuild
|
|
|
|
|
anyway (e.g. to pick up upstream kernel and package updates, which
|
|
|
|
|
Aurora's build resolves at build time).
|
|
|
|
|
preconditions:
|
|
|
|
|
- sh: '[ "$(uname -s)" = Linux ]'
|
|
|
|
|
msg: >-
|
|
|
|
|
Stage 0 (Aurora's own buildah build) only runs on Linux. To build
|
|
|
|
|
on stock upstream Aurora instead, pass
|
|
|
|
|
BASE_IMAGE=ghcr.io/ublue-os/aurora:{{.AURORA_TAG}}.
|
|
|
|
|
- sh: 'for t in buildah cpp just skopeo jq yq cosign; do command -v "$t" >/dev/null || { echo "missing: $t"; exit 1; }; done'
|
|
|
|
|
msg: 'Stage 0 needs buildah, cpp, just (>= 1.46), skopeo, jq, yq and cosign on PATH.'
|
|
|
|
|
dir: stage-0
|
|
|
|
|
cmds:
|
|
|
|
|
# sudo resets PATH (just/yq/cosign often live in ~/.local/bin or brew),
|
|
|
|
|
# and git refuses to read a repo owned by another user unless told it's
|
|
|
|
|
# safe; Aurora's Justfile runs `git status` / `git rev-parse` here.
|
2026-10-30 04:14:12 -08:00
|
|
|
# Aurora reads its own past tags from ghcr.io/$GITHUB_REPOSITORY_OWNER,
|
|
|
|
|
# and hands $GITHUB_TOKEN to GitHub's API. On Forgejo Actions those are
|
|
|
|
|
# this org and a Forgejo token, so: always ublue-os, and the token only
|
|
|
|
|
# when the job runs on GitHub.
|
2026-09-28 00:47:44 -08:00
|
|
|
- >-
|
2026-10-30 04:14:12 -08:00
|
|
|
{{.SUDO}} env
|
|
|
|
|
$([ "${GITHUB_SERVER_URL:-https://github.com}" = https://github.com ] || echo -u GITHUB_TOKEN)
|
|
|
|
|
"PATH=$PATH" GITHUB_REPOSITORY_OWNER=ublue-os
|
2026-09-28 00:47:44 -08:00
|
|
|
GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*'
|
2026-10-30 04:14:12 -08:00
|
|
|
$([ "${GITHUB_SERVER_URL:-https://github.com}" = https://github.com ] && [ -n "${GITHUB_TOKEN:-}" ] && echo "GITHUB_TOKEN=$GITHUB_TOKEN")
|
2026-09-28 00:47:44 -08:00
|
|
|
just build --image aurora --tag {{.AURORA_TAG}} --flavor main
|
|
|
|
|
# buildah builds into root's containers-storage; docker needs it copied
|
|
|
|
|
# into its daemon for stage 1 to build FROM it.
|
|
|
|
|
- cmd: '{{.SUDO}} skopeo copy containers-storage:{{.STAGE0_IMAGE}} docker-daemon:{{.STAGE0_IMAGE}}'
|
|
|
|
|
if: '[ "{{.CONTAINER_RUNTIME}}" != podman ]'
|
|
|
|
|
# Relative to dir (stage-0/): the fork's whole checked-out tree.
|
|
|
|
|
sources:
|
|
|
|
|
- '**/*'
|
|
|
|
|
status:
|
|
|
|
|
- '{{.SUDO}} {{.CONTAINER_RUNTIME}} image inspect {{.STAGE0_IMAGE}} >/dev/null 2>&1'
|
|
|
|
|
|
2026-09-26 01:25:14 -08:00
|
|
|
build-image:
|
|
|
|
|
desc: >-
|
2026-09-28 00:47:44 -08:00
|
|
|
Stage 1: build the quine-linux bootc container image on top of
|
|
|
|
|
BASE_IMAGE (stage 0 by default, built first if needed), as root
|
|
|
|
|
(under podman this lands in root's containers-storage, which bootc and
|
2026-09-27 02:41:52 -08:00
|
|
|
bootc-image-builder read; under docker, the ISO tasks copy it over).
|
2026-09-26 01:25:14 -08:00
|
|
|
cmds:
|
2026-09-28 00:47:44 -08:00
|
|
|
- task: build-base
|
|
|
|
|
if: '[ "{{.BASE_IMAGE}}" = "{{.STAGE0_IMAGE}}" ]'
|
|
|
|
|
- >-
|
|
|
|
|
{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}}
|
|
|
|
|
--build-arg BASE_IMAGE={{.BASE_IMAGE}}
|
|
|
|
|
-t {{.IMAGE}} -f Containerfile .
|
2026-09-26 01:25:14 -08:00
|
|
|
sources:
|
|
|
|
|
- Containerfile
|
|
|
|
|
- overlay/**/*
|
|
|
|
|
- patches/**/*
|
2026-09-28 00:47:44 -08:00
|
|
|
- stage-0/**/*
|
2026-09-26 01:25:14 -08:00
|
|
|
|
|
|
|
|
build-iso:
|
|
|
|
|
desc: >-
|
|
|
|
|
Build a bootable install ISO from the local quine-linux image, via
|
|
|
|
|
bootc-image-builder. Output lands in {{.OUTPUT_DIR}}/bootiso/install.iso.
|
2026-09-27 06:27:25 -08:00
|
|
|
preconditions:
|
|
|
|
|
- sh: |
|
|
|
|
|
case "$(uname -m)" in x86_64|amd64) a=amd64 ;; aarch64|arm64) a=arm64 ;; *) a="$(uname -m)" ;; esac
|
|
|
|
|
[ "$a" = "{{.TARGET_ARCH}}" ]
|
|
|
|
|
msg: >-
|
|
|
|
|
ISO builds need a host of architecture {{.TARGET_ARCH}}. This one is
|
|
|
|
|
{{ARCH}}, and bootc-image-builder's cross-arch mode fails partway
|
|
|
|
|
(it runs the image for inspection, which needs emulation inside
|
|
|
|
|
its own container). Build the ISO on an x86_64 machine, e.g. the
|
|
|
|
|
Aurora box.
|
2026-09-26 01:25:14 -08:00
|
|
|
cmds:
|
2026-09-27 06:27:25 -08:00
|
|
|
# Not a dep: deps run before preconditions, and this should fail fast.
|
|
|
|
|
- task: build-image
|
2026-09-27 02:41:52 -08:00
|
|
|
- task: export-to-storage
|
|
|
|
|
vars: { IMAGES: "{{.IMAGE}}" }
|
2026-09-26 01:25:14 -08:00
|
|
|
- mkdir -p {{.OUTPUT_DIR}}
|
|
|
|
|
- >-
|
2026-09-27 20:34:46 -08:00
|
|
|
{{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}}
|
2026-09-26 01:25:14 -08:00
|
|
|
--security-opt label=type:unconfined_t
|
|
|
|
|
-v {{.OUTPUT_DIR}}:/output
|
2026-09-27 02:41:52 -08:00
|
|
|
-v {{.STORAGE}}:/var/lib/containers/storage
|
|
|
|
|
{{.BIB_IMAGE}}
|
|
|
|
|
--type iso localhost/{{.IMAGE}}
|
2026-09-26 01:25:14 -08:00
|
|
|
generates:
|
|
|
|
|
- "{{.OUTPUT_DIR}}/bootiso/install.iso"
|
|
|
|
|
|
2026-09-26 03:11:10 -08:00
|
|
|
build-installer-image:
|
|
|
|
|
desc: >-
|
|
|
|
|
Build the patched-Anaconda installer-environment container (see
|
|
|
|
|
installer/Containerfile and docs/patched-anaconda-installer.md). This
|
|
|
|
|
is separate from the quine-linux OS image itself. Defaults to the
|
2026-09-27 05:56:26 -08:00
|
|
|
anaconda-webui built from installer/anaconda-webui/vendor/.
|
2026-09-26 03:11:10 -08:00
|
|
|
cmds:
|
2026-09-27 20:34:46 -08:00
|
|
|
- '{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.INSTALLER_IMAGE}} -f installer/Containerfile installer/'
|
2026-09-26 03:11:10 -08:00
|
|
|
|
|
|
|
|
build-installer-iso:
|
|
|
|
|
desc: >-
|
|
|
|
|
Build a bootable install ISO using the patched-Anaconda installer
|
|
|
|
|
image to install the quine-linux bootc image, via
|
|
|
|
|
bootc-image-builder's `bootc-installer` type. Output lands in
|
2026-09-27 05:56:26 -08:00
|
|
|
{{.OUTPUT_DIR}}/bootc-installer/install.iso.
|
2026-09-27 06:27:25 -08:00
|
|
|
preconditions:
|
|
|
|
|
- sh: |
|
|
|
|
|
case "$(uname -m)" in x86_64|amd64) a=amd64 ;; aarch64|arm64) a=arm64 ;; *) a="$(uname -m)" ;; esac
|
|
|
|
|
[ "$a" = "{{.TARGET_ARCH}}" ]
|
|
|
|
|
msg: >-
|
|
|
|
|
ISO builds need a host of architecture {{.TARGET_ARCH}}. This one is
|
|
|
|
|
{{ARCH}}, and bootc-image-builder's cross-arch mode fails partway
|
|
|
|
|
(it runs the image for inspection, which needs emulation inside
|
|
|
|
|
its own container). Build the ISO on an x86_64 machine, e.g. the
|
|
|
|
|
Aurora box.
|
2026-09-26 03:11:10 -08:00
|
|
|
cmds:
|
2026-09-27 06:27:25 -08:00
|
|
|
# Not a dep: deps run before preconditions, and this should fail fast.
|
|
|
|
|
- task: build-image
|
2026-09-26 03:11:10 -08:00
|
|
|
- task: build-installer-image
|
2026-09-27 02:41:52 -08:00
|
|
|
- task: export-to-storage
|
|
|
|
|
vars: { IMAGES: "{{.IMAGE}} {{.INSTALLER_IMAGE}}" }
|
2026-09-26 03:11:10 -08:00
|
|
|
- mkdir -p {{.OUTPUT_DIR}}
|
2026-10-30 04:46:25 -08:00
|
|
|
# --rootfs: bootc-image-builder takes the default root filesystem from
|
|
|
|
|
# the installer image, and fedora-bootc:rawhide declares none ("missing
|
|
|
|
|
# required info: DefaultRootFs"). btrfs is what the payload declares
|
|
|
|
|
# (Aurora's /usr/lib/bootc/install/20-aurora.toml).
|
2026-09-26 03:11:10 -08:00
|
|
|
- >-
|
2026-09-27 20:34:46 -08:00
|
|
|
{{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}}
|
2026-09-26 03:11:10 -08:00
|
|
|
--security-opt label=type:unconfined_t
|
|
|
|
|
-v {{.OUTPUT_DIR}}:/output
|
2026-09-27 02:41:52 -08:00
|
|
|
-v {{.STORAGE}}:/var/lib/containers/storage
|
|
|
|
|
{{.BIB_IMAGE}}
|
2026-09-26 03:11:10 -08:00
|
|
|
--type bootc-installer
|
2026-10-30 04:46:25 -08:00
|
|
|
--rootfs btrfs
|
2026-09-27 05:56:26 -08:00
|
|
|
--installer-payload-ref localhost/{{.IMAGE}}
|
2026-09-26 03:11:10 -08:00
|
|
|
localhost/{{.INSTALLER_IMAGE}}
|
|
|
|
|
generates:
|
|
|
|
|
- "{{.OUTPUT_DIR}}/bootc-installer/install.iso"
|
|
|
|
|
|
2026-09-27 02:41:52 -08:00
|
|
|
export-to-storage:
|
|
|
|
|
internal: true
|
|
|
|
|
desc: >-
|
|
|
|
|
Under docker, copy IMAGES (space-separated local tags) from the docker
|
|
|
|
|
daemon into the containers-storage volume bootc-image-builder reads.
|
|
|
|
|
No-op under podman, which builds straight into containers-storage.
|
|
|
|
|
status:
|
|
|
|
|
- '[ "{{.CONTAINER_RUNTIME}}" = podman ]'
|
|
|
|
|
cmds:
|
|
|
|
|
- for: { var: IMAGES }
|
|
|
|
|
cmd: >-
|
2026-09-27 20:34:46 -08:00
|
|
|
{{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged
|
2026-09-27 02:41:52 -08:00
|
|
|
-v /var/run/docker.sock:/var/run/docker.sock
|
|
|
|
|
-v {{.STORAGE}}:/var/lib/containers/storage
|
|
|
|
|
{{.SKOPEO_IMAGE}}
|
|
|
|
|
copy docker-daemon:{{.ITEM}}:latest containers-storage:localhost/{{.ITEM}}:latest
|
|
|
|
|
|
2026-09-26 01:25:14 -08:00
|
|
|
status:
|
|
|
|
|
desc: Show current bootc deployment status (what's booted, what's staged/rollback).
|
|
|
|
|
cmds:
|
|
|
|
|
- bootc status
|
|
|
|
|
|
|
|
|
|
test:stage:
|
|
|
|
|
desc: >-
|
|
|
|
|
Build the local image and stage it as the next-boot target on THIS
|
|
|
|
|
machine (bootc switch, no reboot yet). This is the fast way to test a
|
|
|
|
|
change -- especially the CS8409 mic-fix kernel module -- against the
|
|
|
|
|
real running system without building an ISO or reinstalling anything.
|
|
|
|
|
Reboot separately when ready: `task test:boot`, or `sudo systemctl reboot`.
|
|
|
|
|
deps: [build-image]
|
|
|
|
|
cmds:
|
2026-09-27 02:41:52 -08:00
|
|
|
# bootc reads the host's own containers-storage, i.e. podman's (which
|
|
|
|
|
# every bootc host ships), so a docker-built image is loaded into it.
|
2026-09-27 20:34:46 -08:00
|
|
|
- cmd: '{{.SUDO}} docker save {{.IMAGE}} | {{.SUDO}} podman load'
|
2026-09-27 02:41:52 -08:00
|
|
|
platforms: [linux]
|
|
|
|
|
if: '[ "{{.CONTAINER_RUNTIME}}" != podman ]'
|
2026-09-27 20:34:46 -08:00
|
|
|
- '{{.SUDO}} bootc switch --transport containers-storage localhost/{{.IMAGE}}'
|
2026-09-26 01:25:14 -08:00
|
|
|
- bootc status --booted
|
|
|
|
|
- echo "Staged localhost/{{.IMAGE}} for next boot. Run task test:boot when ready to reboot into it."
|
|
|
|
|
|
|
|
|
|
test:boot:
|
|
|
|
|
desc: Stage the local image (if not already) and reboot into it now.
|
|
|
|
|
prompt: This will reboot this machine into the locally-built {{.IMAGE}} image. Continue?
|
|
|
|
|
deps: [test:stage]
|
|
|
|
|
cmds:
|
2026-09-27 20:34:46 -08:00
|
|
|
- '{{.SUDO}} systemctl reboot'
|
2026-09-26 01:25:14 -08:00
|
|
|
|
|
|
|
|
revert:
|
|
|
|
|
desc: >-
|
|
|
|
|
Roll back to the previously-booted deployment and reboot into it --
|
|
|
|
|
undoes a `test:boot` switch (or any other deployment change) if
|
|
|
|
|
something's wrong. Safe even if nothing was ever switched: bootc/
|
|
|
|
|
rpm-ostree always keep the prior deployment around for exactly this.
|
|
|
|
|
prompt: This will reboot this machine back into the previous deployment. Continue?
|
|
|
|
|
cmds:
|
2026-09-27 20:34:46 -08:00
|
|
|
- '{{.SUDO}} bootc rollback'
|
|
|
|
|
- '{{.SUDO}} systemctl reboot'
|