version: '3' vars: IMAGE: quine-linux INSTALLER_IMAGE: quine-installer # 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}}' 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' # 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 # 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 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}}' tasks: default: desc: List available tasks cmds: - task --list silent: true 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. # 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. - >- {{.SUDO}} env $([ "${GITHUB_SERVER_URL:-https://github.com}" = https://github.com ] || echo -u GITHUB_TOKEN) "PATH=$PATH" GITHUB_REPOSITORY_OWNER=ublue-os GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*' $([ "${GITHUB_SERVER_URL:-https://github.com}" = https://github.com ] && [ -n "${GITHUB_TOKEN:-}" ] && echo "GITHUB_TOKEN=$GITHUB_TOKEN") 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' build-image: desc: >- 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 bootc-image-builder read; under docker, the ISO tasks copy it over). cmds: - task: build-base if: '[ "{{.BASE_IMAGE}}" = "{{.STAGE0_IMAGE}}" ]' - >- {{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} --build-arg BASE_IMAGE={{.BASE_IMAGE}} -t {{.IMAGE}} -f Containerfile . sources: - Containerfile - overlay/**/* - patches/**/* - stage-0/**/* 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. 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. cmds: # Not a dep: deps run before preconditions, and this should fail fast. - task: build-image - task: export-to-storage vars: { IMAGES: "{{.IMAGE}}" } - mkdir -p {{.OUTPUT_DIR}} - >- {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} --security-opt label=type:unconfined_t -v {{.OUTPUT_DIR}}:/output -v {{.STORAGE}}:/var/lib/containers/storage {{.BIB_IMAGE}} --type iso localhost/{{.IMAGE}} generates: - "{{.OUTPUT_DIR}}/bootiso/install.iso" 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 anaconda-webui built from installer/anaconda-webui/vendor/. cmds: - '{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.INSTALLER_IMAGE}} -f installer/Containerfile installer/' 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 {{.OUTPUT_DIR}}/bootc-installer/install.iso. 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. cmds: # Not a dep: deps run before preconditions, and this should fail fast. - task: build-image - task: build-installer-image - task: export-to-storage vars: { IMAGES: "{{.IMAGE}} {{.INSTALLER_IMAGE}}" } - mkdir -p {{.OUTPUT_DIR}} - >- {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} --security-opt label=type:unconfined_t -v {{.OUTPUT_DIR}}:/output -v {{.STORAGE}}:/var/lib/containers/storage {{.BIB_IMAGE}} --type bootc-installer --installer-payload-ref localhost/{{.IMAGE}} localhost/{{.INSTALLER_IMAGE}} generates: - "{{.OUTPUT_DIR}}/bootc-installer/install.iso" 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: >- {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged -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 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: # 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. - cmd: '{{.SUDO}} docker save {{.IMAGE}} | {{.SUDO}} podman load' platforms: [linux] if: '[ "{{.CONTAINER_RUNTIME}}" != podman ]' - '{{.SUDO}} bootc switch --transport containers-storage localhost/{{.IMAGE}}' - 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: - '{{.SUDO}} systemctl reboot' 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: - '{{.SUDO}} bootc rollback' - '{{.SUDO}} systemctl reboot'