app-build/action.yml
Philip Peterson 080409370a Upload: retry server errors
The forge answered 504 to dnf-package-store's x86_64 RPM, uploaded just
after its aarch64 one; the build was fine and the RPM never landed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 22:09:35 -09:00

108 lines
4.2 KiB
YAML

# Builds an app as an RPM for each architecture and uploads it to the forge's
# RPM group (README.md). Runs on a host-mode runner (podman, curl):
# runner-x86-linux and runner-arm64-linux, one per architecture from the
# app's matrix; an architecture not the runner's own builds under QEMU
# emulation.
name: app-build
description: >-
Build an app (Flutter or Go) as RPMs for x86_64 and aarch64, packaged by
the repo's own spec, and upload them to the forge's RPM group.
inputs:
name:
description: The package's name; its spec is packaging/<name>.spec
required: true
kind:
description: flutter or go
required: true
go-packages:
description: For go, the packages to build into bin/ (space-separated, e.g. ./cmd/foo ./cmd/bar)
default: ''
dnf-packages:
description: Extra Fedora packages the build needs (space-separated)
default: ''
archs:
description: Architectures to build (space-separated podman names)
default: amd64 arm64
fedora:
description: Fedora release to build in
default: '44'
group:
description: The RPM group to upload to
default: quine
owner:
description: The forge account that holds the RPM group
default: quine-global-labs
registry-username:
description: Account that uploads the RPMs
required: true
registry-password:
description: Its token (package write)
required: true
server:
description: The forge's address (not github.server_url, as site-image explains)
default: https://forge.quinefoundation.com
runs:
using: composite
steps:
- name: Build
shell: bash
env:
NAME: ${{ inputs.name }}
KIND: ${{ inputs.kind }}
GO_PACKAGES: ${{ inputs.go-packages }}
DNF_PACKAGES: ${{ inputs.dnf-packages }}
ARCHS: ${{ inputs.archs }}
FEDORA: ${{ inputs.fedora }}
ACTION_DIR: ${{ github.action_path }}
# /var/cache/quine-rpm-build outlives the job (host-mode runner): the
# Flutter SDKs, Go's toolchain, and package caches.
run: |
set -euo pipefail
case $KIND in flutter|go) ;; *) echo "::error::kind must be flutter or go, not '$KIND'"; exit 1 ;; esac
[ -f "packaging/$NAME.spec" ] || { echo "::error::no packaging/$NAME.spec"; exit 1; }
mkdir -p out /var/cache/quine-rpm-build
for arch in $ARCHS; do
echo "::group::$NAME for $arch"
podman run --rm --platform "linux/$arch" \
-v "$PWD":/src -v "$ACTION_DIR/scripts":/scripts:ro \
-v /var/cache/quine-rpm-build:/cache -v "$PWD/out":/out \
-e NAME -e GO_PACKAGES -e DNF_PACKAGES \
"registry.fedoraproject.org/fedora:$FEDORA" bash "/scripts/build-$KIND.sh"
echo "::endgroup::"
done
ls -l out
- name: Upload
shell: bash
env:
REGISTRY_USERNAME: ${{ inputs.registry-username }}
REGISTRY_PASSWORD: ${{ inputs.registry-password }}
URL: ${{ inputs.server }}/api/packages/${{ inputs.owner }}/rpm/${{ inputs.group }}/upload
run: |
set -euo pipefail
[ -n "$REGISTRY_PASSWORD" ] || { echo "::error::no registry-password (a token with package write)"; exit 1; }
netrc=$(mktemp); trap 'rm -f "$netrc"' EXIT; chmod 600 "$netrc"
host=${URL#https://}; host=${host%%/*}
printf 'machine %s login %s password %s\n' "$host" "$REGISTRY_USERNAME" "$REGISTRY_PASSWORD" >"$netrc"
# The forge rebuilds and signs the group's metadata on each upload, and
# answered 504 to one landing right after another (dnf-package-store,
# 2026-11-04): retry server errors, with a growing wait.
for f in out/*.rpm; do
for try in 1 2 3 4 5 6; do
code=$(curl -sS -o /dev/null -w '%{http_code}' --netrc-file "$netrc" --upload-file "$f" "$URL" || true)
echo "$code ${f#out/}"
# 409: this exact version is there already (also when a retried
# upload had landed after all).
[ "$code" = 201 ] || [ "$code" = 409 ] && continue 2
case $code in 5??|000) [ "$try" = 6 ] || { sleep $((try * 20)); continue; } ;; esac
exit 1
done
exit 1
done
- name: Clean up
if: always()
shell: bash
run: rm -rf out build bin