The first publish spent 9 min 42 s in the download action for the 4.6 GB stable release; the 11.5 GB edge release would not have fitted in the job's 45 minutes. GitHub's artifact storage limits each connection to a few megabytes a second, and the action uses few. An artifact is one zip at a signed address. test/vm-image/nightly-fetch asks the API for that address and takes it with aria2c over 16 connections, checks the size and unpacks it. The same 4.6 GB artifact came down in 123 s that way. The publish job gets actions: read for it, and checks out test/vm-image for the script. test/unit/nightly-test.sh plays the network with stand-ins: the newest artifact of a name is the one fetched, a failed download is tried again with a new address, a wrong size is refused and not unpacked.
210 lines
14 KiB
Bash
210 lines
14 KiB
Bash
#!/bin/bash
|
|
#
|
|
# The nightly's decisions, tried without a runner: which channels a run makes
|
|
# and where they go (test/vm-image/nightly-plan), what it lays out and what
|
|
# latest.json then says (test/vm-image/nightly-layout), and two things the
|
|
# install test decides from what it is given: which Omarchy packages an ISO
|
|
# installs, and when an install is over.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
|
|
plan="$ROOT/test/vm-image/nightly-plan"
|
|
layout="$ROOT/test/vm-image/nightly-layout"
|
|
harness="$ROOT/test/integration.d/base-test.sh"
|
|
bash -n "$plan"
|
|
bash -n "$layout"
|
|
|
|
tmp=$(mktemp -d)
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
fail() { echo "FAIL: $*" >&2; exit 1; }
|
|
equal() { [[ $2 == "$3" ]] || fail "$1: got '$2', expected '$3'"; }
|
|
refused() { # description, command...: it has to fail and say why
|
|
local what=$1; shift
|
|
if "$@" >"$tmp/refused.out" 2>"$tmp/refused.err"; then fail "$what was accepted"; fi
|
|
[[ -s $tmp/refused.err ]] || fail "$what was refused without a word"
|
|
}
|
|
|
|
# ---------------------------------------------------------------- nightly-plan
|
|
planned() { "$plan" "$@" 2>/dev/null; }
|
|
|
|
first=$(planned 2026.10.09 431 1 schedule v4.0.4 "")
|
|
equal "a first run makes both channels" "$(jq -r 'map(.channel) | join(",")' <<<"$first")" "edge,stable"
|
|
equal "the edge version" "$(jq -r '.[0].version' <<<"$first")" "2026.10.09.431"
|
|
equal "the edge path" "$(jq -r '.[0].release' <<<"$first")" "edge/2026.10.09.431"
|
|
equal "the stable version is the release's" "$(jq -r '.[1].version' <<<"$first")" "4.0.4"
|
|
equal "the stable path has the run number" "$(jq -r '.[1].release' <<<"$first")" "stable/4.0.4/431"
|
|
|
|
again=$(planned 2026.10.09 431 2 schedule v4.0.4 "")
|
|
equal "a run started again has its own edge version" "$(jq -r '.[0].version' <<<"$again")" "2026.10.09.431.2"
|
|
equal "a run started again has its own stable path" "$(jq -r '.[1].release' <<<"$again")" "stable/4.0.4/431.2"
|
|
[[ $(jq -r '.[].release' <<<"$first" | sort) != "$(jq -r '.[].release' <<<"$again" | sort)" ]] ||
|
|
fail "two attempts of one run share a path"
|
|
|
|
equal "a published release is left out of a scheduled run" \
|
|
"$(planned 2026.10.09 432 1 schedule v4.0.4 4.0.4 | jq -r 'map(.channel) | join(",")')" "edge"
|
|
equal "a new release is made on the next scheduled run" \
|
|
"$(planned 2026.10.09 432 1 schedule v4.0.5 4.0.4 | jq -r '.[1].version')" "4.0.5"
|
|
equal "a manual run makes a published release again" \
|
|
"$(planned 2026.10.09 432 1 workflow_dispatch v4.0.4 4.0.4 | jq -r 'map(.channel) | join(",")')" "edge,stable"
|
|
|
|
refused "a release tag that is not a version" "$plan" 2026.10.09 431 1 schedule nightly ""
|
|
refused "an empty release tag" "$plan" 2026.10.09 431 1 schedule "" ""
|
|
refused "a release tag with a path in it" "$plan" 2026.10.09 431 1 schedule "v4.0.4/../x" ""
|
|
refused "a run number that is not a number" "$plan" 2026.10.09 "" 1 schedule v4.0.4 ""
|
|
refused "a date in another form" "$plan" 2026-10-09 431 1 schedule v4.0.4 ""
|
|
|
|
# -------------------------------------------------------------- nightly-layout
|
|
sealed() { # directory: what vm-seal leaves
|
|
rm -rf "$1"; mkdir -p "$1"
|
|
local file
|
|
for file in base.qcow2 vmlinuz initrd cmdline; do echo "$file of ${1##*/}" >"$1/$file"; done
|
|
echo "sums of the seal" >"$1/SHA256SUMS"
|
|
}
|
|
export SITE=https://nightly.example ISO_SITE=https://iso.example ISO_COMMIT=0123456789abcdef0123456789abcdef01234567
|
|
|
|
# Edge. pacman answers for omarchy and omarchy-dev with the one installed
|
|
# package, so its line is there twice.
|
|
mkdir -p "$tmp/release"
|
|
echo "an edge iso" >"$tmp/release/omarchy-2026.10.09-x86_64-edge.iso"
|
|
sealed "$tmp/sealed-edge"
|
|
printf '%s\n' "omarchy-dev 4.0.0.r6818.gc352b62-2" "omarchy-dev 4.0.0.r6818.gc352b62-2" \
|
|
"/usr/lib/modules/7.2.8-5-omarchy-bore/pkgbase is owned by linux-omarchy-bore 7.2.8-5" "7.2.8-5-omarchy-bore" >"$tmp/edge-packages"
|
|
"$layout" edge 2026.10.09.431 edge/2026.10.09.431 "$tmp/release/omarchy-2026.10.09-x86_64-edge.iso" \
|
|
"$tmp/sealed-edge" "$tmp/edge-packages" "$tmp/out" >/dev/null
|
|
json=$tmp/out/edge/latest.json
|
|
dir=$tmp/out/edge/2026.10.09.431
|
|
field() { jq -r "$1" "$json"; }
|
|
|
|
equal "edge: files published" "$(cd "$tmp/out" && find . -type f | sort | paste -sd' ')" \
|
|
"./edge/2026.10.09.431/omarchy-edge-2026.10.09.431.iso ./edge/2026.10.09.431/omarchy-edge-2026.10.09.431.iso.sha256 ./edge/2026.10.09.431/omarchy-edge-2026.10.09.431.qcow2 ./edge/2026.10.09.431/omarchy-edge-2026.10.09.431.qcow2.sha256 ./edge/2026.10.09.431/unpacked/SHA256SUMS ./edge/2026.10.09.431/unpacked/cmdline ./edge/2026.10.09.431/unpacked/initrd ./edge/2026.10.09.431/unpacked/vm-boot ./edge/2026.10.09.431/unpacked/vmlinuz ./edge/latest.json"
|
|
(cd "$dir" && sha256sum -c --quiet ./*.sha256) || fail "edge: a checksum file does not match the file it is for"
|
|
equal "edge: the ISO and the image each have a checksum file" "$(cd "$dir" && ls ./*.sha256 | wc -l)" "2"
|
|
equal "edge: the image is the sealed disk" "$(cat "$dir/omarchy-edge-2026.10.09.431.qcow2")" "base.qcow2 of sealed-edge"
|
|
(cd "$dir/unpacked" && sha256sum -c --quiet SHA256SUMS) || fail "edge: SHA256SUMS does not match the unpacked files"
|
|
equal "edge: SHA256SUMS covers every file beside it" \
|
|
"$(awk '{ print $2 }' "$dir/unpacked/SHA256SUMS" | sort | paste -sd' ')" "$(cd "$dir/unpacked" && ls | grep -v SHA256SUMS | sort | paste -sd' ')"
|
|
cmp -s "$dir/unpacked/vm-boot" "$ROOT/test/vm-image/vm-boot" || fail "edge: the published vm-boot is not the repository's"
|
|
equal "edge: version" "$(field .version)" "2026.10.09.431"
|
|
equal "edge: channel" "$(field .channel)" "edge"
|
|
equal "edge: url" "$(field .url)" "https://nightly.example/edge/2026.10.09.431/omarchy-edge-2026.10.09.431.iso"
|
|
equal "edge: sha256 is the ISO's" "$(field .sha256)" "$(sha256sum "$dir/omarchy-edge-2026.10.09.431.iso" | cut -d' ' -f1)"
|
|
equal "edge: size is the ISO's" "$(field .size)" "$(stat -c %s "$dir/omarchy-edge-2026.10.09.431.iso")"
|
|
equal "edge: iso_commit" "$(field .iso_commit)" "$ISO_COMMIT"
|
|
equal "edge: omarchy_version, once" "$(field .omarchy_version)" "4.0.0.r6818.gc352b62-2"
|
|
equal "edge: omarchy_commit" "$(field .omarchy_commit)" "c352b62"
|
|
equal "edge: kernel" "$(field .kernel)" "7.2.8-5-omarchy-bore"
|
|
equal "edge: image address" "$(field .vm.url)" "https://nightly.example/edge/2026.10.09.431/omarchy-edge-2026.10.09.431.qcow2"
|
|
equal "edge: image checksum is the image's" "$(field .vm.sha256)" "$(sha256sum "$dir/omarchy-edge-2026.10.09.431.qcow2" | cut -d' ' -f1)"
|
|
equal "edge: image size is the image's" "$(field .vm.size)" "$(stat -c %s "$dir/omarchy-edge-2026.10.09.431.qcow2")"
|
|
equal "edge: the ISO and the image differ only in their ending" "$(field .url | sed 's/\.iso$//')" "$(field .vm.url | sed 's/\.qcow2$//')"
|
|
equal "edge: unpacked folder" "$(field .vm.unpacked)" "https://nightly.example/edge/2026.10.09.431/unpacked/"
|
|
equal "edge: every unpacked file listed is published" "$(field '.vm.unpacked_files[]' | sort | paste -sd' ')" "$(cd "$dir/unpacked" && ls | sort | paste -sd' ')"
|
|
[[ $(field .built_at) =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9:]{8}Z$ ]] || fail "edge: built_at is '$(field .built_at)'"
|
|
# What a consumer does with the ISO's name: the first run of three or more
|
|
# dotted numbers is the version.
|
|
[[ $(basename "$(field .url)") =~ [0-9]+(\.[0-9]+){2,} ]] || fail "edge: the ISO's name carries no version"
|
|
equal "edge: the version read from the ISO's name" "${BASH_REMATCH[0]}" "2026.10.09.431"
|
|
|
|
# Stable: the image is published, the released ISO stays where it is.
|
|
rm -rf "$tmp/out"
|
|
echo "a released iso" >"$tmp/release/omarchy-4.0.4.iso"
|
|
sealed "$tmp/sealed-stable"
|
|
printf '%s\n' "omarchy 4.0.4-1" "/usr/lib/modules/7.2.5-3-omarchy/pkgbase is owned by linux-omarchy 7.2.5-3" "7.2.5-3-omarchy" >"$tmp/stable-packages"
|
|
"$layout" stable 4.0.4 stable/4.0.4/431 "$tmp/release/omarchy-4.0.4.iso" "$tmp/sealed-stable" "$tmp/stable-packages" "$tmp/out" >/dev/null
|
|
json=$tmp/out/stable/latest.json
|
|
equal "stable: no ISO is published" "$(find "$tmp/out" -name '*.iso*' | wc -l)" "0"
|
|
[[ -f $tmp/release/omarchy-4.0.4.iso ]] || fail "stable: the released ISO was moved away"
|
|
equal "stable: url is the release's" "$(field .url)" "https://iso.example/omarchy-4.0.4.iso"
|
|
equal "stable: sha256 is the ISO's" "$(field .sha256)" "$(sha256sum "$tmp/release/omarchy-4.0.4.iso" | cut -d' ' -f1)"
|
|
equal "stable: version" "$(field .version)" "4.0.4"
|
|
equal "stable: omarchy_version" "$(field .omarchy_version)" "4.0.4-1"
|
|
equal "stable: a release names no commit" "$(field .omarchy_commit)" ""
|
|
equal "stable: image address has the run number" "$(field .vm.url)" "https://nightly.example/stable/4.0.4/431/omarchy-4.0.4.qcow2"
|
|
equal "stable: unpacked folder" "$(field .vm.unpacked)" "https://nightly.example/stable/4.0.4/431/unpacked/"
|
|
(cd "$tmp/out/stable/4.0.4/431" && sha256sum -c --quiet omarchy-4.0.4.qcow2.sha256) || fail "stable: the image's checksum file does not match it"
|
|
(cd "$tmp/out/stable/4.0.4/431/unpacked" && sha256sum -c --quiet SHA256SUMS) || fail "stable: SHA256SUMS does not match the unpacked files"
|
|
|
|
# Nothing half-made is described.
|
|
sealed "$tmp/sealed-short"; rm "$tmp/sealed-short/initrd"
|
|
refused "a sealed image without its initrd" "$layout" stable 4.0.4 stable/4.0.4/9 "$tmp/release/omarchy-4.0.4.iso" "$tmp/sealed-short" "$tmp/stable-packages" "$tmp/out2"
|
|
sealed "$tmp/sealed-x"; : >"$tmp/no-packages"
|
|
refused "an image that named no Omarchy package" "$layout" stable 4.0.4 stable/4.0.4/9 "$tmp/release/omarchy-4.0.4.iso" "$tmp/sealed-x" "$tmp/no-packages" "$tmp/out3"
|
|
sealed "$tmp/sealed-y"
|
|
refused "a released ISO under another version's name" "$layout" stable 4.0.5 stable/4.0.5/9 "$tmp/release/omarchy-4.0.4.iso" "$tmp/sealed-y" "$tmp/stable-packages" "$tmp/out4"
|
|
sealed "$tmp/sealed-z"
|
|
refused "a channel that does not exist" "$layout" rc 4.0.4 rc/4.0.4 "$tmp/release/omarchy-4.0.4.iso" "$tmp/sealed-z" "$tmp/stable-packages" "$tmp/out5"
|
|
|
|
# --------------------------------------------------------------- nightly-fetch
|
|
# The network is played by three stand-ins: gh lists the artifacts, curl gives
|
|
# the signed address, aria2c "downloads" a zip made here.
|
|
fetch="$ROOT/test/vm-image/nightly-fetch"
|
|
bash -n "$fetch"
|
|
if command -v zip >/dev/null && command -v unzip >/dev/null; then
|
|
stubs=$tmp/stubs; mkdir -p "$stubs" "$tmp/payload/edge/1"
|
|
echo "an image" >"$tmp/payload/edge/1/omarchy-edge-1.qcow2"
|
|
echo '{"version": "1"}' >"$tmp/payload/edge/latest.json"
|
|
(cd "$tmp/payload" && zip -qr "$tmp/artifact.zip" .)
|
|
zip_size=$(stat -c %s "$tmp/artifact.zip")
|
|
cat >"$stubs/gh" <<STUB
|
|
#!/bin/bash
|
|
case "\$*" in
|
|
"auth token") echo token ;;
|
|
*"/runs/7/artifacts"*) printf '%s\\n' \${FETCH_IDS:-11 12} ;;
|
|
*"/artifacts/"*) echo \${FETCH_SIZE:-$zip_size} ;;
|
|
esac
|
|
STUB
|
|
cat >"$stubs/curl" <<'STUB'
|
|
#!/bin/bash
|
|
echo "${@: -1}" >>"$FETCH_LOG"; printf 'https://storage.example/signed'
|
|
STUB
|
|
cat >"$stubs/aria2c" <<STUB
|
|
#!/bin/bash
|
|
echo aria2c >>"\$FETCH_LOG"
|
|
if [[ -n \${FETCH_FAIL_ONCE:-} && ! -e $tmp/failed ]]; then touch $tmp/failed; exit 1; fi
|
|
for arg in "\$@"; do case \$arg in --dir=*) dir=\${arg#--dir=} ;; --out=*) out=\${arg#--out=} ;; esac; done
|
|
cp "$tmp/artifact.zip" "\$dir/\$out"
|
|
STUB
|
|
chmod +x "$stubs"/*
|
|
fetched() { PATH="$stubs:$PATH" TMPDIR="$tmp" FETCH_LOG="$tmp/fetch.log" "$@"; }
|
|
|
|
: >"$tmp/fetch.log"
|
|
fetched "$fetch" omacom/omarchy-iso 7 nightly-edge-7 "$tmp/fetched" >/dev/null
|
|
equal "fetch: the files of the artifact" "$(cd "$tmp/fetched" && find . -type f | sort | paste -sd' ')" "./edge/1/omarchy-edge-1.qcow2 ./edge/latest.json"
|
|
grep -q "/artifacts/12/zip" "$tmp/fetch.log" || fail "fetch: of two artifacts with one name, the newer was not the one fetched"
|
|
equal "fetch: nothing left behind" "$(ls "$tmp" | grep -c 'nightly-fetch\.')" "0"
|
|
|
|
: >"$tmp/fetch.log"; rm -f "$tmp/failed"
|
|
FETCH_FAIL_ONCE=1 fetched "$fetch" omacom/omarchy-iso 7 nightly-edge-7 "$tmp/fetched-again" >/dev/null 2>&1
|
|
equal "fetch: a failed download is tried again with a new address" "$(grep -c zip "$tmp/fetch.log")" "2"
|
|
|
|
refused "an artifact that does not exist" env FETCH_IDS=" " PATH="$stubs:$PATH" TMPDIR="$tmp" FETCH_LOG="$tmp/fetch.log" "$fetch" omacom/omarchy-iso 7 nightly-edge-7 "$tmp/f3"
|
|
refused "a download of the wrong size" env FETCH_SIZE=1 PATH="$stubs:$PATH" TMPDIR="$tmp" FETCH_LOG="$tmp/fetch.log" "$fetch" omacom/omarchy-iso 7 nightly-edge-7 "$tmp/f4"
|
|
[[ ! -e $tmp/f4/edge ]] || fail "fetch: a download of the wrong size was unpacked"
|
|
else
|
|
echo "nightly-fetch tests skipped: no zip or unzip here"
|
|
fi
|
|
|
|
# ------------------------------------------------------------ the install test
|
|
# The harness is a script that starts a VM when it is read; take the two
|
|
# functions out of it.
|
|
eval "$(sed -n '/^detect_packages() {$/,/^}$/p; /^installed_system_started() {$/,/^}$/p' "$harness")"
|
|
declare -F detect_packages installed_system_started >/dev/null || fail "the harness no longer has detect_packages and installed_system_started"
|
|
|
|
packages_of() { ISO=$1; detect_packages; echo "$RUNTIME_PACKAGE $SETTINGS_PACKAGE"; }
|
|
equal "an edge ISO installs the -dev packages" "$(packages_of release/omarchy-2026.10.09-x86_64-edge.iso)" "omarchy-dev omarchy-settings-dev"
|
|
equal "a dev ISO installs the -dev packages" "$(packages_of release/omarchy-2026.10.09-x86_64-dev.iso)" "omarchy-dev omarchy-settings-dev"
|
|
equal "a released ISO installs the release packages" "$(packages_of release/omarchy-4.0.4.iso)" "omarchy omarchy-settings"
|
|
equal "a stable build installs the release packages" "$(packages_of /x/edge/omarchy-2026.10.09-x86_64-stable.iso)" "omarchy omarchy-settings"
|
|
|
|
printf '%s\r\n' 'BdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00011 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x5,0xFFFF,0x0)' \
|
|
'BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00011 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x5,0xFFFF,0x0)' >"$tmp/serial"
|
|
if installed_system_started "$tmp/serial"; then fail "the installer's own boot counts as the installed system's"; fi
|
|
printf '%s\r\n' 'BdsDxe: loading Boot0005 "Limine" from HD(1,GPT,774175D2,0x800,0x400000)/\EFI\limine\limine_x64.efi' >>"$tmp/serial"
|
|
if installed_system_started "$tmp/serial"; then fail "loading Limine counts as having started it"; fi
|
|
printf '%s\r\n' 'BdsDxe: starting Boot0005 "Limine" from HD(1,GPT,774175D2,0x800,0x400000)/\EFI\limine\limine_x64.efi' >>"$tmp/serial"
|
|
installed_system_started "$tmp/serial" || fail "the installed system's Limine starting is not noticed"
|
|
if installed_system_started "$tmp/no-such-log"; then fail "a missing serial log counts as a started system"; fi
|
|
|
|
echo "nightly tests passed"
|