fix(iso): fail builds on missing VPN binaries, skip units cleanly, invalidate stale rootfs cache
v1.7.104 shipped ISOs whose units crash-looped (nostr-relay 3s loop, archipelago-diag 203/EXEC) because build-time extraction failures were mere warnings and the cached rootfs never tracked its recipe: - hash the rootfs-defining region of the build script and rebuild when it changes (stale cache shipped ISOs without wpasupplicant/iw/rfkill) - refuse to build without nvpn / nostr-rs-relay unless ALLOW_MISSING_VPN_BINARIES=1 - ConditionPathExists on nostr-relay/nostr-vpn/diag units so a stripped image skips them instead of crash-looping - post-install test: every enabled archipelago*/nostr* unit must have an existing ExecStart payload Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
cf4a8eef0e
commit
ae12ff2517
@ -254,8 +254,17 @@ container_pull() {
|
|||||||
echo "📦 Step 1: Building root filesystem..."
|
echo "📦 Step 1: Building root filesystem..."
|
||||||
|
|
||||||
ROOTFS_TAR="$WORK_DIR/archipelago-rootfs.tar"
|
ROOTFS_TAR="$WORK_DIR/archipelago-rootfs.tar"
|
||||||
|
ROOTFS_STAMP="$WORK_DIR/archipelago-rootfs.recipe.sha256"
|
||||||
|
|
||||||
if [ ! -f "$ROOTFS_TAR" ] || [ "$1" == "--rebuild" ]; then
|
# The cached rootfs must be invalidated when its recipe changes: a stale
|
||||||
|
# archipelago-rootfs.tar on the build machine shipped ISOs with NO
|
||||||
|
# wpasupplicant/iw/rfkill (WiFi dead on laptops) long after those packages
|
||||||
|
# were added to the Dockerfile below — the cache condition never looked at
|
||||||
|
# the recipe. Hash the rootfs-defining region of this script; any edit to it
|
||||||
|
# forces a rebuild. `--rebuild` still forces one unconditionally.
|
||||||
|
RECIPE_HASH=$(sed -n '/^# STEP 1: Build complete root filesystem/,/^# STEP 2: Build minimal installer/p' "$0" | sha256sum | cut -d' ' -f1)
|
||||||
|
|
||||||
|
if [ ! -f "$ROOTFS_TAR" ] || [ "${1:-}" == "--rebuild" ] || [ "$(cat "$ROOTFS_STAMP" 2>/dev/null)" != "$RECIPE_HASH" ]; then
|
||||||
echo " Using Docker to create Debian root filesystem..."
|
echo " Using Docker to create Debian root filesystem..."
|
||||||
|
|
||||||
# Create a Dockerfile for building the rootfs
|
# Create a Dockerfile for building the rootfs
|
||||||
@ -694,6 +703,7 @@ SYSTEMDSERVICE
|
|||||||
$CONTAINER_CMD export archipelago-rootfs-tmp > "$ROOTFS_TAR"
|
$CONTAINER_CMD export archipelago-rootfs-tmp > "$ROOTFS_TAR"
|
||||||
$CONTAINER_CMD rm archipelago-rootfs-tmp
|
$CONTAINER_CMD rm archipelago-rootfs-tmp
|
||||||
|
|
||||||
|
echo "$RECIPE_HASH" > "$ROOTFS_STAMP"
|
||||||
echo "✅ Root filesystem created: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
echo "✅ Root filesystem created: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
||||||
else
|
else
|
||||||
echo "✅ Using cached root filesystem: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
echo "✅ Using cached root filesystem: $(du -h "$ROOTFS_TAR" | cut -f1)"
|
||||||
@ -1218,6 +1228,23 @@ else
|
|||||||
echo " ⚠ nostr-rs-relay image not available — relay binary will be missing"
|
echo " ⚠ nostr-rs-relay image not available — relay binary will be missing"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# A missing nvpn/nostr-rs-relay used to be a warning, and the resulting ISO
|
||||||
|
# shipped units that crash-looped (or silently lacked VPN signaling) on every
|
||||||
|
# install. Refuse to produce that ISO unless explicitly overridden.
|
||||||
|
MISSING_VPN_BINARIES=""
|
||||||
|
[ -f "$ARCH_DIR/bin/nvpn" ] || MISSING_VPN_BINARIES="$MISSING_VPN_BINARIES nvpn"
|
||||||
|
[ -f "$ARCH_DIR/bin/nostr-rs-relay" ] || MISSING_VPN_BINARIES="$MISSING_VPN_BINARIES nostr-rs-relay"
|
||||||
|
if [ -n "$MISSING_VPN_BINARIES" ]; then
|
||||||
|
if [ "${ALLOW_MISSING_VPN_BINARIES:-0}" = "1" ]; then
|
||||||
|
echo " ⚠ Building WITHOUT:$MISSING_VPN_BINARIES (ALLOW_MISSING_VPN_BINARIES=1)"
|
||||||
|
else
|
||||||
|
echo " ❌ Required binaries not extracted:$MISSING_VPN_BINARIES"
|
||||||
|
echo " The registry (146.59.87.168:3000) must be reachable and hold the images,"
|
||||||
|
echo " or set ALLOW_MISSING_VPN_BINARIES=1 to ship without VPN signaling."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Copy WireGuard helper script
|
# Copy WireGuard helper script
|
||||||
if [ -f "$WORK_DIR/archipelago-wg" ]; then
|
if [ -f "$WORK_DIR/archipelago-wg" ]; then
|
||||||
cp "$WORK_DIR/archipelago-wg" "$ARCH_DIR/bin/archipelago-wg"
|
cp "$WORK_DIR/archipelago-wg" "$ARCH_DIR/bin/archipelago-wg"
|
||||||
@ -3345,6 +3372,11 @@ echo ""
|
|||||||
echo "=== Done ==="
|
echo "=== Done ==="
|
||||||
DIAGSCRIPT
|
DIAGSCRIPT
|
||||||
chmod +x /mnt/target/opt/archipelago/scripts/first-boot-diag.sh
|
chmod +x /mnt/target/opt/archipelago/scripts/first-boot-diag.sh
|
||||||
|
# v1.7.104 shipped installs where this script was missing while its unit was
|
||||||
|
# enabled (203/EXEC forever). Verify the write actually landed, loudly.
|
||||||
|
if [ ! -x /mnt/target/opt/archipelago/scripts/first-boot-diag.sh ]; then
|
||||||
|
echo "ERROR: first-boot-diag.sh was not written to the target" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
# Systemd oneshot service for first-boot diagnostics
|
# Systemd oneshot service for first-boot diagnostics
|
||||||
cat > /mnt/target/etc/systemd/system/archipelago-diag.service <<'DIAGSVC'
|
cat > /mnt/target/etc/systemd/system/archipelago-diag.service <<'DIAGSVC'
|
||||||
@ -3352,6 +3384,8 @@ cat > /mnt/target/etc/systemd/system/archipelago-diag.service <<'DIAGSVC'
|
|||||||
Description=Archipelago First Boot Diagnostics
|
Description=Archipelago First Boot Diagnostics
|
||||||
After=multi-user.target archipelago.service nginx.service
|
After=multi-user.target archipelago.service nginx.service
|
||||||
ConditionPathExists=!/var/log/archipelago-first-boot-diag.log
|
ConditionPathExists=!/var/log/archipelago-first-boot-diag.log
|
||||||
|
# Skip cleanly (instead of failing 203/EXEC) if the script is missing.
|
||||||
|
ConditionPathExists=/opt/archipelago/scripts/first-boot-diag.sh
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
|||||||
@ -3,6 +3,9 @@ Description=Archipelago Private Nostr Relay
|
|||||||
After=network-online.target
|
After=network-online.target
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
Before=nostr-vpn.service
|
Before=nostr-vpn.service
|
||||||
|
# An ISO built without the relay binary (registry unreachable at build time)
|
||||||
|
# must not crash-loop every 3s forever — skip cleanly instead.
|
||||||
|
ConditionPathExists=/usr/local/bin/nostr-rs-relay
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@ -4,6 +4,9 @@ After=network-online.target tor.service archipelago.service
|
|||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
StartLimitIntervalSec=300
|
StartLimitIntervalSec=300
|
||||||
StartLimitBurst=10
|
StartLimitBurst=10
|
||||||
|
# An ISO built without the nvpn binary (registry unreachable at build time)
|
||||||
|
# must not restart-loop — skip cleanly instead.
|
||||||
|
ConditionPathExists=/usr/local/bin/nvpn
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@ -103,6 +103,23 @@ for f in /usr/local/bin/archipelago \
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# 1.1b — Every enabled archipelago/nostr unit must have an existing ExecStart
|
||||||
|
# payload. v1.7.104 shipped nostr-relay.service without its binary (3s
|
||||||
|
# crash-loop forever) and archipelago-diag.service without its script
|
||||||
|
# (203/EXEC) — catch that class of ISO defect on first boot, by generic rule.
|
||||||
|
for unit in /etc/systemd/system/archipelago*.service /etc/systemd/system/nostr*.service; do
|
||||||
|
[ -f "$unit" ] || continue
|
||||||
|
systemctl is-enabled "$(basename "$unit")" >/dev/null 2>&1 || continue
|
||||||
|
exec_bin=$(grep -m1 '^ExecStart=' "$unit" | sed 's/^ExecStart=[-+@!:]*//' | awk '{print $1}')
|
||||||
|
case "$exec_bin" in
|
||||||
|
/*) if [ -e "$exec_bin" ]; then
|
||||||
|
pass "Unit payload exists: $(basename "$unit") → $exec_bin"
|
||||||
|
else
|
||||||
|
fail "Unit payload missing" "$(basename "$unit") → $exec_bin"
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# 1.2 — Critical services active
|
# 1.2 — Critical services active
|
||||||
for svc in archipelago nginx; do
|
for svc in archipelago nginx; do
|
||||||
if systemctl is-active "$svc" >/dev/null 2>&1; then
|
if systemctl is-active "$svc" >/dev/null 2>&1; then
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user