chore(release): archive ISO build recipes, tarball-only releases
Releases no longer ship as bootable ISOs. Archipelago updates are distributed as the backend binary plus a frontend tarball referenced by releases/manifest.json. Nodes OTA-update via scripts/self-update.sh. Filebrowser and AIUI remain bundled inside the frontend tarball and deployed atomically, verified present in v1.7.43-alpha release artifact (189 AIUI files, filebrowser-client bundle). Archived under image-recipe/_archived/ (resurrectable if ISO distribution is reintroduced): - build-auto-installer-iso.sh - build-unbundled-iso.sh - test-iso-qemu.sh - scripts/convert-iso-to-disk.sh - BUILD-ISO-STATUS.md, ISO-BUILD-CHECKLIST.md - branding/isohdpfx.bin - .gitea/workflows/build-iso-dev.yml Updated release process docs to drop ISO references: - scripts/create-release.sh (next-steps text) - docs/BETA-RELEASE-CHECKLIST.md - docs/hotfix-process.md - README.md
This commit is contained in:
352
image-recipe/_archived/.gitea-workflows/build-iso-dev.yml
Normal file
352
image-recipe/_archived/.gitea-workflows/build-iso-dev.yml
Normal file
@@ -0,0 +1,352 @@
|
||||
name: Build Archipelago ISO (dev)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, dev-iso]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-iso:
|
||||
runs-on: iso-builder
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- name: Checkout
|
||||
run: |
|
||||
# Direct fetch + sync (actions/checkout token is broken on this Gitea)
|
||||
REPO_DIR="$HOME/archy"
|
||||
cd "$REPO_DIR" && git fetch origin main && git reset --hard origin/main
|
||||
echo "=== Source at commit: $(git log --oneline -1) ==="
|
||||
rsync -a --delete \
|
||||
--exclude '.git' --exclude 'node_modules' --exclude 'target' \
|
||||
--exclude 'image-recipe/build' --exclude 'image-recipe/results' \
|
||||
--exclude 'web/dist' \
|
||||
"$REPO_DIR/" "$GITHUB_WORKSPACE/"
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
echo "=== Workspace version: $(grep '^version' core/archipelago/Cargo.toml) ==="
|
||||
[ -f "scripts/first-boot-containers.sh" ] && echo " first-boot-containers.sh: PRESENT" || echo " first-boot-containers.sh: MISSING"
|
||||
grep -q 'network-alias' scripts/first-boot-containers.sh 2>/dev/null && echo " network-alias fix: PRESENT" || echo " network-alias fix: MISSING"
|
||||
|
||||
- name: Install ISO build dependencies
|
||||
run: |
|
||||
# Skip apt if packages already installed (persistent runner)
|
||||
if dpkg -s debootstrap squashfs-tools xorriso isolinux syslinux-common mtools \
|
||||
grub-efi-amd64-bin grub-pc-bin grub-common musl-tools >/dev/null 2>&1; then
|
||||
echo "ISO build deps already installed, skipping apt"
|
||||
else
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq \
|
||||
debootstrap squashfs-tools xorriso \
|
||||
isolinux syslinux-common mtools \
|
||||
grub-efi-amd64-bin grub-pc-bin grub-common \
|
||||
musl-tools
|
||||
fi
|
||||
# Ensure musl Rust target is available
|
||||
source $HOME/.cargo/env 2>/dev/null || true
|
||||
rustup target add x86_64-unknown-linux-musl 2>/dev/null || true
|
||||
|
||||
- name: Build backend (incremental, musl static)
|
||||
run: |
|
||||
source $HOME/.cargo/env 2>/dev/null || true
|
||||
# Build in persistent repo dir to reuse target/ cache
|
||||
cd "$HOME/archy"
|
||||
export GIT_HASH=$(git rev-parse --short HEAD)
|
||||
# Static musl build for portability — ensures binary runs regardless
|
||||
# of glibc version differences between build host and ISO rootfs.
|
||||
cargo build --release --target x86_64-unknown-linux-musl --manifest-path core/Cargo.toml
|
||||
# Copy binary to workspace for downstream steps
|
||||
mkdir -p "$GITHUB_WORKSPACE/core/target/release"
|
||||
cp core/target/x86_64-unknown-linux-musl/release/archipelago "$GITHUB_WORKSPACE/core/target/release/"
|
||||
|
||||
- name: Build frontend
|
||||
run: |
|
||||
source $HOME/.nvm/nvm.sh 2>/dev/null || true
|
||||
cd neode-ui && npm ci && npm run build
|
||||
|
||||
- name: Type check frontend
|
||||
run: |
|
||||
source $HOME/.nvm/nvm.sh 2>/dev/null || true
|
||||
cd neode-ui && npx vue-tsc -b --noEmit
|
||||
|
||||
- name: Run frontend tests
|
||||
run: |
|
||||
source $HOME/.nvm/nvm.sh 2>/dev/null || true
|
||||
cd neode-ui && npx vitest run
|
||||
|
||||
- name: Include AIUI if available
|
||||
run: |
|
||||
# AIUI (the Claude chat sidebar) lives outside the Vue build
|
||||
# and must be copied into the frontend dist BEFORE packaging,
|
||||
# otherwise OTA-tarball upgrades silently strip it from nodes
|
||||
# in the field. Try in order: cached on runner, then the
|
||||
# newest release tarball in this repo's releases/ dir as a
|
||||
# fallback so a freshly-provisioned runner still gets AIUI.
|
||||
AIUI_SRC=""
|
||||
if [ -f "/opt/archipelago/web-ui/aiui/index.html" ]; then
|
||||
AIUI_SRC="/opt/archipelago/web-ui/aiui"
|
||||
elif [ -f "$HOME/archy/web/dist/neode-ui/aiui/index.html" ]; then
|
||||
AIUI_SRC="$HOME/archy/web/dist/neode-ui/aiui"
|
||||
else
|
||||
LATEST_FRONTEND=$(ls -t releases/v*/archipelago-frontend-*.tar.gz 2>/dev/null | head -1)
|
||||
if [ -n "$LATEST_FRONTEND" ]; then
|
||||
echo "Extracting AIUI from $LATEST_FRONTEND (runner cache miss)"
|
||||
TMP=$(mktemp -d)
|
||||
tar xzf "$LATEST_FRONTEND" -C "$TMP" ./aiui 2>/dev/null || true
|
||||
if [ -f "$TMP/aiui/index.html" ]; then
|
||||
AIUI_SRC="$TMP/aiui"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ -n "$AIUI_SRC" ]; then
|
||||
mkdir -p web/dist/neode-ui/aiui
|
||||
cp -r "$AIUI_SRC/"* web/dist/neode-ui/aiui/
|
||||
echo "AIUI included from $AIUI_SRC ($(du -sh web/dist/neode-ui/aiui | cut -f1))"
|
||||
else
|
||||
echo "FAIL: AIUI not found anywhere (runner cache + release tarballs)"
|
||||
echo " checked: /opt/archipelago/web-ui/aiui"
|
||||
echo " \$HOME/archy/web/dist/neode-ui/aiui"
|
||||
echo " releases/v*/archipelago-frontend-*.tar.gz"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Configure root podman for insecure registry
|
||||
run: |
|
||||
sudo mkdir -p /etc/containers/registries.conf.d
|
||||
echo '[[registry]]
|
||||
location = "git.tx1138.com"
|
||||
insecure = true' | sudo tee /etc/containers/registries.conf.d/archipelago.conf
|
||||
|
||||
- name: Build unbundled ISO
|
||||
run: |
|
||||
cd image-recipe
|
||||
export ARCHIPELAGO_BIN="$(pwd)/../core/target/release/archipelago"
|
||||
if [ ! -x "$ARCHIPELAGO_BIN" ]; then
|
||||
echo "FAIL: backend binary missing or not executable at $ARCHIPELAGO_BIN"
|
||||
exit 1
|
||||
fi
|
||||
BIN_VERSION=$(strings "$ARCHIPELAGO_BIN" | grep -oE 'archipelago [0-9]+\.[0-9]+\.[0-9]+(-[a-z]+)?' | head -1 || true)
|
||||
EXPECTED=$(grep '^version' ../core/archipelago/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
echo "Binary: $ARCHIPELAGO_BIN ($(du -h "$ARCHIPELAGO_BIN" | cut -f1))"
|
||||
echo "Embedded version string: ${BIN_VERSION:-unknown}"
|
||||
echo "Expected version (Cargo.toml): $EXPECTED"
|
||||
sudo -E UNBUNDLED=1 DEV_SERVER=localhost BUILD_FROM_SOURCE=0 \
|
||||
ARCHIPELAGO_BIN="$ARCHIPELAGO_BIN" \
|
||||
./build-auto-installer-iso.sh
|
||||
|
||||
- name: Smoke test ISO
|
||||
run: |
|
||||
ISO=$(ls image-recipe/results/archipelago-installer-unbundled-*.iso 2>/dev/null | head -1)
|
||||
if [ -z "$ISO" ]; then
|
||||
echo "FAIL: No ISO produced"
|
||||
exit 1
|
||||
fi
|
||||
echo "ISO: $ISO ($(du -h "$ISO" | cut -f1))"
|
||||
|
||||
# Mount and verify structure
|
||||
MNT=$(mktemp -d)
|
||||
sudo mount -o loop,ro "$ISO" "$MNT"
|
||||
|
||||
FAIL=0
|
||||
for f in live/vmlinuz live/initrd.img live/filesystem.squashfs \
|
||||
isolinux/isolinux.bin isolinux/isolinux.cfg \
|
||||
boot/grub/grub.cfg EFI/BOOT/BOOTX64.EFI \
|
||||
archipelago/auto-install.sh archipelago/rootfs.tar; do
|
||||
if [ -e "$MNT/$f" ]; then
|
||||
echo " OK: $f ($(sudo du -h "$MNT/$f" 2>/dev/null | cut -f1))"
|
||||
else
|
||||
echo " MISSING: $f"
|
||||
FAIL=1
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify initrd has live-boot
|
||||
INITRD_DIR=$(mktemp -d)
|
||||
sudo unmkinitramfs "$MNT/live/initrd.img" "$INITRD_DIR" 2>/dev/null
|
||||
if [ -e "$INITRD_DIR/scripts/live" ] || [ -e "$INITRD_DIR/main/scripts/live" ]; then
|
||||
echo " OK: initrd has live-boot scripts"
|
||||
else
|
||||
echo " MISSING: live-boot scripts in initrd!"
|
||||
echo " initrd scripts/: $(ls "$INITRD_DIR/scripts/" 2>/dev/null || ls "$INITRD_DIR/main/scripts/" 2>/dev/null)"
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
# Check GRUB config has boot=live
|
||||
if grep -q "boot=live" "$MNT/boot/grub/grub.cfg"; then
|
||||
echo " OK: grub.cfg has boot=live"
|
||||
else
|
||||
echo " MISSING: boot=live in grub.cfg"
|
||||
FAIL=1
|
||||
fi
|
||||
|
||||
sudo umount "$MNT" 2>/dev/null
|
||||
rmdir "$MNT" 2>/dev/null
|
||||
sudo rm -r "$INITRD_DIR" 2>/dev/null
|
||||
|
||||
if [ "$FAIL" = "1" ]; then
|
||||
echo "SMOKE TEST FAILED"
|
||||
exit 1
|
||||
fi
|
||||
echo "SMOKE TEST PASSED"
|
||||
|
||||
- name: QEMU boot test
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
run: |
|
||||
ISO=$(ls image-recipe/results/archipelago-installer-unbundled-*.iso 2>/dev/null | head -1)
|
||||
if [ -n "$ISO" ] && command -v qemu-system-x86_64 >/dev/null 2>&1; then
|
||||
echo "Running headless QEMU boot test..."
|
||||
bash image-recipe/test-iso-qemu.sh "$ISO" 120
|
||||
else
|
||||
echo "Skipping QEMU test (no ISO or QEMU not available)"
|
||||
fi
|
||||
|
||||
- name: Copy to Builds
|
||||
run: |
|
||||
ISO=$(ls image-recipe/results/archipelago-installer-unbundled-*.iso 2>/dev/null | head -1)
|
||||
if [ -n "$ISO" ]; then
|
||||
DATE=$(date +%Y%m%d-%H%M)
|
||||
DEST="/var/lib/archipelago/filebrowser/Builds/archipelago-dev-unbundled-${DATE}.iso"
|
||||
sudo cp "$ISO" "$DEST"
|
||||
sudo chown 1000:1000 "$DEST"
|
||||
echo "ISO: archipelago-dev-unbundled-${DATE}.iso"
|
||||
echo "Size: $(du -h "$DEST" | cut -f1)"
|
||||
echo "SHA256: $(sha256sum "$DEST" | cut -d' ' -f1)"
|
||||
fi
|
||||
|
||||
- name: Publish release artifacts and manifest
|
||||
run: |
|
||||
VERSION=$(grep '^version' core/archipelago/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
RELEASE_DIR="/var/lib/archipelago/filebrowser/Builds/releases/v${VERSION}"
|
||||
sudo mkdir -p "$RELEASE_DIR"
|
||||
|
||||
# Copy backend binary
|
||||
BINARY="core/target/release/archipelago"
|
||||
if [ -f "$BINARY" ]; then
|
||||
sudo cp "$BINARY" "$RELEASE_DIR/archipelago"
|
||||
sudo chmod 755 "$RELEASE_DIR/archipelago"
|
||||
echo "Backend: $(du -h "$RELEASE_DIR/archipelago" | cut -f1)"
|
||||
fi
|
||||
|
||||
# Create frontend archive
|
||||
if [ -d "web/dist/neode-ui" ]; then
|
||||
FRONTEND_ARCHIVE="$RELEASE_DIR/archipelago-frontend-${VERSION}.tar.gz"
|
||||
sudo tar -czf "$FRONTEND_ARCHIVE" -C web/dist neode-ui
|
||||
echo "Frontend: $(du -h "$FRONTEND_ARCHIVE" | cut -f1)"
|
||||
fi
|
||||
|
||||
# Generate manifest with SHA256 hashes
|
||||
BACKEND_HASH=$(sha256sum "$RELEASE_DIR/archipelago" 2>/dev/null | awk '{print $1}')
|
||||
BACKEND_SIZE=$(stat -c%s "$RELEASE_DIR/archipelago" 2>/dev/null || echo 0)
|
||||
FRONTEND_NAME="archipelago-frontend-${VERSION}.tar.gz"
|
||||
FRONTEND_HASH=$(sha256sum "$RELEASE_DIR/$FRONTEND_NAME" 2>/dev/null | awk '{print $1}')
|
||||
FRONTEND_SIZE=$(stat -c%s "$RELEASE_DIR/$FRONTEND_NAME" 2>/dev/null || echo 0)
|
||||
|
||||
# Build download base URL (FileBrowser serves from /Builds/)
|
||||
HOST=$(hostname -I 2>/dev/null | awk '{print $1}')
|
||||
BASE_URL="http://${HOST:-192.168.1.228}:8083/Builds/releases/v${VERSION}"
|
||||
|
||||
# Generate manifest JSON
|
||||
python3 -c "
|
||||
import json
|
||||
manifest = {
|
||||
'version': '$VERSION',
|
||||
'release_date': '$DATE',
|
||||
'changelog': ['Update to version $VERSION'],
|
||||
'components': []
|
||||
}
|
||||
if '$BACKEND_HASH':
|
||||
manifest['components'].append({
|
||||
'name': 'archipelago',
|
||||
'current_version': '$VERSION',
|
||||
'new_version': '$VERSION',
|
||||
'download_url': '$BASE_URL/archipelago',
|
||||
'sha256': '$BACKEND_HASH',
|
||||
'size_bytes': int('$BACKEND_SIZE' or '0')
|
||||
})
|
||||
if '$FRONTEND_HASH':
|
||||
manifest['components'].append({
|
||||
'name': '$FRONTEND_NAME',
|
||||
'current_version': '$VERSION',
|
||||
'new_version': '$VERSION',
|
||||
'download_url': '$BASE_URL/$FRONTEND_NAME',
|
||||
'sha256': '$FRONTEND_HASH',
|
||||
'size_bytes': int('$FRONTEND_SIZE' or '0')
|
||||
})
|
||||
print(json.dumps(manifest, indent=2))
|
||||
" | sudo tee "$RELEASE_DIR/manifest.json" > /dev/null
|
||||
|
||||
# Also copy manifest to repo releases/ dir for git-based serving
|
||||
cp "$RELEASE_DIR/manifest.json" releases/manifest.json 2>/dev/null || true
|
||||
|
||||
sudo chown -R 1000:1000 "$RELEASE_DIR"
|
||||
echo ""
|
||||
echo "Release manifest:"
|
||||
cat "$RELEASE_DIR/manifest.json"
|
||||
echo ""
|
||||
echo "Artifacts published to: $RELEASE_DIR"
|
||||
|
||||
- name: Build report
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set +eo pipefail
|
||||
echo "══════════════════════════════════════════"
|
||||
echo "DEV ISO BUILD REPORT"
|
||||
echo "══════════════════════════════════════════"
|
||||
echo "Commit: $(git -C "$HOME/archy" rev-parse --short HEAD 2>/dev/null || echo 'unknown') ($(git -C "$HOME/archy" log -1 --format=%s 2>/dev/null || echo 'unknown'))"
|
||||
echo "Branch: ${GITHUB_REF_NAME:-dev-iso}"
|
||||
echo "Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "Runner: $(hostname)"
|
||||
echo ""
|
||||
echo "── Artifacts ──"
|
||||
ls -lh image-recipe/results/*.iso 2>/dev/null || echo " No ISO produced"
|
||||
ls -lh /var/lib/archipelago/filebrowser/Builds/archipelago-dev-*.iso 2>/dev/null | tail -3
|
||||
echo ""
|
||||
echo "── Rootfs contents check ──"
|
||||
ROOTFS=$(ls image-recipe/build/auto-installer/archipelago-rootfs.tar 2>/dev/null) || true
|
||||
if [ -n "$ROOTFS" ]; then
|
||||
echo " rootfs.tar: $(sudo du -h "$ROOTFS" 2>/dev/null | cut -f1 || echo 'unknown')"
|
||||
# List key paths once (podman export omits ./ prefix, so match without it)
|
||||
ROOTFS_LIST=$(sudo tar tf "$ROOTFS" 2>/dev/null | grep -E '(etc/nginx/sites-available/archipelago|etc/archipelago/ssl/archipelago.crt|usr/local/bin/archipelago-kiosk-launcher|usr/local/bin/archipelago|opt/archipelago/web-ui/index.html)' || true)
|
||||
for item in \
|
||||
"nginx config:etc/nginx/sites-available/archipelago" \
|
||||
"SSL cert:etc/archipelago/ssl/archipelago.crt" \
|
||||
"kiosk launcher:usr/local/bin/archipelago-kiosk-launcher" \
|
||||
"backend binary:usr/local/bin/archipelago" \
|
||||
"web-ui index:opt/archipelago/web-ui/index.html"; do
|
||||
label="${item%%:*}"; path="${item#*:}"
|
||||
echo "$ROOTFS_LIST" | grep -q "$path" && echo " $label: PRESENT" || echo " $label: MISSING"
|
||||
done
|
||||
else
|
||||
echo " rootfs.tar not found in workspace"
|
||||
fi
|
||||
echo ""
|
||||
echo "── ISO contents check ──"
|
||||
ISO=$(ls image-recipe/results/archipelago-installer-unbundled-*.iso 2>/dev/null | head -1) || true
|
||||
if [ -n "$ISO" ]; then
|
||||
echo " ISO size: $(sudo du -h "$ISO" 2>/dev/null | cut -f1 || echo 'unknown')"
|
||||
ISO_MOUNT=$(mktemp -d)
|
||||
if sudo mount -o loop,ro "$ISO" "$ISO_MOUNT" 2>/dev/null; then
|
||||
echo " auto-install.sh: $([ -f "$ISO_MOUNT/archipelago/auto-install.sh" ] && echo 'PRESENT' || echo 'MISSING')"
|
||||
echo " rootfs.tar: $([ -f "$ISO_MOUNT/archipelago/rootfs.tar" ] && echo "PRESENT ($(sudo du -h "$ISO_MOUNT/archipelago/rootfs.tar" 2>/dev/null | cut -f1))" || echo 'MISSING')"
|
||||
echo " backend bin: $([ -f "$ISO_MOUNT/archipelago/bin/archipelago" ] && echo "PRESENT ($(sudo du -h "$ISO_MOUNT/archipelago/bin/archipelago" 2>/dev/null | cut -f1))" || echo 'MISSING')"
|
||||
echo " frontend: $([ -f "$ISO_MOUNT/archipelago/web-ui/index.html" ] && echo 'PRESENT' || echo 'MISSING')"
|
||||
echo " vmlinuz: $([ -f "$ISO_MOUNT/live/vmlinuz" ] && echo 'PRESENT' || echo 'MISSING')"
|
||||
echo " initrd: $([ -f "$ISO_MOUNT/live/initrd.img" ] && echo 'PRESENT' || echo 'MISSING')"
|
||||
echo " squashfs: $([ -f "$ISO_MOUNT/live/filesystem.squashfs" ] && echo "PRESENT ($(sudo du -h "$ISO_MOUNT/live/filesystem.squashfs" 2>/dev/null | cut -f1))" || echo 'MISSING')"
|
||||
echo " grub theme: $([ -d "$ISO_MOUNT/boot/grub/themes/archipelago" ] && echo 'PRESENT' || echo 'MISSING')"
|
||||
sudo umount "$ISO_MOUNT" 2>/dev/null || true
|
||||
else
|
||||
echo " Could not mount ISO for inspection"
|
||||
fi
|
||||
rmdir "$ISO_MOUNT" 2>/dev/null || true
|
||||
fi
|
||||
echo "══════════════════════════════════════════"
|
||||
|
||||
- name: Fix workspace permissions
|
||||
if: always()
|
||||
run: |
|
||||
sudo chown -R $(id -u):$(id -g) . 2>/dev/null || true
|
||||
sudo chmod -R u+rwX . 2>/dev/null || true
|
||||
sudo chown -R $(id -u):$(id -g) "$HOME/.cache/act" 2>/dev/null || true
|
||||
sudo chmod -R u+rwX "$HOME/.cache/act" 2>/dev/null || true
|
||||
86
image-recipe/_archived/BUILD-ISO-STATUS.md
Normal file
86
image-recipe/_archived/BUILD-ISO-STATUS.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Archipelago ISO Build - Quick Guide
|
||||
|
||||
## TL;DR - Build ISO with Live Server State
|
||||
|
||||
```bash
|
||||
cd ~/archy/image-recipe
|
||||
sudo bash build-auto-installer-iso.sh
|
||||
```
|
||||
|
||||
The script will automatically:
|
||||
1. Try to capture backend from `/usr/local/bin/archipelago`
|
||||
2. Try to capture frontend from `/opt/archipelago/web-ui`
|
||||
3. Fall back to building from source if capture fails
|
||||
|
||||
## Build Modes
|
||||
|
||||
### Default: Capture from Dev Server (RECOMMENDED)
|
||||
```bash
|
||||
# From your Mac (captures from remote dev server):
|
||||
cd image-recipe
|
||||
DEV_SERVER=archipelago@192.168.1.228 sudo bash build-auto-installer-iso.sh
|
||||
|
||||
# From the dev server itself:
|
||||
cd ~/archy/image-recipe
|
||||
sudo bash build-auto-installer-iso.sh
|
||||
```
|
||||
|
||||
### Alternative: Build from Source
|
||||
```bash
|
||||
BUILD_FROM_SOURCE=1 sudo bash build-auto-installer-iso.sh
|
||||
```
|
||||
|
||||
## Known Issues & Workarounds
|
||||
|
||||
### Issue: Can't capture from localhost via SCP
|
||||
|
||||
**Problem**: When running on the server itself, `scp localhost:/path` doesn't work.
|
||||
|
||||
**Workaround**: Use direct file copy instead:
|
||||
```bash
|
||||
# Instead of building on the server, build from your Mac:
|
||||
cd ~/Projects/archy/image-recipe
|
||||
DEV_SERVER=archipelago@192.168.1.228 sudo bash build-auto-installer-iso.sh
|
||||
```
|
||||
|
||||
### Issue: Podman registry not configured
|
||||
|
||||
**Problem**: Podman can't pull images because `/etc/containers/registries.conf` has no unqualified-search registries.
|
||||
|
||||
**Fix**:
|
||||
```bash
|
||||
ssh archipelago@192.168.1.228
|
||||
sudo tee -a /etc/containers/registries.conf <<EOF
|
||||
[registries.search]
|
||||
registries = ['docker.io']
|
||||
EOF
|
||||
```
|
||||
|
||||
## Flash ISO to USB
|
||||
|
||||
```bash
|
||||
cd ~/Projects/archy/image-recipe
|
||||
./write-usb-dd.sh /dev/diskX
|
||||
```
|
||||
|
||||
## What Gets Captured
|
||||
|
||||
From your dev server (192.168.1.228):
|
||||
- ✅ Backend binary: `/usr/local/bin/archipelago` (6.2M)
|
||||
- ✅ Frontend: `/opt/archipelago/web-ui` (~64M)
|
||||
- ✅ Nginx config: `/etc/nginx/sites-available/default`
|
||||
- ✅ Systemd service: `/etc/systemd/system/archipelago.service`
|
||||
- ✅ App manifests: `~/archy/apps/`
|
||||
|
||||
## Current Status
|
||||
|
||||
**Latest Working ISO**: `archipelago-debian-13-x86_64.iso` (469M, built 18:28)
|
||||
- This ISO was built earlier today
|
||||
- Contains the auto-installer
|
||||
- **Should be tested** - might already have your live server state
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Flash the existing ISO** and test it on the Dell OptiPlex
|
||||
2. **Fix the build script** to properly capture from localhost (use `cp` instead of `scp`)
|
||||
3. **Configure Podman registries** on dev server for fallback source builds
|
||||
155
image-recipe/_archived/ISO-BUILD-CHECKLIST.md
Normal file
155
image-recipe/_archived/ISO-BUILD-CHECKLIST.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# ISO Build Checklist
|
||||
|
||||
This checklist ensures that all changes from the live development server are properly integrated into the ISO build.
|
||||
|
||||
## Pre-Build Steps
|
||||
|
||||
### 1. Sync System Configurations from Live Server
|
||||
|
||||
```bash
|
||||
cd image-recipe
|
||||
./sync-from-live.sh
|
||||
```
|
||||
|
||||
This captures:
|
||||
- [ ] Systemd service configuration (`archipelago.service`) - **User=root** required for Podman
|
||||
- [ ] Nginx configuration (`nginx-archipelago.conf`) - includes app proxies (Nextcloud, Vaultwarden, Immich, Penpot)
|
||||
- [ ] Logrotate configuration (if exists)
|
||||
- [ ] Any custom scripts in `/opt/archipelago/scripts/`
|
||||
|
||||
**Critical**: `build-auto-installer-iso.sh` uses `configs/` for nginx and archipelago.service. Ensure these are synced before building.
|
||||
|
||||
### 2. Verify Code Changes
|
||||
|
||||
Ensure all code changes are committed:
|
||||
- [ ] Backend changes in `core/`
|
||||
- [ ] Frontend changes in `neode-ui/`
|
||||
- [ ] Script changes in `scripts/`
|
||||
|
||||
### 3. Build Components
|
||||
|
||||
```bash
|
||||
cd image-recipe
|
||||
|
||||
# Build backend
|
||||
./scripts/build-backend.sh
|
||||
|
||||
# Build frontend
|
||||
./scripts/build-frontend.sh
|
||||
```
|
||||
|
||||
Verify builds:
|
||||
- [ ] Backend binary exists: `build/backend/archipelago`
|
||||
- [ ] Frontend files exist: `build/frontend/index.html`
|
||||
|
||||
## Integration Check
|
||||
|
||||
### 4. Update Build Scripts
|
||||
|
||||
Review and update if needed:
|
||||
- [ ] `integrate-archipelago.sh` - Includes all config files
|
||||
- [ ] `build-debian-iso.sh` - Installs to correct paths
|
||||
|
||||
### 5. Critical Configuration Values
|
||||
|
||||
Verify in `configs/archipelago.service`:
|
||||
- [ ] `User=root` (required for Podman root context)
|
||||
- [ ] `Environment="ARCHIPELAGO_DEV_MODE=true"` (enables container detection)
|
||||
- [ ] `Environment="ARCHIPELAGO_BIND=127.0.0.1:5678"`
|
||||
|
||||
Verify in `configs/nginx-archipelago.conf`:
|
||||
- [ ] Root path: `/opt/archipelago/web-ui`
|
||||
- [ ] RPC proxy: `/rpc/` → `http://127.0.0.1:5678`
|
||||
- [ ] WebSocket proxy: `/ws` → `http://127.0.0.1:5678`
|
||||
|
||||
## Build Process
|
||||
|
||||
### 6. Build the ISO
|
||||
|
||||
```bash
|
||||
./build-debian-iso.sh
|
||||
```
|
||||
|
||||
Expected output:
|
||||
- [ ] ISO created in `results/` directory
|
||||
- [ ] No build errors
|
||||
- [ ] File size reasonable (~500MB - 2GB)
|
||||
|
||||
### 7. Test in QEMU
|
||||
|
||||
```bash
|
||||
./test-iso-qemu.sh
|
||||
```
|
||||
|
||||
Test checklist:
|
||||
- [ ] ISO boots successfully
|
||||
- [ ] Backend service starts: `systemctl status archipelago`
|
||||
- [ ] Nginx serves frontend
|
||||
- [ ] Can access UI at `http://localhost:8080` (or mapped port)
|
||||
- [ ] Container detection works: Check logs for "Detected container"
|
||||
|
||||
## App Stack Hardening (Immich, Penpot, etc.)
|
||||
|
||||
The first-boot script and deploy script ensure:
|
||||
- [ ] **Immich**: Old single-container `immich` (wrong port) is removed before creating `immich_server` stack
|
||||
- [ ] **First-boot**: Waits for postgres (pg_isready) before starting Immich server
|
||||
- [ ] **Backend**: `package.install` for Immich removes old container before creating stack
|
||||
- [ ] **Deploy**: Ensures Immich stack on every deploy, cleans up conflicts
|
||||
|
||||
## Post-Build
|
||||
|
||||
### 8. Write to USB (Optional)
|
||||
|
||||
```bash
|
||||
./write-usb-dd.sh /dev/diskN
|
||||
```
|
||||
|
||||
Or use Balena Etcher to flash the ISO.
|
||||
|
||||
### 9. Test on Real Hardware
|
||||
|
||||
- [ ] Boot from USB
|
||||
- [ ] Network configuration works
|
||||
- [ ] All services start automatically
|
||||
- [ ] Can access web UI
|
||||
- [ ] Containers are detected and managed
|
||||
|
||||
## Deployment Paths Reference
|
||||
|
||||
The ISO build must install to these paths:
|
||||
|
||||
| Component | Path | Source |
|
||||
|-----------|------|--------|
|
||||
| Backend binary | `/usr/local/bin/archipelago` | `build/backend/archipelago` |
|
||||
| Frontend files | `/opt/archipelago/web-ui/` | `build/frontend/*` |
|
||||
| Systemd service | `/etc/systemd/system/archipelago.service` | `configs/archipelago.service` |
|
||||
| Nginx config | `/etc/nginx/sites-available/archipelago` | `configs/nginx-archipelago.conf` |
|
||||
| Nginx symlink | `/etc/nginx/sites-enabled/archipelago` | Link to sites-available |
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Backend Not Detecting Containers
|
||||
- Verify service runs as `root` user
|
||||
- Check Podman context: `sudo podman ps` should show containers
|
||||
- Enable dev mode: `ARCHIPELAGO_DEV_MODE=true`
|
||||
|
||||
### UI Not Loading
|
||||
- Check nginx configuration paths
|
||||
- Verify frontend files deployed to `/opt/archipelago/web-ui/`
|
||||
- Check nginx error logs: `/var/log/nginx/error.log`
|
||||
|
||||
### Service Fails to Start
|
||||
- Check binary permissions: Should be executable
|
||||
- Check systemd logs: `journalctl -u archipelago`
|
||||
- Test binary manually: `sudo /usr/local/bin/archipelago`
|
||||
|
||||
## Version Tracking
|
||||
|
||||
When building a new ISO, document:
|
||||
- Date: _______________
|
||||
- Git commit: _______________
|
||||
- Backend version: _______________
|
||||
- Frontend version: _______________
|
||||
- ISO filename: _______________
|
||||
- Tested on hardware: _______________
|
||||
- Issues found: _______________
|
||||
37
image-recipe/_archived/README.md
Normal file
37
image-recipe/_archived/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Archived ISO build recipes
|
||||
|
||||
These scripts built the Archipelago auto-installer ISO (bundled and
|
||||
unbundled variants). As of v1.7.43-alpha, ISOs are no longer part of the
|
||||
release deliverable. Releases ship as tarballs consumed by
|
||||
`scripts/self-update.sh` on existing nodes.
|
||||
|
||||
Archived here rather than deleted so they can be resurrected if ISO
|
||||
distribution is reintroduced.
|
||||
|
||||
## Contents
|
||||
|
||||
- `build-auto-installer-iso.sh` — orchestrator, bundles container images into squashfs
|
||||
- `build-unbundled-iso.sh` — thin wrapper that sets BUNDLE_IMAGES=0 and delegates
|
||||
- `test-iso-qemu.sh` — smoke-tests a built ISO under QEMU
|
||||
- `scripts/convert-iso-to-disk.sh` — converts an ISO to a raw disk image
|
||||
- `BUILD-ISO-STATUS.md`, `ISO-BUILD-CHECKLIST.md` — contributor guides
|
||||
- `branding/isohdpfx.bin` — isolinux MBR hybrid image
|
||||
- `.gitea-workflows/build-iso-dev.yml` — CI workflow that ran the build+smoke-test
|
||||
|
||||
## To resurrect
|
||||
|
||||
1. `git mv image-recipe/_archived/* image-recipe/` (adjust paths back)
|
||||
2. Restore `.gitea/workflows/build-iso-dev.yml`
|
||||
3. Re-add release-process references (see `scripts/create-release.sh`,
|
||||
`docs/BETA-RELEASE-CHECKLIST.md`, `docs/hotfix-process.md`, `README.md`).
|
||||
|
||||
## Why archived
|
||||
|
||||
The release flow is simpler and faster as tarball-only:
|
||||
- `releases/vX.Y.Z-alpha/archipelago` (backend binary)
|
||||
- `releases/vX.Y.Z-alpha/archipelago-frontend-X.Y.Z-alpha.tar.gz` (frontend + AIUI + filebrowser UI assets)
|
||||
- `releases/manifest.json` (pointers + changelog)
|
||||
|
||||
Nodes pull these via `scripts/self-update.sh` from either Gitea mirror.
|
||||
Filebrowser and AIUI remain bundled inside the frontend tarball and deployed
|
||||
atomically by `self-update.sh`.
|
||||
BIN
image-recipe/_archived/branding/isohdpfx.bin
Normal file
BIN
image-recipe/_archived/branding/isohdpfx.bin
Normal file
Binary file not shown.
3524
image-recipe/_archived/build-auto-installer-iso.sh
Executable file
3524
image-recipe/_archived/build-auto-installer-iso.sh
Executable file
File diff suppressed because it is too large
Load Diff
33
image-recipe/_archived/build-unbundled-iso.sh
Executable file
33
image-recipe/_archived/build-unbundled-iso.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Build Archipelago UNBUNDLED Auto-Installer ISO
|
||||
#
|
||||
# Same as build-auto-installer-iso.sh but WITHOUT pre-bundled container images.
|
||||
# Users install all apps on-demand from the Marketplace (requires internet).
|
||||
#
|
||||
# Benefits:
|
||||
# - Much smaller ISO (~1-2GB vs ~8-10GB)
|
||||
# - Faster build (no image pulling/saving)
|
||||
# - Faster install (no image copying/loading)
|
||||
#
|
||||
# Trade-offs:
|
||||
# - Internet required after first boot to install apps
|
||||
# - No apps pre-loaded — everything comes from Marketplace
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./build-unbundled-iso.sh
|
||||
# DEV_SERVER=archipelago@192.168.1.228 sudo ./build-unbundled-iso.sh
|
||||
# BUILD_FROM_SOURCE=1 sudo ./build-unbundled-iso.sh
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
DEV_SERVER="${DEV_SERVER:-archipelago@192.168.1.228}"
|
||||
BUILD_FROM_SOURCE="${BUILD_FROM_SOURCE:-0}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# Delegate to the main build script with UNBUNDLED mode
|
||||
export UNBUNDLED=1
|
||||
exec "$SCRIPT_DIR/build-auto-installer-iso.sh" "$@"
|
||||
65
image-recipe/_archived/scripts/convert-iso-to-disk.sh
Executable file
65
image-recipe/_archived/scripts/convert-iso-to-disk.sh
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
# Convert ISO image to bootable disk image
|
||||
# Creates a raw disk image that can be flashed directly
|
||||
|
||||
set -e
|
||||
|
||||
OUTPUT_DIR="${1:-../results}"
|
||||
ARCHIPELAGO_VERSION="${ARCHIPELAGO_VERSION:-0.1.0}"
|
||||
ARCH="${ARCH:-x86_64}"
|
||||
|
||||
echo "💾 Converting ISO to disk image..."
|
||||
|
||||
# Find ISO file
|
||||
ISO_FILE=$(ls "$OUTPUT_DIR"/*.iso 2>/dev/null | head -1)
|
||||
if [ -z "$ISO_FILE" ]; then
|
||||
echo "❌ No ISO file found in $OUTPUT_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " Source ISO: $ISO_FILE"
|
||||
|
||||
# Create disk image (4GB minimum)
|
||||
DISK_SIZE=4096 # 4GB in MB
|
||||
DISK_IMG="$OUTPUT_DIR/archipelago-${ARCHIPELAGO_VERSION}-${ARCH}.img"
|
||||
|
||||
echo " Creating disk image: $DISK_IMG"
|
||||
|
||||
# Check if we have required tools
|
||||
if ! command -v dd >/dev/null 2>&1; then
|
||||
echo "❌ dd not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create empty disk image
|
||||
dd if=/dev/zero of="$DISK_IMG" bs=1M count=$DISK_SIZE 2>/dev/null || {
|
||||
echo "❌ Failed to create disk image"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Note: Full disk image creation with partitions requires:
|
||||
# - parted or fdisk
|
||||
# - mkfs.vfat, mkfs.ext4
|
||||
# - losetup (Linux only)
|
||||
# - grub-install
|
||||
|
||||
# For now, we'll create a simple approach:
|
||||
# The ISO can be used directly, or users can use tools like:
|
||||
# - balenaEtcher (macOS/Linux GUI)
|
||||
# - Rufus (Windows)
|
||||
# - dd (command line)
|
||||
|
||||
echo "⚠️ Full disk image conversion requires additional tools"
|
||||
echo " For now, use the ISO file directly with:"
|
||||
echo " - balenaEtcher (recommended)"
|
||||
echo " - dd command (see docs)"
|
||||
echo ""
|
||||
echo " ISO file: $ISO_FILE"
|
||||
echo " Size: $(du -h "$ISO_FILE" | cut -f1)"
|
||||
|
||||
# Clean up empty image file
|
||||
rm -f "$DISK_IMG"
|
||||
|
||||
echo ""
|
||||
echo "💡 Tip: Use the ISO file with a USB flashing tool"
|
||||
echo " The ISO is bootable and can be flashed directly"
|
||||
157
image-recipe/_archived/test-iso-qemu.sh
Executable file
157
image-recipe/_archived/test-iso-qemu.sh
Executable file
@@ -0,0 +1,157 @@
|
||||
#!/bin/bash
|
||||
# Test Archipelago ISO in QEMU
|
||||
#
|
||||
# Usage:
|
||||
# ./test-iso-qemu.sh [path-to-iso] [--bios] [--nographic]
|
||||
#
|
||||
# Options:
|
||||
# --bios Force legacy BIOS mode (default: UEFI)
|
||||
# --nographic No GUI window, serial console only (great for logging)
|
||||
#
|
||||
# Serial log is always written to /tmp/archipelago-qemu-serial.log
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SERIAL_LOG="/tmp/archipelago-qemu-serial.log"
|
||||
FORCE_BIOS=false
|
||||
NOGRAPHIC=false
|
||||
TIMEOUT=0
|
||||
ISO=""
|
||||
|
||||
# Simple arg parsing. First non-flag positional is the ISO path. A bare
|
||||
# numeric (e.g. `120`) is taken as a boot-test timeout in seconds so CI
|
||||
# can call `test-iso-qemu.sh <iso> 120` without hanging the job. The
|
||||
# pre-fix version used `case *) ISO=...`, which silently overwrote ISO
|
||||
# with the timeout value and sent QEMU looking for a file literally
|
||||
# named "120".
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--bios) FORCE_BIOS=true ;;
|
||||
--nographic) NOGRAPHIC=true ;;
|
||||
--timeout=*) TIMEOUT="${arg#--timeout=}" ;;
|
||||
[0-9]*) TIMEOUT="$arg" ;;
|
||||
*) ISO="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# A positive TIMEOUT implies headless (no DISPLAY in CI anyway) and keeps
|
||||
# the entire script wrapped in `timeout` to guarantee the job returns.
|
||||
if [ "$TIMEOUT" -gt 0 ] 2>/dev/null; then
|
||||
NOGRAPHIC=true
|
||||
fi
|
||||
|
||||
# Auto-detect ISO
|
||||
if [ -z "$ISO" ]; then
|
||||
ISO=$(ls -t "$SCRIPT_DIR"/results/archipelago-installer-unbundled-*.iso 2>/dev/null | head -1)
|
||||
fi
|
||||
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
||||
ISO=$(ls -t "$SCRIPT_DIR"/results/archipelago-*.iso 2>/dev/null | head -1)
|
||||
fi
|
||||
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
||||
echo "ISO not found."
|
||||
echo ""
|
||||
echo "Usage: $0 [path-to-iso] [--bios] [--nographic]"
|
||||
echo ""
|
||||
echo "Or place an ISO in: $SCRIPT_DIR/results/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Testing Archipelago ISO in QEMU"
|
||||
echo " ISO: $ISO"
|
||||
echo " Size: $(du -h "$ISO" | cut -f1)"
|
||||
echo " RAM: 4GB"
|
||||
echo " CPU: 2 cores"
|
||||
echo " Serial: $SERIAL_LOG"
|
||||
echo ""
|
||||
|
||||
# Create test disk if it doesn't exist
|
||||
DISK="/tmp/archipelago-test-disk.qcow2"
|
||||
if [ ! -f "$DISK" ]; then
|
||||
echo "Creating 20GB test disk..."
|
||||
qemu-img create -f qcow2 "$DISK" 20G
|
||||
fi
|
||||
|
||||
# Common QEMU args
|
||||
QEMU_ARGS=(
|
||||
-m 4G
|
||||
-smp 2
|
||||
-boot d
|
||||
-cdrom "$ISO"
|
||||
-drive if=virtio,format=qcow2,file="$DISK"
|
||||
-net nic,model=virtio -net user,hostfwd=tcp::2222-:22,hostfwd=tcp::8100-:80
|
||||
-serial file:"$SERIAL_LOG"
|
||||
)
|
||||
|
||||
# Display mode
|
||||
if [ "$NOGRAPHIC" = true ]; then
|
||||
QEMU_ARGS+=(-nographic -append "console=ttyS0")
|
||||
else
|
||||
QEMU_ARGS+=(-vga virtio -display default)
|
||||
fi
|
||||
|
||||
echo "Starting VM..."
|
||||
echo "(Serial console logging to $SERIAL_LOG)"
|
||||
echo "(Press Ctrl+Alt+G to release mouse, Ctrl+C to stop VM)"
|
||||
echo ""
|
||||
|
||||
# Detect UEFI firmware
|
||||
OVMF=""
|
||||
if [ "$FORCE_BIOS" = false ]; then
|
||||
if [ -f "/opt/homebrew/share/qemu/edk2-x86_64-code.fd" ]; then
|
||||
OVMF="/opt/homebrew/share/qemu/edk2-x86_64-code.fd"
|
||||
elif [ -f "/usr/share/OVMF/OVMF_CODE.fd" ]; then
|
||||
OVMF="/usr/share/OVMF/OVMF_CODE.fd"
|
||||
fi
|
||||
fi
|
||||
|
||||
run_qemu() {
|
||||
if [ -n "$OVMF" ]; then
|
||||
echo " Boot: UEFI ($OVMF)"
|
||||
qemu-system-x86_64 \
|
||||
-machine q35 \
|
||||
-drive if=pflash,format=raw,readonly=on,file="$OVMF" \
|
||||
"${QEMU_ARGS[@]}"
|
||||
else
|
||||
echo " Boot: Legacy BIOS"
|
||||
qemu-system-x86_64 \
|
||||
-machine pc \
|
||||
"${QEMU_ARGS[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Wrap the QEMU invocation in `timeout` when a CI caller passed one so
|
||||
# the script always returns instead of hanging on a VM that never exits
|
||||
# its boot loop. Exit 124 from coreutils' timeout is treated as "VM
|
||||
# reached the timeout", which for a CI boot test is success as long as
|
||||
# the serial log shows a kernel reaching userspace — we inspect that
|
||||
# after the QEMU process ends.
|
||||
if [ "$TIMEOUT" -gt 0 ] 2>/dev/null; then
|
||||
timeout --foreground --preserve-status "${TIMEOUT}s" bash -c "$(declare -f run_qemu); run_qemu"
|
||||
rc=$?
|
||||
if [ $rc -eq 124 ] || [ $rc -eq 137 ]; then
|
||||
echo "(QEMU terminated after ${TIMEOUT}s boot-test window)"
|
||||
rc=0
|
||||
fi
|
||||
else
|
||||
run_qemu
|
||||
rc=$?
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "VM stopped. Serial log: $SERIAL_LOG"
|
||||
echo "Last 20 lines:"
|
||||
tail -20 "$SERIAL_LOG" 2>/dev/null
|
||||
|
||||
# Boot-sanity check: the CI wrapper wants a non-zero exit only when the
|
||||
# kernel never reached userspace. Look for a well-known marker emitted
|
||||
# by live-boot/systemd early in the sequence. If the marker never
|
||||
# appeared, surface the real failure; otherwise treat "timeout reached
|
||||
# with a live kernel" as a pass.
|
||||
if [ "$TIMEOUT" -gt 0 ] 2>/dev/null && [ -f "$SERIAL_LOG" ]; then
|
||||
if grep -qE "Welcome to Debian|Reached target|systemd\[1\]:" "$SERIAL_LOG"; then
|
||||
echo " Boot sanity: OK (systemd reached in serial log)"
|
||||
exit 0
|
||||
fi
|
||||
echo " Boot sanity: FAIL — no systemd markers in serial log within ${TIMEOUT}s"
|
||||
exit 1
|
||||
fi
|
||||
exit "${rc:-0}"
|
||||
Reference in New Issue
Block a user