arm64: a weekly emulated build, as aurora:stable-arm64
All checks were successful
Build Aurora / build (push) Successful in 8m22s

Upstream has no arm64 Aurora or akmods. A stand-in akmods image carries
Fedora's own aarch64 kernel (the one Kinoite ships) and no extra kmods;
buildah-arm64 makes `just build` target linux/arm64. Vendoring now copies
every architecture, and both builds prune their images when done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Philip Peterson 2026-11-02 18:04:15 -09:00
parent a30c1130a0
commit e06d965f7d
6 changed files with 163 additions and 3 deletions

41
.forgejo/akmods-arm64.sh Executable file
View file

@ -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"

9
.forgejo/buildah-arm64 Executable file
View file

@ -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 "$@"

View file

@ -20,7 +20,8 @@ export CONTAINERS_REGISTRIES_CONF=/dev/null
for ref in "$@"; do for ref in "$@"; do
src="docker://$ref" src="docker://$ref"
dest="docker://$mirror/$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 if [[ $ref == *@sha256:* ]]; then
# Pinned by digest: copy the whole index unchanged, so the digest the # Pinned by digest: copy the whole index unchanged, so the digest the
# build asks for exists on the mirror. skopeo refuses a reference with # build asks for exists on the mirror. skopeo refuses a reference with

View file

@ -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-<date>). 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

View file

@ -146,6 +146,11 @@ jobs:
[ -n "$fedora" ] || { echo "::warning::no dnf cache under /var/tmp/buildah-cache-0"; exit 0; } [ -n "$fedora" ] || { echo "::warning::no dnf cache under /var/tmp/buildah-cache-0"; exit 0; }
.forgejo/upload-rpms.sh "$(dirname "$fedora")" .forgejo/upload-rpms.sh "$(dirname "$fedora")"
- name: Log out - name: Clean up
if: always() 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

View file

@ -20,6 +20,9 @@ KERNEL_PKGS=(
/tmp/kernel-rpms/kernel-modules-*.rpm /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=( KMODS=(
/tmp/rpms/{common,kmods}/*xone*.rpm /tmp/rpms/{common,kmods}/*xone*.rpm
/tmp/rpms/{common,kmods}/*v4l2loopback*.rpm /tmp/rpms/{common,kmods}/*v4l2loopback*.rpm