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