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" - uses: actions/upload-artifact@v4 if: always() with: name: fedora-lenovo-yoga-c630 path: | output/*.img.zst output/*.sha256 retention-days: 14 compression-level: 0 # already zstd 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