Files
phyllomeos/build-image.sh
T
Lukas GreveandClaude Sonnet 5 67316d9852
build-image / validate (push) Successful in 8s
ci / cook (push) Successful in 10s
build-image / build-image (phyllomeos, 16384) (push) Failing after 1m13s
build-image / build-image (phyllomeos-headless, 8192) (push) Failing after 11m10s
cook: two default dishes (phyllomeos, phyllomeos-headless) + guest/experimental tiers; CI builds raw images
Manifest groups get a tier; only `default` is generated (make all TIER=...,
--tier). Default: phyllomeos (GNOME + virt-manager) and phyllomeos-headless,
Fedora 44, systemd-boot, CPU-agnostic hypervisor ingredient (hypervisor_type:
any). guest tier: guest-server, guest-desktop. experimental: biosboot/grub,
encrypted, rawhide. Generator clears stale recipes/dishes before writing.

build-image.sh: --dish and --tier; deploy.sh: --tier. build-iso.yaml replaced
by build-image.yaml: raw images for both editions on the fedora:host runner
(TMPDIR=/var/tmp, max-parallel 1), attached with the flattened kickstarts to
tagged releases. Live edition no longer built.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 11:35:28 +02:00

208 lines
7.4 KiB
Bash
Executable File

#!/bin/bash
# Build a raw (or qcow2) disk image from a dish, without libvirt/virt-install.
#
# Runs `livemedia-creator --no-virt --make-disk` directly on the host, so
# the result is a disk image you can hand to another VM manager directly
# (e.g. `virtpull local ./build/<dish>.img --wonder-vm NAME`) instead of a
# bootable ISO.
#
# Usage: ./build-image.sh [--dish NAME] [--tier TIER] [--as qcow2]
# [--root-size MiB] [--extra-ks FILE]
#
# --dish skips the interactive picker (used by CI); --tier selects which
# manifest tier of dishes to generate (default: default; see
# cook/recipes_manifest.yaml).
#
# Requires: lorax-lmc-novirt (livemedia-creator), qemu-img, and root (the
# --no-virt disk install needs real loop-device access via
# /dev/loop-control; individual commands below run under sudo — passwordless
# sudo, or run this whole script as root).
#
# Earlier iteration wrapped this in `mock` (mirroring
# .gitea/workflows/build-iso.yaml's ISO build) to pin the target release's
# toolchain. That does not work for --make-disk: mock's chroot has no live
# systemd-udevd, so udev never populates properties for the loop device
# livemedia-creator creates for the image, and blivet's device scan
# crashes (blivet/udev.py device_get_name: "TypeError: argument of type
# 'NoneType' is not a container or iterable") — reproduced identically on
# Fedora 44 and 43 mock chroots, and confirmed absent when run directly on
# the host instead. --make-iso never hits this: ISO building doesn't touch
# block devices at all, only --make-disk (and other disk/appliance modes)
# does. The consequence: this script now builds using whatever
# anaconda/lorax the host has installed — the kickstart's own repo/url
# lines still pin the actual package set to the target Fedora release
# regardless, same as any other direct (non-mock) livemedia-creator use.
set -euo pipefail
AS_FORMAT=""
DISH=""
TIER="${TIER:-default}"
ROOT_SIZE=8192
EXTRA_KS=""
while [[ $# -gt 0 ]]; do
case "$1" in
--dish)
DISH="$2"
shift 2
;;
--tier)
TIER="$2"
shift 2
;;
--as)
AS_FORMAT="$2"
shift 2
;;
--root-size)
ROOT_SIZE="$2"
shift 2
;;
--extra-ks)
EXTRA_KS="$2"
shift 2
;;
*)
echo "Unknown argument: $1"
echo "Usage: $0 [--dish NAME] [--tier TIER] [--as qcow2] [--root-size MiB] [--extra-ks FILE]"
exit 1
;;
esac
done
if [[ -n "$AS_FORMAT" && "$AS_FORMAT" != "qcow2" ]]; then
echo "--as only supports 'qcow2' (the build itself always produces raw)"
exit 1
fi
if ! [[ "$ROOT_SIZE" =~ ^[0-9]+$ ]]; then
echo "--root-size must be a whole number of MiB, got '$ROOT_SIZE'"
exit 1
fi
if [[ -n "$EXTRA_KS" && ! -f "$EXTRA_KS" ]]; then
echo "--extra-ks file not found: $EXTRA_KS"
exit 1
fi
for tool in livemedia-creator qemu-img; do
if ! command -v "$tool" &> /dev/null; then
echo "Missing required tool: $tool"
echo "Fedora: sudo dnf install lorax-lmc-novirt qemu-img"
exit 1
fi
done
if [[ $EUID -ne 0 ]] && ! sudo -n true 2>/dev/null; then
echo "This needs root (loop-device access for the --no-virt disk install)."
echo "Re-run as root, or ensure passwordless sudo is available."
exit 1
fi
# Regenerate recipes and dishes before building (they are build products)
if ! (cd cook && make all TIER="$TIER"); then
echo "Failed to generate dishes in cook/ (see 'make all' in cook/)"
exit 1
fi
# Pick a dish: --dish, or interactively (shared with deploy/deploy-distro.sh)
if [[ -n "$DISH" ]]; then
dish="${DISH%.cfg}"
if [[ ! -f "cook/dishes/${dish}.cfg" ]]; then
echo "Dish not found: cook/dishes/${dish}.cfg (is --tier '$TIER' right?)"
exit 1
fi
else
# shellcheck source=./deploy/select-dish.sh
source "$(dirname "$0")/deploy/select-dish.sh"
dish="$selected_dish"
fi
# Derive the target release, same convention as
# deploy/deploy-distro.sh's find_fedora_iso() (used for --releasever, which
# substitutes @VERSION@ in bootloader/template text — the kickstart's own
# repo/url lines are what actually pin the package set).
if [[ "$dish" == *"rawhide"* ]]; then
releasever="rawhide"
elif [[ "$dish" =~ _([0-9]+) ]]; then
releasever="${BASH_REMATCH[1]}"
else
echo "Could not determine a Fedora release version from dish name '$dish'."
echo "Expected a '_<NN>' segment (e.g. desktop_44_standard...) or 'rawhide'."
exit 1
fi
out_dir="./build"
mkdir -p "$out_dir"
out_dir="$(cd "$out_dir" && pwd)"
# Adjustments needed only for this --no-virt disk-image path, applied to a
# scratch copy rather than the shared dish:
#
# 1. cook/ingredients/core/base.ks sets `text` for deploy/deploy-distro.sh's
# virt-install netinstall console; livemedia-creator manages the display
# mode itself and refuses a kickstart that also sets one ("The kickstart
# must not set a display mode").
#
# 2. cook/ingredients' storage layout uses `part / --grow` with no explicit
# --size: fine for virt-install, which pre-creates the disk at a known
# size for anaconda to grow into. livemedia-creator instead computes the
# total image size up front by summing partition sizes, and crashes
# (TypeError: unsupported operand type(s) for +: 'int' and 'NoneType')
# on a --grow partition with no size to add. Give it one via --root-size
# (default 8192 MiB; the desktop dishes likely need more).
#
# 3. --extra-ks appends arbitrary local content (e.g. a bespoke `user`/
# `rootpw`/`security` override) after the above. Kickstart's singular
# commands take the last occurrence, so this can override anything the
# dish already set without touching the shared ingredients — see the
# README's "bespoke dish not part of the matrix" pattern. Never commit
# a file used here if it carries real credentials.
disk_ks="$(mktemp "${TMPDIR:-/tmp}/${dish}.disk-build.XXXXXX.cfg")"
trap 'rm -f "$disk_ks"' EXIT
grep -v -E '^(text|cmdline|graphical)([[:space:]]|$)' "cook/dishes/${dish}.cfg" \
| awk -v size="$ROOT_SIZE" '
/^part / && /--grow/ && !/--size=/ { $0 = $0 " --size=" size }
{ print }
' > "$disk_ks"
if [[ -n "$EXTRA_KS" ]]; then
{
echo
cat "$EXTRA_KS"
} >> "$disk_ks"
fi
if command -v ksvalidator &> /dev/null; then
echo "Validating kickstart (informational; a build failure is the real test)..."
ksvalidator "$disk_ks" || true
fi
resultdir="$(mktemp -u "${TMPDIR:-/tmp}/lmc-result.XXXXXX")"
echo "Building disk image for '$dish' (target release $releasever)..."
sudo livemedia-creator --ks "$disk_ks" --no-virt --resultdir "$resultdir" \
--make-disk --image-name "${dish}.img" --releasever "$releasever"
# livemedia-creator's exact output filename depends on the installed lorax
# version; don't assume --image-name is honored verbatim, take whatever
# single disk image landed in the resultdir.
produced=$(sudo find "$resultdir" -maxdepth 1 -type f \( -iname '*.img' -o -iname '*disk*' \) | head -n 1)
if [[ -z "$produced" ]]; then
echo "livemedia-creator did not produce a recognizable disk image under $resultdir"
exit 1
fi
raw_out="${out_dir}/${dish}.img"
sudo cp --reflink=auto "$produced" "$raw_out"
sudo chown "$(id -u):$(id -g)" "$raw_out"
sudo rm -rf "$resultdir"
echo "Built: $raw_out"
if [[ "$AS_FORMAT" == "qcow2" ]]; then
qcow2_out="${out_dir}/${dish}.qcow2"
qemu-img convert -O qcow2 "$raw_out" "$qcow2_out"
echo "Converted: $qcow2_out"
final="$qcow2_out"
else
final="$raw_out"
fi
echo
echo "Next: virtpull local $final --wonder-vm <NAME>"