diff --git a/.forgejo/workflows/iso.yml b/.forgejo/workflows/iso.yml index b735b26..2678221 100644 --- a/.forgejo/workflows/iso.yml +++ b/.forgejo/workflows/iso.yml @@ -6,7 +6,9 @@ name: Installer ISO # # Runs on a runner labelled `iso-builder`: bootc-image-builder needs an # x86_64 host with root podman (the job runs as root, or with sudo) and -# ~30 GB of free disk. +# ~30 GB of free disk. Stage 0 (the stage-0/ Aurora fork) also needs +# buildah, cpp, skopeo and jq from the runner's distro; just, yq and cosign +# are fetched below. # # Secrets: MINIO_ACCESS_KEY, MINIO_SECRET_KEY. # Variables (optional): MINIO_ENDPOINT (default https://s3.quineglobal.com), @@ -28,14 +30,20 @@ env: jobs: iso: runs-on: iso-builder - timeout-minutes: 180 + timeout-minutes: 300 steps: - uses: actions/checkout@v4 + with: + submodules: true - - name: Install task and mc + - name: Install task, mc, just, yq and cosign run: | mkdir -p "$HOME/.local/bin" curl -fsSL https://taskfile.dev/install.sh | sh -s -- -d -b "$HOME/.local/bin" + curl -fsSL https://just.systems/install.sh | bash -s -- --to "$HOME/.local/bin" + curl -fsSL -o "$HOME/.local/bin/yq" https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 + curl -fsSL -o "$HOME/.local/bin/cosign" https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 + chmod +x "$HOME/.local/bin/yq" "$HOME/.local/bin/cosign" curl -fsSL -o "$HOME/.local/bin/mc" https://dl.min.io/client/mc/release/linux-amd64/mc chmod +x "$HOME/.local/bin/mc" echo "$HOME/.local/bin" >> "$GITHUB_PATH" diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..de91d23 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "stage-0"] + path = stage-0 + url = https://github.com/ublue-os/aurora diff --git a/Containerfile b/Containerfile index 2c893f1..04f82c1 100644 --- a/Containerfile +++ b/Containerfile @@ -1,12 +1,17 @@ -ARG BASE_DIGEST=sha256:fbcef6075541eec7c2ca4a91c87c7012455cfba4e5e748d0cf2013d6aa3e66d3 +# Stage 1 of the build. BASE_IMAGE is stage 0: our Aurora fork, built from the +# stage-0/ submodule by `task build-base` into root's containers-storage. Its +# pin is the submodule commit. Pass e.g. +# --build-arg BASE_IMAGE=ghcr.io/ublue-os/aurora:stable to build on stock +# upstream Aurora instead. +ARG BASE_IMAGE=localhost/aurora:stable # --- Builds the out-of-tree snd-hda-codec-cs8409 module (iMac19,2 mic fix). --- # See docs/imac19-2-mic-fix.md and patches/snd-hda-codec-cs8409/ for the full # story. Kept as its own stage so the build toolchain never lands in the -# final image, and pinned to the same BASE_DIGEST as the final stage so the +# final image, and built FROM the same BASE_IMAGE as the final stage so the # kernel-devel headers installed here can never drift out of sync with the # kernel that actually ships below. -FROM ghcr.io/ublue-os/aurora@${BASE_DIGEST} AS kmod-builder +FROM ${BASE_IMAGE} AS kmod-builder RUN set -eux; \ NVR="$(rpm -q kernel-core --qf '%{VERSION}-%{RELEASE}.%{ARCH}')"; \ @@ -31,7 +36,7 @@ RUN set -eux; \ cp snd-hda-codec-cs8409.ko /output/ # --- Final image. --- -FROM ghcr.io/ublue-os/aurora@${BASE_DIGEST} AS final +FROM ${BASE_IMAGE} AS final COPY overlay/ / diff --git a/README.md b/README.md index e42ad75..f45bff1 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,35 @@ Quine distro of Linux based on uBlue Aurora ## Building -`Containerfile` builds a custom bootc image on top of `ghcr.io/ublue-os/aurora:stable`, -layering in everything under `overlay/`, plus a `kmod-builder` stage that -compiles the CS8409 mic-fix kernel module (see -[Hardware-specific patches](#hardware-specific-patches) below). +The image is built in two stages: + +- **Stage 0: `stage-0/`**, a git submodule holding our fork of + [Aurora](https://github.com/ublue-os/aurora). It's built with Aurora's own + `just build` into `localhost/aurora:stable`. Changes that have to happen + *inside* Aurora's build (its package set, build scripts, the Kinoite base + it starts from) go here, as commits on the fork; bump the submodule to ship + them. Needs a Linux host with buildah, cpp, just (>= 1.46), skopeo, jq, yq + and cosign. +- **Stage 1: `Containerfile`**, which builds the quine-linux bootc image on + top of stage 0, layering in everything under `overlay/`, plus a + `kmod-builder` stage that compiles the CS8409 mic-fix kernel module (see + [Hardware-specific patches](#hardware-specific-patches) below). Anything a + `RUN` step on top of Aurora can do belongs here, not in stage 0. + +Clone with `git clone --recurse-submodules`, or run +`git submodule update --init` in an existing checkout. [`Taskfile.yml`](Taskfile.yml) (requires [`task`](https://taskfile.dev)) is the supported way to drive all of this — run `task` with no arguments to list what's available. Notably: -- `task build-image` — builds the image locally, as root, with podman, or +- `task build-base` — builds stage 0. It's skipped when `stage-0/` is + unchanged and the image exists. Run `task build-base --force` to rebuild + anyway, e.g. to pick up upstream kernel and package updates, which Aurora's + build resolves at build time. +- `task build-image` — builds stage 0 if needed, then stage 1. To skip stage 0 + and build on stock upstream Aurora (the only option on macOS), pass + `BASE_IMAGE=ghcr.io/ublue-os/aurora:stable`. Builds locally, as root, with podman, or docker if podman isn't installed (force either with `CONTAINER_RUNTIME=docker|podman`). Every task below works with either; under docker, images are copied via skopeo into a @@ -37,7 +56,8 @@ list what's available. Notably: Equivalently, by hand: ```sh -podman build -t quine-linux -f Containerfile . +(cd stage-0 && sudo just build --image aurora --tag stable --flavor main) +sudo podman build -t quine-linux -f Containerfile . ``` An ISO can then be generated from the built image with `bootc-image-builder` diff --git a/Taskfile.yml b/Taskfile.yml index b3d7994..f289ac0 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -3,6 +3,16 @@ 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). @@ -34,17 +44,60 @@ tasks: - 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. + - >- + {{.SUDO}} env "PATH=$PATH" + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*' + ${GITHUB_TOKEN:+"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: >- - Build the quine-linux bootc container image, as root (under podman - this lands in root's containers-storage, which bootc and + 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: - - '{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.IMAGE}} -f Containerfile .' + - 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: >- diff --git a/stage-0 b/stage-0 new file mode 160000 index 0000000..6f5aa60 --- /dev/null +++ b/stage-0 @@ -0,0 +1 @@ +Subproject commit 6f5aa60aa5584f847a58953c96b047a8fc2d26c4