veilor-os/.github/workflows/build-installer-iso.yml
s8n-ru c272050890 feat(installer): persist install logs to USB by default
- new helper overlay/usr/share/veilor-os/scripts/persist-install-logs.sh
  detects boot USB (BOOT=/findfs, /run/install/repo, /sys/block removable),
  copies /tmp/anaconda.log + program/storage/packaging/dnf/syslog/X +
  journalctl -b + dmesg + lsblk/blkid/mount + /proc/cmdline into
  /veilor-install-logs/<UTC-ts>/ on the stick; mirrors backup into
  /mnt/sysroot/var/log/veilor-install-logs/ so logs survive even on RO
  USB or detect failure
- toggle: kernel cmdline veilor.install_logs=on|off (default ON until
  v1.0 final); never fails install on log persistence error
- kickstart/install-ostreecontainer-installer.ks: add %post --nochroot
  block calling helper with toggle-aware inline fallback if helper
  missing
- .github/workflows/build-installer-iso.yml: switch bib config from
  [customizations.user] to [customizations.installer.kickstart] so our
  new %post --nochroot actually lands in the produced ISO; admin user
  now created by ks user directive (locked + chage 0); ostreecontainer
  line stripped (bib auto-appends it); kernel-cmdline-default
  limitation documented (osbuild/bootc-image-builder#899)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 00:51:16 +01:00

208 lines
9 KiB
YAML

name: Build veilor-os Installer ISO
# v0.7+ — produces a small Anaconda installer ISO that consumes
# kickstart/install-ostreecontainer-installer.ks. The ISO boots
# Anaconda, asks for LUKS pw + admin pw interactively, then
# `ostreecontainer` populates / from the v0.7 OCI image at
# ghcr.io/veilor-org/veilor-os:43.
on:
push:
branches: [v0.7-bluebuild-spike]
paths:
- 'kickstart/install-ostreecontainer.ks'
- 'kickstart/install-ostreecontainer-installer.ks'
- 'bluebuild/recipe.yml'
- '.github/workflows/build-installer-iso.yml'
workflow_dispatch:
inputs:
releasever:
description: 'Fedora release version'
required: false
default: '43'
permissions:
contents: write # needed to create+update installer-latest release
jobs:
build:
name: Build installer ISO
runs-on: nullstone
timeout-minutes: 120
env:
RELEASEVER: ${{ github.event.inputs.releasever || '43' }}
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
- name: Install build tooling (Fedora)
run: |
set -euxo pipefail
dnf -y upgrade --refresh
dnf -y install --skip-unavailable podman jq
- name: Login to Forgejo registry (pull veilor-os OCI)
env:
FORGEJO_REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }}
FORGEJO_REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }}
run: |
set -euo pipefail
if [ -n "${FORGEJO_REGISTRY_TOKEN:-}" ]; then
echo "$FORGEJO_REGISTRY_TOKEN" | podman login \
--username "${FORGEJO_REGISTRY_USER:-veilor-org}" \
--password-stdin git.s8n.ru
fi
- name: Build installer ISO with bootc-image-builder
run: |
set -euxo pipefail
# livemedia-creator does NOT support ostreecontainer (only
# ostreesetup / url / nfs install methods). bootc-image-builder
# is the canonical tool for ostreecontainer-based installer
# ISOs; consumes our OCI image directly.
OUT="/tmp/bib-out-$$"
rm -rf "$OUT"
mkdir -p "$OUT"
# Pull the veilor-os OCI we built; bootc-image-builder needs
# it locally to compose the installer ISO.
podman pull ghcr.io/veilor-org/veilor-os:43 || \
podman pull git.s8n.ru/veilor-org/veilor-os:43
# Generate config.toml for bootc-image-builder.
#
# We use [customizations.installer.kickstart] (NOT
# [customizations.user]) because we need our own %post --nochroot
# block to persist install logs back to the boot USB. Per upstream
# docs, [customizations.user] and [customizations.installer.kickstart]
# are mutually exclusive (see osbuild/bootc-image-builder#528) — so
# the admin user is now created by a kickstart `user` directive
# below, locked + chage 0 so first SDDM login forces a real pw.
#
# bootc-image-builder auto-appends `ostreecontainer ...` to the
# contents we provide; we MUST NOT include that line ourselves
# (we strip it from the source kickstart with sed).
#
# NOTE on kernel cmdline default: ideally we'd set
# `veilor.install_logs=on` as an installer-kernel default, but
# `[customizations.kernel].append` targets the INSTALLED system's
# kargs.d, not the live ISO's grub.cfg (osbuild/bootc-image-builder
# #899 still open). The persist-install-logs.sh helper defaults to
# ON when the toggle is absent, so the desired default is achieved
# without needing installer-cmdline injection. Operators flip to
# off at boot via GRUB edit: append `veilor.install_logs=off`.
KS_SRC="kickstart/install-ostreecontainer-installer.ks"
KS_FILTERED="$(grep -v '^ostreecontainer' "$KS_SRC")"
# Insert a locked admin user directive under the rootpw block —
# Anaconda's interactive Users spoke is unavailable in unattended
# bib mode, so we pre-create admin and let chage -d 0 force a pw
# change at first login.
USER_LINE='user --name=admin --groups=wheel --plaintext --password="" --lock'
KS_FILTERED="$(printf '%s\n' "$KS_FILTERED" | awk -v ul="$USER_LINE" '/^rootpw --lock$/ { print; print ul; next } { print }')"
{
echo '[customizations.installer.kickstart]'
echo 'contents = """'
printf '%s\n' "$KS_FILTERED"
echo '"""'
} > /tmp/bib-config.toml
podman run --rm \
--privileged \
--pull=newer \
--security-opt label=type:unconfined_t \
-v "$OUT:/output" \
-v /tmp/bib-config.toml:/config.toml:ro \
-v /var/lib/containers/storage:/var/lib/containers/storage \
quay.io/centos-bootc/bootc-image-builder:latest \
--type anaconda-iso \
--config /config.toml \
--rootfs btrfs \
ghcr.io/veilor-org/veilor-os:43
mkdir -p build/out
find "$OUT" -name '*.iso' -exec cp {} build/out/ \;
ls -lh build/out/
- name: Rename ISO + sha256
run: |
set -euxo pipefail
ISO_FILE=$(ls build/out/*.iso 2>/dev/null | head -1)
[ -n "$ISO_FILE" ] || { echo "[ERR] no ISO produced"; exit 1; }
ISO_NAME="veilor-os-installer-${RELEASEVER}-$(date +%Y%m%d-%H%M%S).iso"
mv "$ISO_FILE" "build/out/$ISO_NAME"
cd build/out
sha256sum "$ISO_NAME" > "$ISO_NAME.sha256"
ls -lh "$ISO_NAME"
- name: Split ISO into 1900M chunks
if: success() && github.ref == 'refs/heads/v0.7-bluebuild-spike'
run: |
set -euo pipefail
cd build/out
ISO=$(ls *.iso | head -1)
[ -n "$ISO" ] || { echo "[ERR] no ISO"; exit 1; }
split -b 1900M -d --suffix-length=2 "$ISO" "${ISO}.part-"
rm -f "$ISO"
sha256sum *.part-* > "${ISO}.parts.sha256"
ls "${ISO}".part-*
- name: Publish to installer-latest rolling prerelease (Forgejo)
if: success() && github.ref == 'refs/heads/v0.7-bluebuild-spike' && github.server_url != 'https://github.com'
env:
FORGEJO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FORGEJO_API: ${{ github.server_url }}/api/v1
REPO: ${{ github.repository }}
GIT_SHA: ${{ github.sha }}
run: |
set -euo pipefail
TAG="installer-latest"
REL_JSON=$(curl -fsSL -H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_API}/repos/${REPO}/releases/tags/${TAG}" 2>/dev/null || echo "")
if [ -n "$REL_JSON" ]; then
REL_ID=$(echo "$REL_JSON" | grep -oE '"id":\s*[0-9]+' | head -1 | grep -oE '[0-9]+')
if [ -n "$REL_ID" ]; then
curl -fsSL -X DELETE -H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_API}/repos/${REPO}/releases/${REL_ID}" || true
curl -fsSL -X DELETE -H "Authorization: token ${FORGEJO_TOKEN}" \
"${FORGEJO_API}/repos/${REPO}/git/refs/tags/${TAG}" || true
fi
fi
BODY="Rolling auto-build from v0.7-bluebuild-spike. Latest commit: ${GIT_SHA}.
Installer ISO — boots Anaconda, prompts for LUKS pw + admin pw,
then ostreecontainer-pulls / from ghcr.io/veilor-org/veilor-os:43.
Reassemble:
cat veilor-os-installer-*.iso.part-* > veilor-os-installer.iso
sha256sum -c veilor-os-installer-*.iso.parts.sha256
Not a stable release — for testing only."
PAYLOAD=$(BODY="$BODY" TAG="$TAG" python3 -c "
import json,os
print(json.dumps({
'tag_name': os.environ['TAG'],
'target_commitish': 'v0.7-bluebuild-spike',
'name': 'installer-latest (auto)',
'body': os.environ['BODY'],
'prerelease': True,
'draft': False,
}))")
REL_ID=$(curl -fsSL -X POST -H "Authorization: token ${FORGEJO_TOKEN}" \
-H "Content-Type: application/json" \
-d "$PAYLOAD" \
"${FORGEJO_API}/repos/${REPO}/releases" | \
grep -oE '"id":\s*[0-9]+' | head -1 | grep -oE '[0-9]+')
[ -n "$REL_ID" ] || { echo "[ERR] failed to create release"; exit 1; }
cd build/out
for f in *.iso.part-* *.sha256; do
[ -f "$f" ] || continue
curl -fsSL -X POST -H "Authorization: token ${FORGEJO_TOKEN}" \
-F "attachment=@${f}" \
"${FORGEJO_API}/repos/${REPO}/releases/${REL_ID}/assets?name=${f}"
done
- name: Print build log on failure
if: failure()
run: |
echo "─── build/out/build.log ───"
tail -200 build/out/build.log 2>/dev/null || echo "(no build.log)"
find build/out -name 'program.log' -exec tail -100 {} \; 2>/dev/null || true
find /var/lmc -name '*.log' -exec tail -50 {} \; 2>/dev/null || true