fix: production onboarding, CI tests, container security, keyboard nav
Some checks failed
Build Archipelago ISO / build-iso (push) Has been cancelled

Install & Onboarding:
- Remove DEV_MODE=true from production ISO service file (auto-created
  users, skipped password setup)
- Auto-install no longer overwrites rootfs service file with bad template
- Login.vue always checks auth.isSetup — shows password creation form
  on fresh install without requiring dev build flag
- Deploy image-versions.sh to /opt/archipelago/scripts/ on installed nodes
- First-boot-containers sources image-versions.sh, runs podman as
  archipelago user (rootless), enables linger + podman.socket
- Correct volume ownership (100000:100000 for rootless UID mapping)

Container Security:
- FileBrowser: add --cap-add=DAC_OVERRIDE for rootless podman volume access
- FileBrowser: add --read-only, /data volume for database, proper cmd args
- First-boot script matches backend config (security hardening + health check)

CI Pipeline:
- Add vue-tsc type check + vitest run to build-iso.yml (runs every push)
- Add post-install-tests.yml workflow (workflow_dispatch, SSH to target)
- Build report: set +eo pipefail, fix rootfs path, add || true guards
- Bundle run-post-install-tests.sh into ISO

E2E Test Suite (scripts/run-post-install-tests.sh):
- Phase 1: Install verification (files, services, podman, linger, DEV_MODE check)
- Phase 2: Onboarding flow (auth.isSetup, auth.setup, login, DID, complete)
- Phase 3: Container lifecycle (install 3 apps via package.install RPC,
  verify running, stop, verify stopped, restart, verify running, health)
- Phase 4: Log verification (first-boot log, diagnostics, journal errors)
- Correct package.install params: {"id", "dockerImage"}

