Skip to content

ci

ci #539

Workflow file for this run

name: ci
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
schedule:
# Daily live-ISO signal: base images drift under us (dracut, systemd,
# bootc) — a broken live boot should page this repo, not a downstream.
- cron: '20 4 * * *'
# Without this, every push to a branch/PR (including Renovate's frequent
# rebases) queues a brand-new run instead of superseding the last one for
# that ref. Observed 2026-07-20: 26 queued runs piled up on this repo alone
# — many of them stale duplicates from earlier rebases of the same PR —
# saturating the shared macOS/Windows runner pool for hours and starving
# genuinely new work. github.head_ref is empty on push/schedule, so this
# still groups correctly (falls back to github.ref) for non-PR triggers.
concurrency:
group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
# ── Stage 1+2: lint + unit + recipe schema parse ────────────────────────────
# Cheap, runs on every PR. ~2 min.
lint-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
- name: go vet
run: go vet ./...
- name: go test
run: go test ./...
- name: go test with coverage summary
run: go test -cover -covermode=set ./...
# Integration-tag help checks: builds the binary and execs --help
# for every subcommand. Confirms the CLI surface matches what CI depends on.
- name: go test (integration tag, help-only)
run: go test -tags=integration -run='Help' ./...
- name: go build
run: go build ./...
# Stage 2: every example recipe must parse successfully. The build
# tool itself enforces the schema; we just feed each one to a no-op
# invocation and check exit status. --help short-circuits before any
# actual I/O so this is fast.
- name: recipe schema parses
run: |
go build -o tacklebox ./cmd/tacklebox
fail=0
for r in examples/*.json fixtures/*.json; do
[ -f "$r" ] || continue
# Use a syntactic-only parse: jq if installed, else go's parser
# via `tacklebox build --help` (rejects the recipe arg before
# opening anything).
if ! python3 -c "import json,sys; json.load(open('$r'))"; then
echo "::error file=$r::JSON parse failed"
fail=1
fi
done
exit $fail
- name: shellcheck dracut modules
uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca # master as of 2026-06-09
with:
additional_files: src/dracut/95tbox-root/module-setup.sh src/dracut/95tbox-root/tbox-root-mount.sh src/dracut/90tbox-live/module-setup.sh src/dracut/90tbox-live/parse-tbox-live.sh src/dracut/90tbox-live/tbox-live-root.sh src/dracut/90tbox-live/tbox-live-generator.sh src/dracut/90tbox-live/tbox-live-mount.sh scripts/test-boot.sh
severity: warning
# ── Stage 3: two-env disk-build smoke, verify, update, and boot ────────────
# Builds a 10 GB loop image with two distinct upstream bootc images and
# asserts `tacklebox verify` passes (each env must end up with a distinct
# ostree commit hash — the regression check for the 2026-05-11 cross-env
# collision bug). Then runs `tacklebox update` to re-install all envs
# without reformatting, and verifies again — regression check for the
# USB pre-flight unmount and the update command.
#
# Also exercises the UnmountDevice loop-device smoke at the integration
# level: creates a loop partition, mounts it, calls UnmountDevice, asserts
# the mount is gone.
#
# Uses /mnt (~75 GB NVMe on free runners); jlumbroso/free-disk-space
# recovers ~30 GB on / first.
verify-smoke:
runs-on: ubuntu-latest
needs: lint-test
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
- name: free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # main as of 2026-06-09
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: true
swap-storage: false
- name: install build deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
xorriso mtools squashfs-tools dosfstools \
systemd-boot systemd-boot-efi gdisk podman \
qemu-system-x86 ovmf
# Runner image ubuntu24/20260726 bumped its Podman 4.9.3 -> 5.8.4 but
# left crun at noble's 1.14.1. Podman >= 5 stamps `ociVersion: 1.2.0`
# into config.json, and crun <= 1.14 rejects any version that is not
# 1.0.x, so every `podman run` dies before the container starts with
# "OCI runtime error: crun: unknown version specified" (exit 126).
# crun 1.15 dropped that check and runc never had it, so point podman
# at runc for exactly that pairing: a no-op on images whose crun and
# podman already agree.
- name: match podman's OCI runtime to its spec version
run: |
podman_ver=$(podman --version | awk '{print $3}')
crun_ver=$(crun --version 2>/dev/null | awk 'NR==1{print $3}')
echo ">>> podman=$podman_ver crun=${crun_ver:-absent}"
if dpkg --compare-versions "$podman_ver" ge 5 &&
[ -n "$crun_ver" ] && dpkg --compare-versions "$crun_ver" lt 1.15; then
command -v runc >/dev/null || {
echo "::error::crun $crun_ver cannot run podman $podman_ver's spec and there is no runc to fall back to"
exit 1
}
sudo install -d /etc/containers/containers.conf.d
printf '[engine]\nruntime = "runc"\n' |
sudo tee /etc/containers/containers.conf.d/10-tbox-runc.conf
podman info --format '>>> OCI runtime: {{.Host.OCIRuntime.Name}} {{.Host.OCIRuntime.Version}}'
fi
- name: build tacklebox
run: go build -o tacklebox ./cmd/tacklebox
# Persist the image-ID-keyed build caches across runs. tacklebox keys
# cache entries by image ID internally, so restoring a stale snapshot
# is harmless (changed images simply miss). Rolling key: every run
# saves a fresh snapshot, restore picks the most recent. Saves the
# ~2 min/env dracut initramfs rebuild on cache-warm runs.
- name: prepare output base for cache restore
run: sudo install -d -o "$(id -u)" -g "$(id -g)" /mnt/tbx
- name: restore build caches
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
/mnt/tbx/initramfs-cache
/mnt/tbx/squashfs-cache
key: tbox-build-cache-${{ github.run_id }}
restore-keys: |
tbox-build-cache-
- name: pre-pull fixture images
# Quay CDN occasionally drops blobs mid-transfer (unexpected EOF).
# Retry up to 3 times with exponential backoff.
run: |
for img in quay.io/centos-bootc/centos-bootc:stream10 quay.io/fedora/fedora-bootc:44; do
ok=0
for i in 1 2 3; do
echo ">>> pull $img (attempt $i/3)"
if sudo podman pull "$img"; then ok=1; break; fi
sleep $((i * 10))
done
if [ "$ok" -eq 0 ]; then
echo "::error::failed to pull $img after 3 attempts"
exit 1
fi
done
- name: tacklebox build (2-env block image)
run: |
sudo mkdir -p /mnt/tbx
sudo ./tacklebox build fixtures/smoke-2env.json -b /mnt/tbx
- name: tacklebox verify
run: sudo ./tacklebox verify /mnt/tbx/tacklebox.img
- name: tacklebox status check
run: sudo ./tacklebox status /mnt/tbx/tacklebox.img
# ── Stage 3b: update smoke ───────────────────────────────────────────
# Re-installs both envs on the already-built image using
# `tacklebox update`. This exercises:
# - loop-image attach (resolveDevice)
# - pre-flight unmount (UnmountDevice) on a loop device with
# no existing mounts — should be a clean no-op
# - full bootc re-install → BLS rewrite pipeline
# - verify that the result still passes after update
# Shares -b /mnt/tbx with the build step on purpose: the update must
# hit the initramfs cache the build populated minutes earlier (the
# cache-reuse regression check).
- name: tacklebox update (re-install same recipe)
run: |
sudo ./tacklebox update --yes fixtures/smoke-2env.json \
/mnt/tbx/tacklebox.img -b /mnt/tbx
- name: tacklebox verify (post-update)
run: sudo ./tacklebox verify /mnt/tbx/tacklebox.img
# ── Stage 3c: UnmountDevice loop smoke (integration test) ────────────
# Runs the Go integration test that creates a loop device, mounts a
# partition, and asserts UnmountDevice clears it. Faster than the full
# build smoke and directly exercises the new pre-flight unmount code.
- name: UnmountDevice loop smoke (integration)
run: go test -tags=integration -v -run=TestUnmountDevice_LoopSmoke ./internal/blockdev/...
# ── Stage 3d: add / remove smoke ────────────────────────────────────
# Adds a third env (using the already-pulled centos-bootc image) to
# the 2-env image, verifies the 3-env result, then removes the added
# env and verifies the image is back to 2-env. Exercises the full
# add/remove mutation pipeline without reformatting.
- name: tacklebox add (3rd env)
run: |
sudo ./tacklebox add fixtures/smoke-add.json \
/mnt/tbx/tacklebox.img --yes -b /mnt/tbx
- name: tacklebox verify (post-add, 3 envs)
run: sudo ./tacklebox verify /mnt/tbx/tacklebox.img
- name: tacklebox status (post-add)
run: sudo ./tacklebox status /mnt/tbx/tacklebox.img
- name: tacklebox remove (the added env)
run: |
sudo ./tacklebox remove centos-add \
/mnt/tbx/tacklebox.img --yes -b /mnt/tbx
- name: tacklebox verify (post-remove, back to 2 envs)
run: sudo ./tacklebox verify /mnt/tbx/tacklebox.img
# Stage 4: Boot smoke. We use TCG (no KVM on free runners) so it's
# slow (~3 min to login).
- name: tacklebox boot smoke (Stage 4)
run: sudo ./scripts/test-boot.sh /mnt/tbx/tacklebox.img 600
- name: upload boot log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: verify-smoke-boot-log
path: qemu-boot.log
if-no-files-found: ignore
- name: artifact summary
if: always()
run: |
echo '## tacklebox build artefacts' >> "$GITHUB_STEP_SUMMARY"
ls -lh /mnt/tbx/ 2>/dev/null | awk 'NR>1 {print "- `" $NF "` (" $5 ")"}' >> "$GITHUB_STEP_SUMMARY"
df -h / /mnt 2>/dev/null >> "$GITHUB_STEP_SUMMARY"
if [ -f qemu-boot.log ]; then
echo '### QEMU Boot Log (tail)' >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
tail -n 50 qemu-boot.log >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
fi
# ── Stage 3-ISO: two-env ISO smoke (per-env + dedup layouts) ──────────────
# Builds BOTH ISO layouts from one pair of fixture images (STOCK
# fedora-bootc + a per-env marker, built in-job so the localhost/
# user-store path is exercised), verifies them, asserts that the dedup
# layout actually shrinks the ISO, and QEMU-boots each layout. The stock
# fixture (no dracut-live) is the regression test for the embedded
# tbox-live module (#90). The two recipes pick different default_boot
# envs on purpose so both envs get boot coverage:
# - per-env layout boots beta (plain tbox-live path)
# - dedup layout boots alpha and must log the tbox-root subtree pivot
# No actions/cache here: the fixture images are rebuilt every run, so their
# image IDs (and thus every tacklebox cache key) change every run.
iso-smoke:
# RunsOn build-amd64 (controlled ubuntu24-full-x64 image): the GitHub
# runner's podman 5.8.4 wedges the customize commit and its crun/conmon
# cannot be re-pinned (tuna-os/tunaOS#1893). The `runs-on=` marker + `runner=`
# group is RunsOn's label format (see docs/runners/labels).
runs-on: runs-on=${{ github.run_id }}/runner=build-amd64
needs: lint-test
timeout-minutes: 60
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
- name: free disk space
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # main as of 2026-06-09
with:
tool-cache: false
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: true
swap-storage: false
- name: install build deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
xorriso mtools squashfs-tools dosfstools \
systemd-boot systemd-boot-efi podman \
qemu-system-x86 ovmf
# Same crun/podman spec-version mismatch as verify-smoke; see there.
- name: match podman's OCI runtime to its spec version
run: |
podman_ver=$(podman --version | awk '{print $3}')
crun_ver=$(crun --version 2>/dev/null | awk 'NR==1{print $3}')
echo ">>> podman=$podman_ver crun=${crun_ver:-absent}"
if dpkg --compare-versions "$podman_ver" ge 5 &&
[ -n "$crun_ver" ] && dpkg --compare-versions "$crun_ver" lt 1.15; then
command -v runc >/dev/null || {
echo "::error::crun $crun_ver cannot run podman $podman_ver's spec and there is no runc to fall back to"
exit 1
}
sudo install -d /etc/containers/containers.conf.d
printf '[engine]\nruntime = "runc"\n' |
sudo tee /etc/containers/containers.conf.d/10-tbox-runc.conf
podman info --format '>>> OCI runtime: {{.Host.OCIRuntime.Name}} {{.Host.OCIRuntime.Version}}'
fi
# podman 5.8.4 (what both the GitHub runner and the ubuntu24 RunsOn image
# ship) is broken for this workload: the customize commit wedges and
# `podman build` fails with "cannot re-exec process". Install the Kubic
# unstable podman (6.x) and drop the image's 5.8.4 shadow from
# /usr/local/bin so the pinned podman wins on PATH.
- name: install podman 6.x from Kubic unstable
run: |
set -euo pipefail
sudo install -d /etc/apt/keyrings
curl -fsSL https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_24.04/Release.key \
| gpg --dearmor | sudo tee /etc/apt/keyrings/kubic.gpg >/dev/null
echo 'deb [signed-by=/etc/apt/keyrings/kubic.gpg] https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_24.04/ /' \
| sudo tee /etc/apt/sources.list.d/kubic.list
sudo apt-get update -qq
# Force the Kubic stack: apt kept the image's 4.9.3+conmon2.1.10
# (conmon journald failure). Remove the old podman/conmon so the
# Kubic podman 6.x + matching conmon/crun come in fresh.
sudo apt-get remove -y podman conmon crun || true
sudo apt-get install -y podman
# conmon 2.1.10 on this image lacks journald support and dies on the
# customize run ("[conmon:e]: Include journald in compilation path").
# Force a file log driver so conmon never touches journald.
sudo install -d /etc/containers/containers.conf.d
printf '[engine]\nlog_driver = "k8s-file"\n' | \
sudo tee /etc/containers/containers.conf.d/10-tbox-log.conf
for p in /usr/local/bin/podman /usr/local/sbin/podman; do
if [ -e "$p" ] && ! dpkg -S "$p" >/dev/null 2>&1; then
echo ">>> removing non-apt podman shadow: $p"
sudo rm -f "$p"
fi
done
hash -r
echo ">>> podman now: $(podman --version) / conmon $(conmon --version 2>/dev/null | head -1 || echo none)"
- name: build tacklebox
run: go build -o tacklebox ./cmd/tacklebox
# Plain podman (no sudo): the images must land in the runner user's
# store, which is where the live pipeline reads from.
# k8s-file log driver for all podman runs resolves the runner journald
# gap (tuna-os/tunaOS#1893).
- name: build fixture live images
run: |
# sudo (root) podman: the RunsOn image's rootless re-exec is broken
# ("cannot re-exec process"); the tacklebox build below is root too.
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=alpha -t localhost/tbox-iso-alpha:latest fixtures
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=beta -t localhost/tbox-iso-beta:latest fixtures
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=gamma -t localhost/tbox-iso-gamma:latest fixtures
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=delta -t localhost/tbox-iso-delta:latest fixtures
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=epsilon -t localhost/tbox-iso-epsilon:latest fixtures
sudo podman build -f fixtures/iso-smoke.Containerfile \
--build-arg MARKER=zeta -t localhost/tbox-iso-zeta:latest fixtures
- name: build per-env ISO
run: |
sudo mkdir -p /mnt/tbx
sudo ./tacklebox build fixtures/iso-2env.json --iso /mnt/tbx/smoke.iso -b /mnt/tbx
- name: verify per-env ISO
run: ./tacklebox verify /mnt/tbx/smoke.iso
# Boot gate: verify checks structure; this checks the thing users
# do — boot it. Catches non-executable generators, sysroot.mount
# regressions, ESP/El Torito breakage, unmountable rootfs images.
- name: live boot gate (QEMU/OVMF to login)
run: sudo ./scripts/test-live-boot.sh /mnt/tbx/smoke.iso 900
- name: upload live boot log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: iso-smoke-live-boot-log
path: qemu-live-boot.log
if-no-files-found: ignore
# alpha carries live_customize (fixtures/live-customize-smoke.sh); beta
# doesn't. The marker must be inside alpha's squashed rootfs and absent
# from beta's — this exercises the full path: recipe parsing, script
# resolution, container run, commit, derived-image squash.
- name: assert live_customize applied
run: |
xorriso -osirrox on -indev /mnt/tbx/smoke.iso \
-extract /LiveOS/alpha.rootfs.sfs /tmp/alpha.sfs \
-extract /LiveOS/beta.rootfs.sfs /tmp/beta.sfs
unsquashfs -l /tmp/alpha.sfs 'usr/share/tbox-live-customize-marker' \
| grep -q tbox-live-customize-marker \
|| { echo "::error::alpha rootfs missing live_customize marker"; exit 1; }
if unsquashfs -l /tmp/beta.sfs 'usr/share/tbox-live-customize-marker' 2>/dev/null \
| grep -q tbox-live-customize-marker; then
echo "::error::beta (no live_customize) unexpectedly contains the marker"
exit 1
fi
echo "live_customize marker present in alpha, absent in beta"
rm -f /tmp/alpha.sfs /tmp/beta.sfs
- name: build dedup ISO
run: sudo ./tacklebox build fixtures/iso-dedup-2env.json --iso /mnt/tbx/dedup.iso -b /mnt/tbx
- name: verify dedup ISO
run: ./tacklebox verify /mnt/tbx/dedup.iso
# The fixture envs differ by one marker file, so the combined squashfs
# should be roughly half the size of two per-env squashfs files. The
# 90% bound is deliberately loose — it catches "dedup silently does
# nothing" without being flaky about compression noise.
- name: assert dedup shrinks the ISO
run: |
per_env=$(stat -c%s /mnt/tbx/smoke.iso)
dedup=$(stat -c%s /mnt/tbx/dedup.iso)
echo "per-env ISO: ${per_env} bytes; dedup ISO: ${dedup} bytes"
if [ "$dedup" -ge $((per_env * 9 / 10)) ]; then
echo "::error::dedup ISO (${dedup}) is not meaningfully smaller than per-env ISO (${per_env})"
exit 1
fi
# ── 6-env dedup smoke ────────────────────────────
- name: build 6-env dedup ISO
run: sudo ./tacklebox build fixtures/iso-dedup-6env.json --iso /mnt/tbx/dedup-6env.iso -b /mnt/tbx
- name: verify 6-env dedup ISO
run: ./tacklebox verify /mnt/tbx/dedup-6env.iso
# 6 nearly-identical envs should produce an ISO barely larger than 2.
# Bound: 6-env < 2 × 2-env (very conservative, real ratio is much
# tighter — each additional env adds only a marker file worth of
# unique content).
- name: assert 6-env dedup scales
run: |
dedup2=$(stat -c%s /mnt/tbx/dedup.iso)
dedup6=$(stat -c%s /mnt/tbx/dedup-6env.iso)
echo "2-env dedup ISO: ${dedup2} bytes; 6-env dedup ISO: ${dedup6} bytes"
if [ "$dedup6" -ge $((dedup2 * 2)) ]; then
echo "::error::6-env ISO (${dedup6}) is too large — expected ≤ 2× 2-env (${dedup2})"
exit 1
fi
echo "6-env/dedup2 ratio: $(echo "scale=2; $dedup6 / $dedup2" | bc)"
# ── delta layout smoke ──────────────────────────────────────────────
# dedup_layout=delta: alpha is the base squashfs, beta boots
# base + beta.delta.sfs stacked as overlay lowerdirs. The delta must
# be tiny (fixtures differ by one marker file) and must contain the
# marker — proof the tree-diff caught the divergent file.
- name: build delta ISO
run: sudo ./tacklebox build fixtures/iso-delta-2env.json --iso /mnt/tbx/delta.iso -b /mnt/tbx
- name: verify delta ISO
run: ./tacklebox verify /mnt/tbx/delta.iso
- name: assert delta is small and carries the marker
run: |
xorriso -osirrox on -indev /mnt/tbx/delta.iso \
-extract /LiveOS/beta.delta.sfs /tmp/beta.delta.sfs
size=$(stat -c%s /tmp/beta.delta.sfs)
echo "beta.delta.sfs: ${size} bytes"
# The fixtures differ by one marker file (plus initramfs-adjacent
# noise); anything over 64 MiB means the diff degenerated into a
# near-full copy.
if [ "$size" -ge $((64 * 1024 * 1024)) ]; then
echo "::error::beta.delta.sfs (${size}B) is far too large for a one-file diff"
exit 1
fi
unsquashfs -l /tmp/beta.delta.sfs usr/share/tbox-env-marker \
| grep -q tbox-env-marker \
|| { echo "::error::delta squashfs missing the env marker diff"; exit 1; }
rm -f /tmp/beta.delta.sfs
# ── offline store smoke ─────────────────────────────────────────────
# Builds a dedup ISO with offline_payloads that embeds container images
# as a separate store.squashfs.img in /LiveOS. The boot-time
# tbox-offline-store.mount unit picks this up for air-gapped installs.
- name: build offline-store ISO
run: |
sudo ./tacklebox build fixtures/iso-offline.json \
--iso /mnt/tbx/offline.iso -b /mnt/tbx
- name: verify offline-store ISO
run: ./tacklebox verify /mnt/tbx/offline.iso
# Boot all three ISOs in parallel to keep CI time under the 60-min
# budget. Each QEMU VM gets 2G RAM (enough for a non-interactive
# boot to login prompt). Output tee'd to step log + per-ISO file.
- name: boot smoke, all ISOs (parallel)
run: |
fail=0
# 6-env dedup ISO: gamma env with tbox-root pivot
( QEMU_LOG=qemu-iso-6env.log ./scripts/test-boot.sh /mnt/tbx/dedup-6env.iso 600 \
"tacklebox.env=gamma" "Tacklebox: rebased OK" \
2>&1 | tee -a qemu-iso-6env.out ) &
pid1=$!
# per-env ISO: beta env (non-dedup path)
( QEMU_LOG=qemu-iso-perenv.log ./scripts/test-boot.sh /mnt/tbx/smoke.iso 600 \
"tacklebox.env=beta" \
2>&1 | tee -a qemu-iso-perenv.out ) &
pid2=$!
# dedup ISO: alpha env with tbox-root pivot
( QEMU_LOG=qemu-iso-dedup.log ./scripts/test-boot.sh /mnt/tbx/dedup.iso 600 \
"tacklebox.env=alpha" "Tacklebox: rebased OK" \
2>&1 | tee -a qemu-iso-dedup.out ) &
pid3=$!
# delta ISO: beta env boots base + beta.delta.sfs lowerdir stack
( QEMU_LOG=qemu-iso-delta.log ./scripts/test-boot.sh /mnt/tbx/delta.iso 600 \
"tacklebox.env=beta" "tacklebox.live.delta=beta.delta.sfs" \
2>&1 | tee -a qemu-iso-delta.out ) &
pid4=$!
wait $pid1 || { echo "::error::6-env dedup ISO boot failed"; fail=1; }
wait $pid2 || { echo "::error::per-env ISO boot failed"; fail=1; }
wait $pid3 || { echo "::error::dedup ISO boot failed"; fail=1; }
wait $pid4 || { echo "::error::delta ISO boot failed"; fail=1; }
exit $fail
- name: upload boot logs
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: iso-smoke-boot-logs
path: qemu-iso-*.log
if-no-files-found: ignore
- name: artifact summary
if: always()
run: |
echo '## iso-smoke artefacts' >> "$GITHUB_STEP_SUMMARY"
ls -lh /mnt/tbx/*.iso 2>/dev/null | awk '{print "- `" $NF "` (" $5 ")"}' >> "$GITHUB_STEP_SUMMARY"
for log in qemu-iso-perenv.log qemu-iso-dedup.log qemu-iso-6env.log; do
if [ -f "$log" ]; then
echo "### $log (tail)" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
tail -n 30 "$log" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
fi
done