From 8ad5e45dc2e3389651427dc724253cfb6fad93c6 Mon Sep 17 00:00:00 2001 From: Philip Peterson <1326208+philip-peterson@users.noreply.github.com> Date: Sun, 27 Sep 2026 20:34:46 -0800 Subject: [PATCH] Build the installer ISO on Forgejo and upload it to MinIO .forgejo/workflows/iso.yml runs `task build-installer-iso` on a runner labelled iso-builder (x86_64, root podman) and uploads the ISO to MinIO under installer// and installer/latest/. For unattended runs, the Taskfile now only uses sudo when not already root (CI runners often lack it), and OUTPUT_DIR is absolute so the bind mount into bootc-image-builder doesn't depend on relative-path support. Co-Authored-By: Claude Opus 5.5 --- .forgejo/workflows/iso.yml | 61 ++++++++++++++++++++++++++++++++++++++ Taskfile.yml | 26 +++++++++------- 2 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 .forgejo/workflows/iso.yml diff --git a/.forgejo/workflows/iso.yml b/.forgejo/workflows/iso.yml new file mode 100644 index 0000000..b735b26 --- /dev/null +++ b/.forgejo/workflows/iso.yml @@ -0,0 +1,61 @@ +name: Installer ISO + +# Builds the installer ISO (`task build-installer-iso`) and uploads it to +# MinIO as /installer//install.iso and +# /installer/latest/install.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. +# +# Secrets: MINIO_ACCESS_KEY, MINIO_SECRET_KEY. +# Variables (optional): MINIO_ENDPOINT (default https://s3.quineglobal.com), +# MINIO_BUCKET (default quine-linux). + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: iso-${{ github.ref }} + cancel-in-progress: true + +env: + MINIO_ENDPOINT: ${{ vars.MINIO_ENDPOINT || 'https://s3.quineglobal.com' }} + MINIO_BUCKET: ${{ vars.MINIO_BUCKET || 'quine-linux' }} + +jobs: + iso: + runs-on: iso-builder + timeout-minutes: 180 + steps: + - uses: actions/checkout@v4 + + - name: Install task and mc + run: | + mkdir -p "$HOME/.local/bin" + curl -fsSL https://taskfile.dev/install.sh | sh -s -- -d -b "$HOME/.local/bin" + 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" + + - name: Build installer ISO + run: task build-installer-iso CONTAINER_RUNTIME=podman + + - name: Upload to MinIO + env: + MINIO_ACCESS_KEY: ${{ secrets.MINIO_ACCESS_KEY }} + MINIO_SECRET_KEY: ${{ secrets.MINIO_SECRET_KEY }} + run: | + iso=output/bootc-installer/install.iso + ls -lh "$iso" + mc alias set store "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" + mc mb --ignore-existing "store/${MINIO_BUCKET}" + dest="store/${MINIO_BUCKET}/installer" + mc cp "$iso" "${dest}/${GITHUB_SHA}/install.iso" + mc cp "${dest}/${GITHUB_SHA}/install.iso" "${dest}/latest/install.iso" + + - name: Clean up build output + if: always() + run: rm -rf output diff --git a/Taskfile.yml b/Taskfile.yml index 2bdb360..b3d7994 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -3,7 +3,11 @@ version: '3' vars: IMAGE: quine-linux INSTALLER_IMAGE: quine-installer - OUTPUT_DIR: ./output + 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: @@ -36,7 +40,7 @@ tasks: 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 . + - '{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.IMAGE}} -f Containerfile .' sources: - Containerfile - overlay/**/* @@ -63,7 +67,7 @@ tasks: vars: { IMAGES: "{{.IMAGE}}" } - mkdir -p {{.OUTPUT_DIR}} - >- - sudo {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} + {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} --security-opt label=type:unconfined_t -v {{.OUTPUT_DIR}}:/output -v {{.STORAGE}}:/var/lib/containers/storage @@ -79,7 +83,7 @@ tasks: 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/ + - '{{.SUDO}} {{.CONTAINER_RUNTIME}} build --platform {{.PLATFORM}} -t {{.INSTALLER_IMAGE}} -f installer/Containerfile installer/' build-installer-iso: desc: >- @@ -105,7 +109,7 @@ tasks: vars: { IMAGES: "{{.IMAGE}} {{.INSTALLER_IMAGE}}" } - mkdir -p {{.OUTPUT_DIR}} - >- - sudo {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} + {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged {{.PULL_FLAG}} --security-opt label=type:unconfined_t -v {{.OUTPUT_DIR}}:/output -v {{.STORAGE}}:/var/lib/containers/storage @@ -127,7 +131,7 @@ tasks: cmds: - for: { var: IMAGES } cmd: >- - sudo {{.CONTAINER_RUNTIME}} run --rm --privileged + {{.SUDO}} {{.CONTAINER_RUNTIME}} run --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock -v {{.STORAGE}}:/var/lib/containers/storage {{.SKOPEO_IMAGE}} @@ -149,10 +153,10 @@ tasks: 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 + - cmd: '{{.SUDO}} docker save {{.IMAGE}} | {{.SUDO}} podman load' platforms: [linux] if: '[ "{{.CONTAINER_RUNTIME}}" != podman ]' - - sudo bootc switch --transport containers-storage localhost/{{.IMAGE}} + - '{{.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." @@ -161,7 +165,7 @@ tasks: prompt: This will reboot this machine into the locally-built {{.IMAGE}} image. Continue? deps: [test:stage] cmds: - - sudo systemctl reboot + - '{{.SUDO}} systemctl reboot' revert: desc: >- @@ -171,5 +175,5 @@ tasks: 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 + - '{{.SUDO}} bootc rollback' + - '{{.SUDO}} systemctl reboot'