From 4fc0c9bfe42b5d0e543c5ca44c86d25a19f5e1d9 Mon Sep 17 00:00:00 2001 From: tranch Date: Fri, 17 Jul 2026 06:56:56 +0800 Subject: Fix live installer ISO build Build the live installer from the requested local AeroCore image, use a separate Fedora live base, pass the payload through rootful container storage, and write reproducible checksum entries without host paths. --- .github/workflows/build-live-installer.yml | 17 ++++-- Justfile | 6 ++- disk_config/iso-gnome.toml | 7 --- disk_config/iso-kde.toml | 7 --- installer/Containerfile | 4 ++ installer/build.sh | 13 +++-- just_scripts/build-live-installer.sh | 84 ++++++++++++++++++++++++++---- 7 files changed, 105 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build-live-installer.yml b/.github/workflows/build-live-installer.yml index 870699e..b5fedb3 100644 --- a/.github/workflows/build-live-installer.yml +++ b/.github/workflows/build-live-installer.yml @@ -13,6 +13,7 @@ on: branches: - main paths: + - "Justfile" - "installer/**" - "just_scripts/build-live-installer.sh" - ".github/workflows/build-live-installer.yml" @@ -34,7 +35,7 @@ jobs: env: IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} IMAGE_TAG: ${{ inputs.image_tag || 'latest' }} - BASE_IMAGE: ghcr.io/ublue-os/bazzite:stable + LIVE_BASE_IMAGE: quay.io/fedora/fedora-kinoite:43 FEDORA_VERSION: "44" steps: @@ -76,11 +77,21 @@ jobs: - name: Build live installer payload run: | + mkdir -p output + sudo podman tag \ + "${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" \ + localhost/aerocore-live-payload:latest + sudo podman save \ + --format oci-archive \ + --output output/install-payload.oci \ + localhost/aerocore-live-payload:latest + sudo podman build \ --cap-add sys_admin \ --security-opt label=disable \ - --build-arg "BASE_IMAGE=${BASE_IMAGE}" \ - --build-arg "INSTALL_IMAGE_PAYLOAD=${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}" \ + --build-arg "BASE_IMAGE=${LIVE_BASE_IMAGE}" \ + --build-arg "INSTALL_IMAGE_PAYLOAD=localhost/aerocore-live-payload:latest" \ + --build-arg "INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci" \ --tag localhost/aerocore-live-payload:latest \ --file installer/Containerfile \ . diff --git a/Justfile b/Justfile index 5e73752..b65d2c9 100644 --- a/Justfile +++ b/Justfile @@ -9,6 +9,8 @@ export image_logo_url := env_var("IMAGE_LOGO_URL") export default_tag := env_var("DEFAULT_TAG") export bib_image := env_var("BIB_IMAGE") export BASE_IMAGE := env_var_or_default("BASE_IMAGE", "ghcr.io/ublue-os/bazzite-deck:stable") +export LIVE_BASE_IMAGE := env_var_or_default("LIVE_BASE_IMAGE", "quay.io/fedora/fedora-kinoite:43") +export INSTALLER_BUILDER_IMAGE := env_var_or_default("INSTALLER_BUILDER_IMAGE", "ghcr.io/jasonn3/build-container-installer:v1.5.0") export FLATPAK_REMOTE_URL := env_var_or_default("FLATPAK_REMOTE_URL", "") export HOMEBREW_BOTTLE_DOMAIN := env_var_or_default("HOMEBREW_BOTTLE_DOMAIN", "") export HOMEBREW_API_DOMAIN := env_var_or_default("HOMEBREW_API_DOMAIN", "") @@ -28,8 +30,8 @@ default: @just --list # Build a Bazzite-style live installer ISO -build-live-installer: - @./just_scripts/build-live-installer.sh +build-live-installer $target_image=("localhost/" + image_name) $tag=default_tag: + @IMAGE_NAME="{{ target_image }}" IMAGE_TAG="{{ tag }}" ./just_scripts/build-live-installer.sh # Boot the live installer ISO with QEMU run-live-installer: diff --git a/disk_config/iso-gnome.toml b/disk_config/iso-gnome.toml index 0fa5652..33cf931 100644 --- a/disk_config/iso-gnome.toml +++ b/disk_config/iso-gnome.toml @@ -1,10 +1,3 @@ -[customizations.installer.kickstart] -contents = """ -%post -bootc switch --mutate-in-place --transport registry ghcr.io/aerocore-io/aerocore-bazzite-image:latest -%end -""" - [customizations.installer.modules] enable = [ "org.fedoraproject.Anaconda.Modules.Storage", diff --git a/disk_config/iso-kde.toml b/disk_config/iso-kde.toml index e6e15a0..7f6c80d 100644 --- a/disk_config/iso-kde.toml +++ b/disk_config/iso-kde.toml @@ -1,10 +1,3 @@ -[customizations.installer.kickstart] -contents = """ -%post -bootc switch --mutate-in-place --transport registry ghcr.io/aerocore-io/aerocore-bazzite-image:latest -%end -""" - [customizations.installer.modules] enable = [ "org.fedoraproject.Anaconda.Modules.Storage", diff --git a/installer/Containerfile b/installer/Containerfile index fa817b6..50579e6 100644 --- a/installer/Containerfile +++ b/installer/Containerfile @@ -3,13 +3,17 @@ ARG BASE_IMAGE=ghcr.io/ublue-os/bazzite:stable ARG INSTALL_IMAGE_PAYLOAD=ghcr.io/AeroCore-IO/aerocore-bazzite-image:stable +ARG INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci FROM ${BASE_IMAGE} ARG BASE_IMAGE ARG INSTALL_IMAGE_PAYLOAD +ARG INSTALL_IMAGE_PAYLOAD_ARCHIVE RUN --mount=type=bind,source=.,target=/src \ + --mount=type=bind,source=output/install-payload.oci,target=/install-payload.oci,ro \ env BASE_IMAGE="${BASE_IMAGE}" \ INSTALL_IMAGE_PAYLOAD="${INSTALL_IMAGE_PAYLOAD}" \ + INSTALL_IMAGE_PAYLOAD_ARCHIVE="${INSTALL_IMAGE_PAYLOAD_ARCHIVE}" \ /src/installer/build.sh diff --git a/installer/build.sh b/installer/build.sh index 7532df8..033fe9b 100755 --- a/installer/build.sh +++ b/installer/build.sh @@ -5,10 +5,14 @@ set -euxo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BASE_IMAGE=${BASE_IMAGE:?} INSTALL_IMAGE_PAYLOAD=${INSTALL_IMAGE_PAYLOAD:?} +INSTALL_IMAGE_PAYLOAD_ARCHIVE=${INSTALL_IMAGE_PAYLOAD_ARCHIVE:-} # The installer is a live KDE environment. Keep the installed system payload # separate: it is the AeroCore image, while BASE_IMAGE provides the live UI. -dnf install -y \ +# Bazzite excludes some base packages from normal transactions. The live +# installer needs Anaconda's exact NetworkManager dependencies, so clear the +# excludes only for this dependency install. +dnf --setopt=excludepkgs= install -y \ anaconda-live \ libblockdev-btrfs \ libblockdev-dm \ @@ -22,9 +26,10 @@ if [[ -d /src/system_files ]]; then cp -a /src/system_files/. / fi -# Make the install payload available to Anaconda. When the build is running -# with the host container store mounted, this avoids a second registry pull. -if mountpoint -q /usr/lib/containers/storage; then +# Make the install payload available to Anaconda. +if [[ -n "${INSTALL_IMAGE_PAYLOAD_ARCHIVE}" && -f "${INSTALL_IMAGE_PAYLOAD_ARCHIVE}" ]]; then + podman load --storage-opt additionalimagestore='' < "${INSTALL_IMAGE_PAYLOAD_ARCHIVE}" +elif mountpoint -q /usr/lib/containers/storage; then podman save --format oci-archive "${INSTALL_IMAGE_PAYLOAD}" | \ podman load --storage-opt additionalimagestore='' else diff --git a/just_scripts/build-live-installer.sh b/just_scripts/build-live-installer.sh index 16b371e..45335cb 100755 --- a/just_scripts/build-live-installer.sh +++ b/just_scripts/build-live-installer.sh @@ -5,41 +5,98 @@ set -euo pipefail project_root="$(git rev-parse --show-toplevel)" cd "${project_root}" -image_name="${IMAGE_NAME:-$(just image_name)}" +image_name="${IMAGE_NAME:-localhost/$(just image_name)}" image_tag="${IMAGE_TAG:-$(just generate-default-tag)}" -base_image="${BASE_IMAGE:-ghcr.io/ublue-os/bazzite:stable}" -payload="localhost/${image_name}:live-payload" +live_base_image="${LIVE_BASE_IMAGE:-quay.io/fedora/fedora-kinoite:43}" +installer_builder_image="${INSTALLER_BUILDER_IMAGE:-ghcr.io/jasonn3/build-container-installer:v1.5.0}" +source_image="${image_name}:${image_tag}" +payload="localhost/aerocore-live-payload:latest" output_dir="${project_root}/output" +payload_archive="${output_dir}/install-payload.oci" +iso="${output_dir}/aerocore-os-${image_tag}-amd64.iso" +checksum="${iso}-CHECKSUM" api_socket="${PODMAN_SOCKET:-/run/podman/podman.sock}" if [[ "${EUID}" -eq 0 ]]; then podman_cmd=(podman) + socket_test=(test -S "${api_socket}") else podman_cmd=(sudo podman) + socket_test=(sudo test -S "${api_socket}") fi -if [[ ! -S "${api_socket}" ]]; then +if ! "${socket_test[@]}"; then echo "Podman API socket not found: ${api_socket}" >&2 echo "Set PODMAN_SOCKET to the rootful Podman socket and retry." >&2 exit 1 fi +ensure_rootful_image() { + local root_image_id user_image_id user_name user_uid copy_tmp + + root_image_id="$("${podman_cmd[@]}" images \ + --filter "reference=${source_image}" \ + --format "{{.ID}}" | head -n1)" + + if [[ -n "${root_image_id}" ]]; then + return 0 + fi + + if [[ "${EUID}" -ne 0 ]]; then + just _rootful_load_image "${image_name}" "${image_tag}" + return + fi + + if [[ -z "${SUDO_USER:-}" ]]; then + return + fi + + user_name="${SUDO_USER}" + user_uid="$(id -u "${user_name}")" + user_image_id="$(sudo -u "${user_name}" \ + env XDG_RUNTIME_DIR="/run/user/${user_uid}" \ + podman images \ + --filter "reference=${source_image}" \ + --format "{{.ID}}" | head -n1)" + + if [[ -z "${user_image_id}" ]]; then + return + fi + + copy_tmp="$(mktemp -p "${project_root}" -d -t _build_podman_scp.XXXXXXXXXX)" + TMPDIR="${copy_tmp}" podman image scp \ + "${user_uid}@localhost::${source_image}" \ + "root@localhost::${source_image}" + rm -rf "${copy_tmp}" +} + mkdir -p "${output_dir}" -"${podman_cmd[@]}" tag "${image_name}:${image_tag}" "${payload}" +ensure_rootful_image + +"${podman_cmd[@]}" tag "${source_image}" "${payload}" +"${podman_cmd[@]}" save --format oci-archive --output "${payload_archive}" "${payload}" "${podman_cmd[@]}" build \ --cap-add sys_admin \ --security-opt label=disable \ - --build-arg "BASE_IMAGE=${base_image}" \ + --build-arg "BASE_IMAGE=${live_base_image}" \ --build-arg "INSTALL_IMAGE_PAYLOAD=${payload}" \ - --tag localhost/aerocore-live-payload:latest \ + --build-arg "INSTALL_IMAGE_PAYLOAD_ARCHIVE=/install-payload.oci" \ + --tag "${payload}" \ --file installer/Containerfile \ . +if [[ "${EUID}" -eq 0 ]]; then + rm -f "${iso}" "${checksum}" +else + sudo rm -f "${iso}" "${checksum}" +fi + "${podman_cmd[@]}" run --rm --privileged \ --volume "${api_socket}:/var/run/docker.sock" \ + --volume /var/lib/containers/storage:/var/lib/containers/storage \ --volume "${project_root}/output:/build-container-installer/build" \ --volume "${project_root}/installer/lorax_templates:/additional_lorax_templates:ro" \ - docker.io/jasonn3/build-container-installer:latest \ + "${installer_builder_image}" \ ADDITIONAL_TEMPLATES="/additional_lorax_templates/remove_root_password_prompt.tmpl /additional_lorax_templates/set_default_user.tmpl" \ ARCH="x86_64" \ ENABLE_CACHE_DNF="false" \ @@ -47,12 +104,19 @@ mkdir -p "${output_dir}" ENABLE_FLATPAK_DEPENDENCIES="false" \ IMAGE_NAME="aerocore-live-payload" \ IMAGE_REPO="localhost" \ + IMAGE_SRC="containers-storage:${payload}" \ IMAGE_TAG="latest" \ ISO_NAME="build/aerocore-os-${image_tag}-amd64.iso" \ VARIANT="Kinoite" \ VERSION="${FEDORA_VERSION:-44}" -iso="${output_dir}/aerocore-os-${image_tag}-amd64.iso" if [[ -f "${iso}" ]]; then - sha256sum "${iso}" | tee "${iso}-CHECKSUM" + if [[ "${EUID}" -ne 0 ]]; then + sudo chown "$(id -u):$(id -g)" "${iso}" + sudo rm -f "${checksum}" + (cd "${output_dir}" && sha256sum "$(basename "${iso}")") | sudo tee "${checksum}" + sudo chown "$(id -u):$(id -g)" "${checksum}" + else + (cd "${output_dir}" && sha256sum "$(basename "${iso}")") | tee "${checksum}" + fi fi -- cgit v1.2.3