diff --git a/.forgejo/akmods-arm64.sh b/.forgejo/akmods-arm64.sh new file mode 100755 index 00000000..10031fac --- /dev/null +++ b/.forgejo/akmods-arm64.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Stands in for ghcr.io/ublue-os/akmods on arm64, which upstream doesn't +# build: an image with the akmods image's layout (/kernel-rpms, /rpms/common, +# /rpms/kmods), holding Fedora's own aarch64 kernel from Koji and no extra +# kmods (so no xone or v4l2loopback on arm64). Pushed where the runner's +# registries.conf looks first for ghcr.io/ublue-os/akmods (the mirror), so +# Containerfile.in's `FROM ghcr.io/ublue-os/akmods:...` finds it. +# +# Usage: akmods-arm64.sh FLAVOR FEDORA KERNEL +# e.g. akmods-arm64.sh main 45 7.2.14-300.fc45.aarch64 +# Needs a `podman login` to the forge first. +set -euo pipefail + +flavor=$1 fedora=$2 kernel=$3 +mirror=${MIRROR:-forge.quinefoundation.com/quine-global-labs/mirror} +ref="$mirror/ghcr.io/ublue-os/akmods:$flavor-$fedora-$kernel" + +nvr=${kernel%.aarch64} # 7.2.14-300.fc45 +version=${nvr%%-*} # 7.2.14 +release=${nvr#*-} # 300.fc45 +koji="https://kojipkgs.fedoraproject.org/packages/kernel/$version/$release/aarch64" + +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT +mkdir -p "$work/kernel-rpms" "$work/rpms/common" "$work/rpms/kmods" +for pkg in kernel kernel-core kernel-modules kernel-modules-core kernel-modules-extra; do + curl -fsSL --retry 5 -o "$work/kernel-rpms/$pkg-$nvr.aarch64.rpm" "$koji/$pkg-$nvr.aarch64.rpm" +done +ls -l "$work/kernel-rpms" +# Empty directories, so the bind mounts of /rpms/* have something to mount. +touch "$work/rpms/common/.keep" "$work/rpms/kmods/.keep" + +ctr=$(buildah from --arch arm64 --os linux scratch) +trap 'buildah rm "$ctr" >/dev/null; rm -rf "$work"' EXIT +buildah copy "$ctr" "$work/kernel-rpms" /kernel-rpms +buildah copy "$ctr" "$work/rpms" /rpms +buildah config --arch arm64 --os linux --label "ostree.linux=$kernel" "$ctr" +buildah commit --rm "$ctr" "$ref" +trap 'rm -rf "$work"' EXIT +podman push --retry 5 "$ref" +echo "pushed $ref" diff --git a/.forgejo/buildah-arm64 b/.forgejo/buildah-arm64 new file mode 100755 index 00000000..37bd40c9 --- /dev/null +++ b/.forgejo/buildah-arm64 @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# `buildah`, but `buildah build` builds for linux/arm64: build-arm64.yml runs +# `just BUILDAH=.../buildah-arm64 build`, so the Justfile stays upstream's. +# On runner-x86-linux the arm64 steps run under QEMU user emulation. +if [[ ${1:-} == build ]]; then + shift + exec buildah build --platform linux/arm64 "$@" +fi +exec buildah "$@" diff --git a/.forgejo/vendor-images.sh b/.forgejo/vendor-images.sh index 92d698d2..6c1ef7c0 100755 --- a/.forgejo/vendor-images.sh +++ b/.forgejo/vendor-images.sh @@ -20,7 +20,8 @@ export CONTAINERS_REGISTRIES_CONF=/dev/null for ref in "$@"; do src="docker://$ref" dest="docker://$mirror/$ref" - opts=(--multi-arch system) + # Every architecture: build-arm64.yml pulls the arm64 halves. + opts=(--multi-arch all) if [[ $ref == *@sha256:* ]]; then # Pinned by digest: copy the whole index unchanged, so the digest the # build asks for exists on the mirror. skopeo refuses a reference with diff --git a/.forgejo/workflows/build-arm64.yml b/.forgejo/workflows/build-arm64.yml new file mode 100644 index 00000000..2111b061 --- /dev/null +++ b/.forgejo/workflows/build-arm64.yml @@ -0,0 +1,101 @@ +name: Build Aurora (arm64) + +# Builds Aurora for arm64 from this tree and pushes it as +# forge.quinefoundation.com/quine-global-labs/aurora:stable-arm64 (plus +# stable-arm64-). quine-global-labs/quine-linux's arm64 build is FROM +# it. A separate tag, not part of `stable`: build.yml pushes `stable` (amd64) +# daily and would drop an arm64 half each time. +# +# Upstream doesn't ship arm64 Aurora yet, and has no arm64 akmods, which is +# also where Aurora's kernel comes from. So this build uses Fedora's own +# aarch64 kernel, the one Kinoite ships, from a stand-in akmods image +# (.forgejo/akmods-arm64.sh), with no extra kmods. +# +# There's no arm64 runner: runner-x86-linux runs it under QEMU user +# emulation (petersweb-infra, hosts/runner-x86-linux), many times slower +# than native, hence weekly and a long timeout. It builds this repo's main +# as build.yml last merged it, without merging upstream itself. +# +# Secret: REGISTRY_TOKEN, as build.yml. Variable (optional): REGISTRY_USER. +on: + schedule: + - cron: '0 8 * * 6' + workflow_dispatch: + +concurrency: + group: aurora-arm64-${{ github.ref }} + cancel-in-progress: true + +env: + REGISTRY: forge.quinefoundation.com + MIRRORS: https://forge.quinefoundation.com/quine-global-labs + IMAGE: forge.quinefoundation.com/quine-global-labs/aurora + TAG: stable + +jobs: + build: + runs-on: runner-x86-linux + timeout-minutes: 1440 + steps: + - uses: https://forge.quinefoundation.com/quine-global-labs/mirror-actions-checkout@v4 + with: + fetch-depth: 0 + + - name: Point downloads at our mirrors + # As build.yml. + run: | + files=$(grep -rlE 'github(usercontent)?\.com/(ublue-os/(akmods|countme)|rcaloras/bash-preexec|coreos/fedora-coreos-config)/' build_scripts) + sed -i -E \ + -e "s#https://github\.com/ublue-os/akmods/raw/refs/heads/([^/]+)/#$MIRRORS/mirror-ublue-os-akmods/raw/branch/\1/#g" \ + -e "s#https://raw\.githubusercontent\.com/ublue-os/countme/([^/]+)/#$MIRRORS/mirror-ublue-os-countme/raw/branch/\1/#g" \ + -e "s#https://raw\.githubusercontent\.com/rcaloras/bash-preexec/([0-9a-f]{40})/#$MIRRORS/mirror-rcaloras-bash-preexec/raw/commit/\1/#g" \ + -e "s#https://raw\.githubusercontent\.com/coreos/fedora-coreos-config/refs/heads/([^/]+)/#$MIRRORS/mirror-coreos-fedora-coreos-config/raw/branch/\1/#g" \ + $files + git update-index --skip-worktree $files + + - name: Log in to the registry + env: + REGISTRY_USER: ${{ vars.REGISTRY_USER || github.actor }} + REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: printf %s "$REGISTRY_TOKEN" | podman login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin + + - name: Vendor upstream images, and the arm64 akmods stand-in + run: | + org=$(just --evaluate base_image_org) + name=$(just --evaluate base_image_name) + ver=$(just fedora_version --image aurora --tag "$TAG" --flavor main) + flavor=$(just akmods_flavor --tag "$TAG") + mapfile -t pinned < <(yq -r '.images[] | select(.name != "kinoite") | "\(.image):\(.tag)@\(.digest)"' image-versions.yml) + .forgejo/vendor-images.sh "$org/$name:$ver" "${pinned[@]}" + # The kernel Kinoite's arm64 image ships. + kernel=$(CONTAINERS_REGISTRIES_CONF=/dev/null skopeo inspect --override-arch arm64 --override-os linux \ + --no-tags --retry-times 3 "docker://$org/$name:$ver" | jq -er '.Labels["ostree.linux"]') + echo "kernel: $kernel" + echo "KERNEL=$kernel" >>"$GITHUB_ENV" + .forgejo/akmods-arm64.sh "$flavor" "$ver" "$kernel" + + - name: Build + # As build.yml, through a buildah that builds for linux/arm64, with + # the kernel pinned to the stand-in's. + run: >- + env -u GITHUB_TOKEN + GITHUB_REPOSITORY_OWNER=ublue-os + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=safe.directory GIT_CONFIG_VALUE_0='*' + just BUILDAH="$PWD/.forgejo/buildah-arm64" + build --image aurora --tag "$TAG" --flavor main --kernel-pin "$KERNEL" + + - name: Push + run: | + podman image inspect --format '{{.Architecture}}' "localhost/aurora:$TAG" + for t in "$TAG-arm64" "$TAG-arm64-$(date -u +%Y%m%d)"; do + podman tag "localhost/aurora:$TAG" "$IMAGE:$t" + podman push --retry 5 "$IMAGE:$t" + done + + - name: Clean up + if: always() + # The runner's hourly sweep keeps a day of images; this build's are + # done with now. + run: | + podman logout "$REGISTRY" || true + podman image prune -af >/dev/null || true diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 84523077..5052b433 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -146,6 +146,11 @@ jobs: [ -n "$fedora" ] || { echo "::warning::no dnf cache under /var/tmp/buildah-cache-0"; exit 0; } .forgejo/upload-rpms.sh "$(dirname "$fedora")" - - name: Log out + - name: Clean up if: always() - run: podman logout "$REGISTRY" || true + # The runner's hourly sweep keeps a day of images; this build's are + # done with now (quine-linux pulls the pushed image from the forge). + # `image prune` leaves the dnf cache the RPM upload reads. + run: | + podman logout "$REGISTRY" || true + podman image prune -af >/dev/null || true diff --git a/build_scripts/base/02-install-common-kernel-akmods.sh b/build_scripts/base/02-install-common-kernel-akmods.sh index 72bbe9ce..24aac0df 100755 --- a/build_scripts/base/02-install-common-kernel-akmods.sh +++ b/build_scripts/base/02-install-common-kernel-akmods.sh @@ -20,6 +20,9 @@ KERNEL_PKGS=( /tmp/kernel-rpms/kernel-modules-*.rpm ) +# Quine: arm64's stand-in akmods image (.forgejo/akmods-arm64.sh) has no +# kmods; without nullglob an unmatched pattern stays literal and fails dnf. +shopt -s nullglob KMODS=( /tmp/rpms/{common,kmods}/*xone*.rpm /tmp/rpms/{common,kmods}/*v4l2loopback*.rpm