Files
c630/.gitea/workflows/build-image.yaml
rob thijssen a5f492d115
All checks were successful
build image / build (push) Successful in 24m3s
ci: upload artifacts with v3 — v4 cannot talk to Gitea
Build 18745 produced a working image (8.00 GiB compressed to 972 MiB in 44
seconds, checksum recorded) and then failed on the very last step:

    GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and
    download-artifact@v4+ are not currently supported on GHES.

Gitea's artifact backend identifies as GHES, and the v2 artifact client behind
upload-artifact@v4 refuses GHES outright rather than falling back. I chose v4
on the assumption Gitea 1.25 supported it; it does not. v3 works.

Dropping compression-level with it — v3 has no such input, and the image is
already zstd so it would have been a no-op anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
2026-07-27 15:26:58 +03:00

159 lines
5.9 KiB
YAML

name: build image
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
inputs:
variant:
description: which variant to build
type: choice
default: minimal
options: [minimal, workstation, both]
# Deliberately no strategy.matrix here.
#
# The obvious shape for this is a `prepare` job emitting a JSON list and a
# matrix built from `fromJSON(needs.prepare.outputs.variants)`. Gitea's runner
# evaluates strategy.matrix when it plans the workflow — before `needs` has run
# — so that expression resolves to an empty string and you get exactly one job
# leg with an empty variant. The prepare job succeeds and sets its output
# correctly; the matrix simply never sees it.
#
# Looping in shell is less elegant and entirely reliable.
jobs:
build:
runs-on: metal
timeout-minutes: 600
steps:
- uses: actions/checkout@v4
- name: Report runner
run: |
echo "host arch : $(uname -m)"
echo "kernel : $(uname -r)"
echo "user : $(id -un) (uid $(id -u))"
echo "podman : $(podman --version 2>/dev/null || echo MISSING)"
df -h .
# Building an aarch64 root filesystem means executing aarch64 rpm
# scriptlets, which needs a binfmt_misc handler in the host kernel. A
# container cannot register one for itself.
- name: Ensure aarch64 emulation
run: |
handler=/proc/sys/fs/binfmt_misc/qemu-aarch64
if [ ! -e "$handler" ]; then
echo "no aarch64 binfmt handler, attempting to install one"
sudo dnf install -y qemu-user-static-aarch64
sudo systemctl restart systemd-binfmt
fi
if [ ! -e "$handler" ]; then
echo "::error::aarch64 emulation is unavailable on this runner." \
"See docs/runner-setup.md for the one-time host setup."
exit 1
fi
cat "$handler"
# Both of these live outside the job workspace, which is wiped between
# runs. On metal runners a host path persists for free and avoids
# shuttling multi-gigabyte caches through Gitea's cache store — the
# tradeoff being that they are per-runner, so a job landing on a runner
# that has not built before starts cold.
- name: Prepare persistent build state
run: |
echo "CACHE_DIR=/var/tmp/c630-build/dnf" >> "$GITHUB_ENV"
echo "WORK_DIR=/var/tmp/c630-build/work" >> "$GITHUB_ENV"
mkdir -p /var/tmp/c630-build/dnf /var/tmp/c630-build/work
# Keep it bounded: drop cached rpms nothing has touched in a month.
find /var/tmp/c630-build/dnf -type f -atime +30 -delete 2>/dev/null || true
du -sh /var/tmp/c630-build/* 2>/dev/null || true
- name: Select variants
run: |
if [ "${{ startsWith(github.ref, 'refs/tags/') }}" = "true" ]; then
variants="minimal workstation"
else
case "${{ inputs.variant }}" in
both) variants="minimal workstation" ;;
workstation) variants="workstation" ;;
*) variants="minimal" ;;
esac
fi
echo "VARIANTS=$variants" | tee -a "$GITHUB_ENV"
- name: Build
run: |
rc=0
for variant in $VARIANTS; do
case "$variant" in
workstation) size=16384 ;;
*) size=8192 ;;
esac
echo "::group::build $variant (${size} MiB)"
# No --fresh: the stamp in stage2.sh hashes the package lists, so a
# change there invalidates the staged base on its own.
if ./build/build-image.sh \
--variant "$variant" \
--size "$size" \
--cache "$CACHE_DIR" \
--work "$WORK_DIR"; then
echo "$variant ok"
else
echo "::error::build failed for $variant"
rc=1
fi
echo "::endgroup::"
done
exit $rc
- name: Checksums
if: always()
run: cat output/*.sha256 2>/dev/null || echo "no images produced"
# v3, not v4. Gitea's artifact backend identifies as GHES, and
# @actions/artifact v2 (which backs upload-artifact@v4) refuses to talk to
# GHES outright — "GHESNotSupportedError". v3 works. The image is already
# zstd, so v3 storing it uncompressed costs nothing.
- uses: actions/upload-artifact@v3
if: always()
with:
name: fedora-lenovo-yoga-c630
path: |
output/*.img.zst
output/*.sha256
retention-days: 14
if-no-files-found: warn
- name: Attach to release
if: startsWith(github.ref, 'refs/tags/')
env:
TOKEN: ${{ github.token }}
SERVER: ${{ github.server_url }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
api="$SERVER/api/v1/repos/$REPO"
auth="Authorization: token $TOKEN"
# python3 rather than jq — jq is not guaranteed on a Fedora Server runner.
field() { python3 -c 'import json,sys; print(json.load(sys.stdin).get(sys.argv[1],""))' "$1"; }
id=$(curl -sf -H "$auth" "$api/releases/tags/$TAG" | field id || true)
if [ -z "${id:-}" ]; then
id=$(curl -sf -X POST -H "$auth" -H 'Content-Type: application/json' \
-d "{\"tag_name\":\"$TAG\",\"name\":\"$TAG\"}" \
"$api/releases" | field id)
fi
echo "release id: $id"
for f in output/*.img.zst output/*.sha256; do
echo "uploading $(basename "$f")"
curl -sf -X POST -H "$auth" \
-F "attachment=@${f}" \
"$api/releases/$id/assets?name=$(basename "$f")" >/dev/null
done