From ac5c29df4252bcc1a0e74a25cd525856b1c774dd Mon Sep 17 00:00:00 2001 From: obsidian-ai Date: Wed, 6 May 2026 14:35:44 +0100 Subject: [PATCH] ci: run build directly in Fedora job container, drop addnab nest forgejo-runner labels nullstone -> fedora:43 image. Switching runs-on: ubuntu-24.04 -> nullstone makes the job container itself the build environment, eliminating the docker-in-docker workspace bind-mount problem (host path != act-container path). Build now runs as root in fedora:43, installs livecd-tools directly via dnf, and writes outputs to $GITHUB_WORKSPACE which is the natural runner workdir on host. No nested docker, no userns juggling, no explicit -v workspace bind needed. --- .github/workflows/build-iso.yml | 244 +++++++++++++------------------- 1 file changed, 95 insertions(+), 149 deletions(-) diff --git a/.github/workflows/build-iso.yml b/.github/workflows/build-iso.yml index ba6dfbf..1ccff57 100644 --- a/.github/workflows/build-iso.yml +++ b/.github/workflows/build-iso.yml @@ -29,7 +29,10 @@ permissions: jobs: build: name: Build live ISO - runs-on: ubuntu-24.04 + # nullstone label resolves to a privileged Fedora 43 container per + # the runner's RUNNER_LABELS map. Build runs directly in this job + # container — no nested docker-run-action, no bind-mount juggling. + runs-on: nullstone timeout-minutes: 90 steps: @@ -38,164 +41,110 @@ jobs: # node24 which forgejo-runner v6.4.0 (node20) cannot exec. uses: actions/checkout@v4.1.7 - - name: Free up disk + - name: Install build tooling (Fedora) run: | - sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /usr/local/share/boost - sudo apt-get clean - df -h + set -euxo pipefail + dnf -y upgrade --refresh + dnf -y install \ + lorax \ + livecd-tools \ + pykickstart \ + python3-imgcreate \ + anaconda-tui \ + squashfs-tools \ + xorriso \ + createrepo_c \ + git \ + which \ + shadow-utils \ + syslinux \ + tar \ + curl \ + sudo - - name: Run build inside Fedora 43 container - # v3 is composite/docker-based — no node runtime in the action - # itself. Safe under node20 forgejo-runner. TODO(infra): consider - # SHA pinning in a follow-up sweep. - uses: addnab/docker-run-action@v3 - with: - # Pinned to amd64 digest from `skopeo inspect --raw` on 2026-05-06. - # Refresh by re-running skopeo against fedora:43 and picking the amd64 entry. - image: registry.fedoraproject.org/fedora:43@sha256:3207ed0f3765f81675f6ce2aa04f52a6cdbb2dddddc5ab9be6e0b0441ece136d - options: | - --privileged - --userns=host - -v ${{ github.workspace }}:/work - -v /dev:/dev - --tmpfs /tmp:rw,nosuid,nodev,exec,size=16G - run: | - set -euxo pipefail + - name: Vendor gum binary into overlay + run: | + set -euxo pipefail + GUM_VERSION="0.17.0" + GUM_URL="https://github.com/charmbracelet/gum/releases/download/v${GUM_VERSION}/gum_${GUM_VERSION}_Linux_x86_64.tar.gz" + GUM_SHA256="69ee169bd6387331928864e94d47ed01ef649fbfe875baed1bbf27b5377a6fdb" + mkdir -p overlay/usr/local/bin + curl -fsSL "$GUM_URL" -o /tmp/gum.tgz + echo "$GUM_SHA256 /tmp/gum.tgz" | sha256sum -c - + tar -xzf /tmp/gum.tgz -C /tmp/ + install -m 0755 "/tmp/gum_${GUM_VERSION}_Linux_x86_64/gum" overlay/usr/local/bin/gum + overlay/usr/local/bin/gum --version + echo "[OK] gum ${GUM_VERSION} vendored into overlay/usr/local/bin/" - # Update Fedora image to latest packages — guarantees pcre2 + - # libselinux + selinux-policy are matched (the local build's - # core problem). CI runners always start fresh, no version skew. - dnf -y upgrade --refresh + - name: Build ISO with livecd-creator + run: | + set -euxo pipefail - # Install build tooling - dnf -y install \ - lorax \ - livecd-tools \ - pykickstart \ - python3-imgcreate \ - anaconda-tui \ - squashfs-tools \ - xorriso \ - createrepo_c \ - git \ - which \ - shadow-utils \ - syslinux \ - tar \ - curl + # PATCH: livecd-creator bug — __get_efi_image_stanza writes + # `root=live:LABEL=...` instead of `live:CDLABEL=...` for dracut. + # Result: dracut hangs on parse-livenet looking for non-CD label. + # Fix in-place before running build. + LIVE_PY=$(python3 -c 'import imgcreate, os; print(os.path.dirname(imgcreate.__file__))')/live.py + sed -i 's|"live:LABEL=%(fslabel)s"|"live:CDLABEL=%(fslabel)s"|g' "$LIVE_PY" + grep -n 'CDLABEL=%(fslabel)s' "$LIVE_PY" || { echo "[ERR] patch failed"; exit 1; } + echo "[OK] livecd-creator patched: LABEL= → CDLABEL= for EFI dracut stanza" - # Vendor gum binary onto the ISO so the TTY1 installer can use - # Charm.sh TUI primitives. gum is not packaged in Fedora repos, - # so pull the upstream release tarball pinned by sha256. - GUM_VERSION="0.17.0" - GUM_URL="https://github.com/charmbracelet/gum/releases/download/v${GUM_VERSION}/gum_${GUM_VERSION}_Linux_x86_64.tar.gz" - GUM_SHA256="69ee169bd6387331928864e94d47ed01ef649fbfe875baed1bbf27b5377a6fdb" - mkdir -p /work/overlay/usr/local/bin - curl -fsSL "$GUM_URL" -o /tmp/gum.tgz - echo "$GUM_SHA256 /tmp/gum.tgz" | sha256sum -c - - tar -xzf /tmp/gum.tgz -C /tmp/ - install -m 0755 "/tmp/gum_${GUM_VERSION}_Linux_x86_64/gum" /work/overlay/usr/local/bin/gum - /work/overlay/usr/local/bin/gum --version - echo "[OK] gum ${GUM_VERSION} vendored into overlay/usr/local/bin/" + # CI uses ks-ci.ks (no local fix-repo line). Generated from main ks. + # Drop `updates` repo: previously 404'd on repodata zchunk during + # Fedora mid-push windows. Base 43 ships the selinux-policy fix. + sed -e '/veilor-fix/d' \ + -e '/^shutdown$/d' \ + -e '/repo --name=updates/d' \ + kickstart/veilor-os.ks > kickstart/veilor-os-ci.ks - cd /work + ksvalidator kickstart/veilor-os-ci.ks + mkdir -p build/out - # Diagnostic: dump workspace state so a path/perm issue surfaces - # before commands silently no-op. - echo "=== /work diagnostic ===" - id - pwd - mountpoint /work || true - ls -la /work | head -20 - ls -la /work/kickstart 2>&1 | head -10 - touch /work/kickstart/.write-test && echo "[OK] /work/kickstart writable" || echo "[ERR] /work/kickstart NOT writable" - rm -f /work/kickstart/.write-test 2>/dev/null - echo "=== end diagnostic ===" + mkdir -p /var/lmc /var/lmc-cache + livecd-creator \ + --verbose \ + --config kickstart/veilor-os-ci.ks \ + --fslabel "veilor-os-43" \ + --title "veilor-os" \ + --product "veilor-os" \ + --releasever "${{ github.event.inputs.releasever || '43' }}" \ + --tmpdir /var/lmc \ + --cache /var/lmc-cache 2>&1 | tee build/out/build.log - # PATCH: livecd-creator bug — __get_efi_image_stanza writes - # `root=live:LABEL=...` instead of `live:CDLABEL=...` for dracut. - # Result: dracut hangs on parse-livenet looking for non-CD label. - # Fix in-place before running build. - LIVE_PY=$(python3 -c 'import imgcreate, os; print(os.path.dirname(imgcreate.__file__))')/live.py - sed -i 's|"live:LABEL=%(fslabel)s"|"live:CDLABEL=%(fslabel)s"|g' "$LIVE_PY" - grep -n 'CDLABEL=%(fslabel)s' "$LIVE_PY" || { echo "[ERR] patch failed"; exit 1; } - echo "[OK] livecd-creator patched: LABEL= → CDLABEL= for EFI dracut stanza" + - name: Graft veilor source tree onto ISO + run: | + set -euxo pipefail + ISO_FILE=$(ls ./*.iso 2>/dev/null | head -1) + [ -n "$ISO_FILE" ] || { echo "[ERR] no ISO produced by livecd-creator"; exit 1; } + echo "[INFO] grafting /veilor/ onto $ISO_FILE" - # CI uses ks-ci.ks (no local fix-repo line). Generated from main ks. - # Also strip flags livecd-creator doesn't recognize. - # Drop `updates` repo: 3 consecutive builds 404'd on its - # repodata zchunk file across all mirrors — Fedora infra issue - # mid-push window. Original reason for `updates` (selinux-policy - # 43.7 pcre2 fix) is no longer needed; base 43 ships fixed. - sed -e '/veilor-fix/d' \ - -e '/^shutdown$/d' \ - -e '/repo --name=updates/d' \ - kickstart/veilor-os.ks > kickstart/veilor-os-ci.ks + xorriso -indev "$ISO_FILE" -report_el_torito as_mkisofs 2>&1 | tee /tmp/iso-boot.txt || true + ORIG_FLAGS=$(xorriso -indev "$ISO_FILE" -report_el_torito as_mkisofs 2>/dev/null | \ + grep -v '^xorriso :' | grep -E '^-' | tr '\n' ' ') + [ -n "$ORIG_FLAGS" ] || { echo "[ERR] could not extract boot stanza from $ISO_FILE"; exit 1; } - ksvalidator kickstart/veilor-os-ci.ks - mkdir -p build/out + mkdir -p /tmp/iso-mod + xorriso -osirrox on -indev "$ISO_FILE" -extract / /tmp/iso-mod + chmod -R u+w /tmp/iso-mod + mkdir -p /tmp/iso-mod/veilor + cp -a overlay scripts assets /tmp/iso-mod/veilor/ - # livecd-creator (livecd-tools) — purpose-built for live ISOs. - # Handles EFI/BOOT + isohybrid + grafting that livemedia-creator - # --make-iso --no-virt does not. Produces UEFI+BIOS bootable ISO. - # --tmpdir /var/lmc to avoid GitHub Actions /tmp tmpfs constraints. - # /var on the runner is the host's ext4 (~80GB free post-disk-cleanup). - mkdir -p /var/lmc /var/lmc-cache - livecd-creator \ - --verbose \ - --config kickstart/veilor-os-ci.ks \ - --fslabel "veilor-os-43" \ - --title "veilor-os" \ - --product "veilor-os" \ - --releasever "${{ github.event.inputs.releasever || '43' }}" \ - --tmpdir /var/lmc \ - --cache /var/lmc-cache 2>&1 | tee build/out/build.log + eval xorriso -as mkisofs \ + -volid "veilor-os-43" \ + $ORIG_FLAGS \ + -o "${ISO_FILE}.tmp" /tmp/iso-mod + mv "${ISO_FILE}.tmp" "$ISO_FILE" + rm -rf /tmp/iso-mod - # Graft veilor source tree onto the ISO so the installer-generated - # kickstart's `%post --nochroot` can find SRC at - # /run/install/repo/veilor/{overlay,scripts,assets}/ when the user - # promotes the live ISO into a real install. - ISO_FILE=$(ls /work/*.iso 2>/dev/null | head -1) - [ -n "$ISO_FILE" ] || { echo "[ERR] no ISO produced by livecd-creator"; exit 1; } - echo "[INFO] grafting /veilor/ onto $ISO_FILE" + mv "$ISO_FILE" build/out/ - # Extract original ISO's exact boot stanza so the rebuild matches - # livecd-creator's layout byte-for-byte. This is immune to upstream - # Fedora layout changes (e.g. images/ vs isolinux/ for efiboot.img, - # partition geometry flags, hybrid MBR/GPT options). - xorriso -indev "$ISO_FILE" -report_el_torito as_mkisofs 2>&1 | tee /tmp/iso-boot.txt || true - ORIG_FLAGS=$(xorriso -indev "$ISO_FILE" -report_el_torito as_mkisofs 2>/dev/null | \ - grep -v '^xorriso :' | grep -E '^-' | tr '\n' ' ') - [ -n "$ORIG_FLAGS" ] || { echo "[ERR] could not extract boot stanza from $ISO_FILE"; exit 1; } - echo "[INFO] re-pack flags from original ISO: $ORIG_FLAGS" - - mkdir -p /tmp/iso-mod - xorriso -osirrox on -indev "$ISO_FILE" -extract / /tmp/iso-mod - chmod -R u+w /tmp/iso-mod - mkdir -p /tmp/iso-mod/veilor - cp -a /work/overlay /work/scripts /work/assets /tmp/iso-mod/veilor/ - - # Replay the exact stanza captured above. eval is needed because - # ORIG_FLAGS contains multiple flag/value pairs that must word-split. - eval xorriso -as mkisofs \ - -volid "veilor-os-43" \ - $ORIG_FLAGS \ - -o "${ISO_FILE}.tmp" /tmp/iso-mod - mv "${ISO_FILE}.tmp" "$ISO_FILE" - rm -rf /tmp/iso-mod - echo "[OK] /veilor/ grafted onto $ISO_FILE" - - # Move output ISO to expected dir - mv ./veilor-os-43.iso build/out/ 2>/dev/null || mv ./*.iso build/out/ 2>/dev/null || true - - # Rename + checksum - ISO_NAME="veilor-os-${{ github.event.inputs.releasever || '43' }}-$(date +%Y%m%d-%H%M%S).iso" - cd build/out - for f in *.iso; do - [[ -f $f && $f != $ISO_NAME ]] && mv "$f" "$ISO_NAME" - done - sha256sum "$ISO_NAME" > "$ISO_NAME.sha256" - ls -lh "$ISO_NAME" + ISO_NAME="veilor-os-${{ github.event.inputs.releasever || '43' }}-$(date +%Y%m%d-%H%M%S).iso" + cd build/out + for f in *.iso; do + [[ -f $f && $f != "$ISO_NAME" ]] && mv "$f" "$ISO_NAME" + done + sha256sum "$ISO_NAME" > "$ISO_NAME.sha256" + ls -lh "$ISO_NAME" # ── ISO publish ──────────────────────────────────────────────────── # GH Release asset size limit = 2 GiB. Our ISO ~2.8 GiB. Split into @@ -205,9 +154,6 @@ jobs: - name: Split ISO into 2GiB chunks if: success() && github.ref == 'refs/heads/main' run: | - # ISO + sidecars created by Fedora container as root. Reclaim - # ownership so this step (running as runner user) can write. - sudo chown -R "$(id -u):$(id -g)" build/out cd build/out ISO=$(ls *.iso | head -1) [ -n "$ISO" ] || { echo "[ERR] no ISO"; exit 1; }