Frontend:
- Fix backdrop-filter tab-switch bug (keep animations paused during rebuild)
- Dashboard glitch animations paused during tab-hidden
- Gamepad nav: auto-focus first container on route change
- Tab roving: Left/Right on role="tab" cycles and activates sibling tabs
- ContainerApps: data-controller-launch on running app cards
- 515 tests passing (fixed 30 broken, added 19 new keyboard nav tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-27 16:16:57 +00:00
parent b684c2972e
commit 127a36c5c8
16 changed files with 1134 additions and 552 deletions

View File

@@ -385,17 +385,19 @@ NGINXCONF
cat > "$WORK_DIR/archipelago.service" <<'SYSTEMDSERVICE'
[Unit]
Description=Archipelago Backend
After=network-online.target
After=network-online.target archipelago-setup-tor.service
Wants=network-online.target
[Service]
Type=simple
User=root
User=archipelago
Environment="ARCHIPELAGO_BIND=127.0.0.1:5678"
Environment="ARCHIPELAGO_DEV_MODE=true"
Environment="XDG_RUNTIME_DIR=/run/user/1000"
ExecStartPre=/bin/bash -c 'mkdir -p /run/user/1000 && chown archipelago:archipelago /run/user/1000 && chmod 700 /run/user/1000'
ExecStart=/usr/local/bin/archipelago
Restart=on-failure
RestartSec=5
ProtectHome=no
[Install]
WantedBy=multi-user.target
@@ -921,25 +923,56 @@ if [ "$UNBUNDLED" = "1" ]; then
#!/bin/bash
# Minimal first-boot: create FileBrowser container only (unbundled ISO)
set -e
DOCKER="podman"
LOG="/var/log/archipelago-first-boot.log"
echo "[$(date)] Starting minimal first-boot (unbundled)..." >> "$LOG"
# Create Cloud storage directories
mkdir -p /var/lib/archipelago/filebrowser
mkdir -p /var/lib/archipelago/data/cloud/{Documents,Photos,Music,Videos,Downloads}
chown -R 1000:1000 /var/lib/archipelago/filebrowser
chown -R 1000:1000 /var/lib/archipelago/data
# Source image versions (provides $FILEBROWSER_IMAGE etc.)
for f in /opt/archipelago/scripts/image-versions.sh /home/archipelago/archy/scripts/image-versions.sh; do
if [ -f "$f" ]; then
source "$f"
echo "[$(date)] Sourced image versions from $f" >> "$LOG"
break
fi
done
if ! $DOCKER ps -a --format '{{.Names}}' 2>/dev/null | grep -q filebrowser; then
echo "[$(date)] Creating FileBrowser container..." >> "$LOG"
$DOCKER run -d --name filebrowser --restart unless-stopped \
--health-cmd="curl -sf http://localhost:80/ || exit 1" \
if [ -z "$FILEBROWSER_IMAGE" ]; then
echo "[$(date)] ERROR: FILEBROWSER_IMAGE not set — image-versions.sh missing or incomplete" >> "$LOG"
exit 1
fi
# Create Cloud storage directories (as root, then fix ownership for rootless podman)
mkdir -p /var/lib/archipelago/filebrowser
mkdir -p /var/lib/archipelago/filebrowser-data
mkdir -p /var/lib/archipelago/data/cloud/{Documents,Photos,Music,Videos,Downloads}
# Container UID 0 maps to host UID 100000 under rootless podman (subuid mapping)
chown -R 100000:100000 /var/lib/archipelago/filebrowser
chown -R 100000:100000 /var/lib/archipelago/filebrowser-data
chown -R 100000:100000 /var/lib/archipelago/data
# Enable linger so rootless podman containers survive logout
loginctl enable-linger archipelago 2>/dev/null || true
# Ensure podman socket is active for archipelago user
runuser -u archipelago -- bash -c 'export XDG_RUNTIME_DIR=/run/user/1000 && systemctl --user enable --now podman.socket' 2>>"$LOG" || true
# Create FileBrowser container as archipelago user (rootless podman)
if ! runuser -u archipelago -- bash -c 'export XDG_RUNTIME_DIR=/run/user/1000 && podman ps -a --format "{{.Names}}"' 2>/dev/null | grep -q filebrowser; then
echo "[$(date)] Creating FileBrowser container ($FILEBROWSER_IMAGE)..." >> "$LOG"
runuser -u archipelago -- bash -c "export XDG_RUNTIME_DIR=/run/user/1000 && podman run -d --name filebrowser --restart unless-stopped \
--cap-drop=ALL \
--cap-add=DAC_OVERRIDE \
--security-opt=no-new-privileges:true \
--read-only \
--tmpfs=/tmp:rw,noexec,nosuid,size=64m \
--health-cmd='curl -sf http://localhost:80/ || exit 1' \
--health-interval=30s --health-timeout=5s --health-retries=3 \
--memory=256m \
-p 8083:80 \
-v /var/lib/archipelago/filebrowser:/srv \
"$FILEBROWSER_IMAGE" 2>>"$LOG" && \
-v /var/lib/archipelago/filebrowser-data:/data \
-v /var/lib/archipelago/data/cloud:/srv/cloud \
$FILEBROWSER_IMAGE \
--database=/data/database.db --root=/srv --address=0.0.0.0 --port=80" 2>>"$LOG" && \
echo "[$(date)] FileBrowser created successfully" >> "$LOG" || \
echo "[$(date)] WARNING: FileBrowser creation failed" >> "$LOG"
fi
@@ -1001,6 +1034,11 @@ if [ -f "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" ]; then
chmod +x "$ARCH_DIR/scripts/run-e2e-tests.sh"
echo " ✅ Bundled E2E test script for post-install validation"
fi
if [ -f "$SCRIPT_DIR/../scripts/run-post-install-tests.sh" ]; then
cp "$SCRIPT_DIR/../scripts/run-post-install-tests.sh" "$ARCH_DIR/scripts/"
chmod +x "$ARCH_DIR/scripts/run-post-install-tests.sh"
echo " ✅ Bundled post-install test suite"
fi
# Bundle self-update script and image-versions for update system
if [ -f "$SCRIPT_DIR/../scripts/self-update.sh" ]; then
@@ -1411,11 +1449,13 @@ mkdir -p /mnt/target/var/lib/archipelago/tor-config
mkdir -p /mnt/target/var/lib/archipelago/identities
mkdir -p /mnt/target/var/lib/archipelago/lnd
# Copy E2E test script for post-install validation
if [ -f "$BOOT_MEDIA/archipelago/scripts/run-e2e-tests.sh" ]; then
cp "$BOOT_MEDIA/archipelago/scripts/run-e2e-tests.sh" /mnt/target/opt/archipelago/scripts/
chmod +x /mnt/target/opt/archipelago/scripts/run-e2e-tests.sh
fi
# Copy test scripts for post-install validation
for test_script in run-e2e-tests.sh run-post-install-tests.sh; do
if [ -f "$BOOT_MEDIA/archipelago/scripts/$test_script" ]; then
cp "$BOOT_MEDIA/archipelago/scripts/$test_script" /mnt/target/opt/archipelago/scripts/
chmod +x /mnt/target/opt/archipelago/scripts/$test_script
fi
done
# Copy self-update script
if [ -f "$BOOT_MEDIA/archipelago/scripts/self-update.sh" ]; then
@@ -1427,6 +1467,15 @@ if [ -f "$BOOT_MEDIA/archipelago/scripts/self-update.sh" ]; then
chmod +x /mnt/target/home/archipelago/archy/scripts/self-update.sh
fi
# Copy image-versions.sh (needed by first-boot-containers and updates)
if [ -f "$BOOT_MEDIA/archipelago/scripts/image-versions.sh" ]; then
cp "$BOOT_MEDIA/archipelago/scripts/image-versions.sh" /mnt/target/opt/archipelago/scripts/
chmod +x /mnt/target/opt/archipelago/scripts/image-versions.sh
# Also place in home for container scripts to find
mkdir -p /mnt/target/home/archipelago/archy/scripts
cp "$BOOT_MEDIA/archipelago/scripts/image-versions.sh" /mnt/target/home/archipelago/archy/scripts/
fi
# Clone repo for git-based updates (first-boot will have network)
# Create a script that runs on first boot to clone the repo
cat > /mnt/target/opt/archipelago/scripts/setup-git-updates.sh <<'GITSETUP'
@@ -1513,26 +1562,12 @@ fi
PROFILE
chmod +x /mnt/target/etc/profile.d/archipelago.sh
# Systemd service: User=root required for Podman container access
cat > /mnt/target/etc/systemd/system/archipelago.service <<'SERVICE'
[Unit]
Description=Archipelago Backend
After=network-online.target archipelago-setup-tor.service
Wants=network-online.target
[Service]
Type=simple
User=root
Environment="ARCHIPELAGO_BIND=127.0.0.1:5678"
Environment="ARCHIPELAGO_DEV_MODE=true"
ExecStartPre=/bin/bash -c 'mkdir -p /etc/archipelago && echo "ARCHIPELAGO_HOST_IP=$(hostname -I 2>/dev/null | awk \"{print \$1}\")" > /etc/archipelago/host-ip.env'
ExecStart=/usr/local/bin/archipelago
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
SERVICE
# Systemd service: use the production version from rootfs (configs/archipelago.service)
# Do NOT overwrite — the rootfs already has the correct User=archipelago, no DEV_MODE version
if [ ! -f /mnt/target/etc/systemd/system/archipelago.service ]; then
echo " WARNING: archipelago.service missing from rootfs — copying from ISO"
cp "$BOOT_MEDIA/archipelago/configs/archipelago.service" /mnt/target/etc/systemd/system/archipelago.service 2>/dev/null || true
fi
# Claude API proxy — middleware that injects max_tokens, strips invalid fields
# API key must be set after install via setup-aiui-server.sh or manually
@@ -1942,6 +1977,19 @@ if [ ! -f /mnt/target/etc/archipelago/ssl/archipelago.crt ]; then
echo " Generated self-signed SSL certificate"
fi
# Enable linger for rootless podman (containers survive logout)
mkdir -p /mnt/target/var/lib/systemd/linger
touch /mnt/target/var/lib/systemd/linger/archipelago
# Enable podman socket for archipelago user (activated on first login/boot)
mkdir -p /mnt/target/home/archipelago/.config/systemd/user/sockets.target.wants
ln -sf /usr/lib/systemd/user/podman.socket /mnt/target/home/archipelago/.config/systemd/user/sockets.target.wants/podman.socket 2>/dev/null || true
chown -R 1000:1000 /mnt/target/home/archipelago/.config 2>/dev/null || true
# Ensure /run/user/1000 is created at boot for podman socket
mkdir -p /mnt/target/etc/tmpfiles.d
echo 'd /run/user/1000 0700 archipelago archipelago -' > /mnt/target/etc/tmpfiles.d/archipelago-runtime.conf
# Enable services
chroot /mnt/target systemctl enable archipelago.service 2>/dev/null || true
chroot /mnt/target systemctl enable nginx.service 2>/dev/null || true