smoke-test.sh checks an image from build-image.sh in two steps: - static, read-only: if the image declares SELinux, no kernel command line (/etc/kernel/cmdline, ESP and /boot loader entries) may carry selinux=0 and files must be labeled. This catches images built on a host with SELinux disabled, where anaconda inherits the host's selinux=0 and overrides the kickstart's `selinux --enforcing` (CI run 237); - boot: KVM with -snapshot, no network, waits for qemu-guest-agent to answer, so it does not depend on a serial console in the image. Verified on the runner: the run-237 image fails the static check, a fresh build passes both (agent answered after 27s). build-image.yaml now smoke-tests each edition before compressing it, and besides v*.*.* tags also runs: - on PRs into main labeled `build-image` (the review gate: Gitea does not let authors approve their own PRs), re-running on new pushes while the label stays, only for image-affecting paths, and not when an unrelated label is added; - weekly (Mondays 03:00), to catch upstream Fedora breakage; - via workflow_dispatch. Non-tag runs keep the images as 7-day artifacts. README updated, including the stale "every push to main" sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
148 lines
5.3 KiB
YAML
148 lines
5.3 KiB
YAML
name: build-image
|
|
|
|
# A full build takes ~25 min on the runner and executes the checked-out code
|
|
# as root there, so it never runs on a plain push. Recipes are still linted
|
|
# and validated on every push and PR by ci.yml. Images are built:
|
|
# - on a release tag (v*.*.*): build, smoke test, publish as a release;
|
|
# - on a PR into main carrying the `build-image` label, i.e. once a
|
|
# maintainer has reviewed it (Gitea does not let authors approve their
|
|
# own PRs, so approval cannot be the gate). Re-runs on every new push to
|
|
# the PR while the label stays. Only for changes that affect images;
|
|
# - weekly, to catch breakage from upstream Fedora package changes;
|
|
# - by hand (workflow_dispatch) on any branch.
|
|
on:
|
|
push:
|
|
tags: ["v*.*.*"]
|
|
pull_request:
|
|
branches: [main]
|
|
types: [labeled, synchronize, reopened]
|
|
paths:
|
|
- "cook/**"
|
|
- "build-image.sh"
|
|
- "smoke-test.sh"
|
|
- ".gitea/workflows/build-image.yaml"
|
|
schedule:
|
|
- cron: "0 3 * * 1" # Mondays 03:00
|
|
workflow_dispatch:
|
|
|
|
# Builds raw disk images of the two default-tier editions (see
|
|
# cook/recipes_manifest.yaml) with build-image.sh, i.e.
|
|
# `livemedia-creator --no-virt --make-disk`. Other tiers (guest, experimental)
|
|
# are built on demand with `./build-image.sh --tier <tier>`.
|
|
#
|
|
# The build job runs on the registered `fedora:host` runner (a Fedora VM, jobs
|
|
# run as root), not in a container: --make-disk needs real loop devices and a
|
|
# live systemd-udevd. The runner needs lorax-lmc-novirt, qemu-img, make,
|
|
# python3-pip, pykickstart and xz installed, plus qemu-system-x86_64,
|
|
# /dev/kvm and edk2-ovmf for smoke-test.sh. It must run with SELinux enabled:
|
|
# anaconda inherits the runner's own selinux=0, if any (smoke-test.sh fails
|
|
# such images).
|
|
|
|
jobs:
|
|
validate:
|
|
# On PRs, only once the `build-image` label is set (see `on:` above), and
|
|
# not again when some other label is added to an already-labeled PR.
|
|
if: >-
|
|
github.event_name != 'pull_request' ||
|
|
(contains(github.event.pull_request.labels.*.name, 'build-image') &&
|
|
(github.event.action != 'labeled' || github.event.label.name == 'build-image'))
|
|
runs-on: fedora
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
container:
|
|
image: git.phyllo.me/devops/fedora-runner-image:latest
|
|
|
|
steps:
|
|
- uses: https://git.phyllo.me/devops/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Install Python deps
|
|
run: pip install -r cook/requirements.txt
|
|
|
|
- name: Generate, lint, and validate dishes
|
|
run: |
|
|
cd cook
|
|
make all
|
|
|
|
build-image:
|
|
needs: validate
|
|
runs-on: fedora
|
|
strategy:
|
|
fail-fast: false
|
|
# NB: Gitea runs every matrix entry as its own task, so `max-parallel`
|
|
# does not serialize them; build-image.sh takes a host-wide flock instead.
|
|
matrix:
|
|
include:
|
|
- edition: phyllomeos
|
|
root_size: 16384
|
|
- edition: phyllomeos-headless
|
|
root_size: 8192
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
env:
|
|
# /tmp is a small tmpfs on the runner; livemedia-creator writes the whole
|
|
# disk image under $TMPDIR
|
|
TMPDIR: /var/tmp
|
|
|
|
steps:
|
|
- uses: https://git.phyllo.me/devops/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Check runner prerequisites
|
|
run: |
|
|
missing=0
|
|
for tool in livemedia-creator qemu-img make pip xz sudo qemu-system-x86_64 debugfs; do
|
|
command -v "$tool" >/dev/null || { echo "missing on runner: $tool"; missing=1; }
|
|
done
|
|
[ -e /dev/loop-control ] || { echo "missing on runner: /dev/loop-control"; missing=1; }
|
|
[ -e /dev/kvm ] || { echo "missing on runner: /dev/kvm"; missing=1; }
|
|
[ -e /usr/share/edk2/ovmf/OVMF_CODE.fd ] || { echo "missing on runner: edk2-ovmf"; missing=1; }
|
|
[ "$missing" = 0 ] || exit 1
|
|
|
|
- name: Install Python deps
|
|
run: pip install -r cook/requirements.txt
|
|
|
|
- name: Build raw image
|
|
run: |
|
|
# Editions are named "<edition>_<values...>.cfg"; the trailing "_" keeps
|
|
# "phyllomeos" from matching "phyllomeos-headless".
|
|
(cd cook && make all)
|
|
dish="$(basename "$(ls cook/dishes/${{ matrix.edition }}_*.cfg)" .cfg)"
|
|
echo "DISH=$dish" >> "$GITHUB_ENV"
|
|
./build-image.sh --dish "$dish" --root-size ${{ matrix.root_size }}
|
|
|
|
- name: Smoke test (static checks + boot)
|
|
run: ./smoke-test.sh "build/$DISH.img"
|
|
|
|
- name: Compress image
|
|
run: xz -T0 "build/$DISH.img"
|
|
|
|
- name: Upload image and kickstart as artifact
|
|
if: "!startsWith(github.ref, 'refs/tags/')"
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: ${{ matrix.edition }}
|
|
path: |
|
|
build/${{ env.DISH }}.img.xz
|
|
cook/dishes/${{ env.DISH }}.cfg
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
- name: Publish image and kickstart as release
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
uses: https://git.phyllo.me/devops/action-gh-release@v2
|
|
with:
|
|
files: |
|
|
build/${{ env.DISH }}.img.xz
|
|
cook/dishes/${{ env.DISH }}.cfg
|
|
draft: false
|
|
prerelease: false
|
|
|
|
- name: Clean up build products
|
|
if: always()
|
|
run: rm -rf build /var/tmp/lmc-result.*
|