fix: xorriso append_partition for real USB boot + grub-mkstandalone
Root cause of USB boot failure: our xorriso used -e boot/grub/efi.img to embed the EFI image inside the ISO. This works for CD-ROM and QEMU but NOT for USB on real UEFI hardware. Fix: use the Will Haley / Debian live-build approach: - -append_partition 2 (GPT type EFI) appends efi.img AFTER ISO data - -e --interval:appended_partition_2:all:: references the appended partition - --mbr-force-bootable forces MBR active flag - grub-mkstandalone with embedded bootstrap config (searches for grub.cfg) - grub.cfg placed in both /boot/grub/ AND /EFI/BOOT/ on ISO - grub.cfg uses search --label ARCHIPELAGO to find the ISO root This is the exact approach used by StartOS, TAILS, and every production custom Debian live ISO that boots from USB. Also: iso-debug, iso-branding skills + reference docs, dev-start.sh option 0 for branding dev, improved dev-branding.sh and test-iso-qemu.sh. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b49f850c85
commit
6cfb8082c5
@ -1,87 +1,121 @@
|
||||
---
|
||||
name: build-iso
|
||||
description: Build a new Archipelago auto-installer ISO image (bundled or unbundled)
|
||||
disable-model-invocation: true
|
||||
allowed-tools: Bash, Read
|
||||
description: Build Archipelago auto-installer ISOs. Custom debootstrap base (no Debian Live dependency), live-boot for squashfs root, hybrid BIOS+UEFI boot, Archipelago branding. Use when user says "build ISO", "build image", "create installer", or needs to work on the ISO build pipeline.
|
||||
allowed-tools: Bash, Read, Edit, Write, Grep, Glob, Agent
|
||||
---
|
||||
|
||||
Build a new Archipelago auto-installer ISO.
|
||||
# Build Archipelago ISO
|
||||
|
||||
## Pre-build checklist
|
||||
## Architecture (dev-iso branch)
|
||||
|
||||
1. Latest code deployed to server (`/deploy` first)
|
||||
2. System configs synced (`/sync-configs` first)
|
||||
3. Everything tested and working on live server
|
||||
4. Sync build scripts to server before building:
|
||||
```bash
|
||||
rsync -avz -e "ssh -i ~/.ssh/archipelago-deploy" \
|
||||
/Users/dorian/Projects/archy/image-recipe/build-auto-installer-iso.sh \
|
||||
/Users/dorian/Projects/archy/image-recipe/build-unbundled-iso.sh \
|
||||
archipelago@192.168.1.228:~/archy/image-recipe/
|
||||
```
|
||||
Custom debootstrap-based installer. NO Debian Live ISO download.
|
||||
|
||||
## Build variants
|
||||
| Component | Source | Size |
|
||||
|-----------|--------|------|
|
||||
| Installer squashfs | debootstrap --variant=minbase + live-boot | ~180MB |
|
||||
| Target rootfs | Docker build (Debian bookworm, full stack) | ~1.5GB compressed |
|
||||
| Kernel + initramfs | From debootstrap, with live-boot hooks | ~50MB |
|
||||
| GRUB + ISOLINUX | Built from packages during Step 2 | ~1MB |
|
||||
| **Total ISO** | **Unbundled** | **~2.2GB** |
|
||||
|
||||
### Unbundled ISO (recommended for distribution — ~3GB)
|
||||
No pre-bundled container images. Apps install on-demand from Marketplace (requires internet).
|
||||
## Build Pipeline (6 Steps)
|
||||
|
||||
**Step 1** (lines ~200-430): Build target rootfs via Docker
|
||||
- Debian bookworm + all runtime packages (podman, nginx, tor, chromium, etc.)
|
||||
- `--no-install-recommends` for size reduction
|
||||
- Strips docs/man/locales
|
||||
- Output: `archipelago-rootfs.tar` (~1.5GB)
|
||||
|
||||
**Step 2** (lines ~430-710): Build installer environment via debootstrap
|
||||
- `debootstrap --variant=minbase` inside a container
|
||||
- Installs live-boot via chroot (NOT --include — minbase can't resolve it)
|
||||
- Custom initramfs with live-boot hooks
|
||||
- Builds GRUB EFI image with grub-mkimage
|
||||
- Creates ISOLINUX files, EFI boot image
|
||||
- Installs GRUB theme + background
|
||||
- Output: vmlinuz, initrd.img, filesystem.squashfs, BOOTX64.EFI, efi.img, isolinux.bin
|
||||
|
||||
**Step 3** (lines ~710-850): Add Archipelago components
|
||||
- Backend binary, web UI, rootfs.tar, scripts, Plymouth theme
|
||||
|
||||
**Step 3b** (lines ~850-1230): Bundle container images (skipped if UNBUNDLED=1)
|
||||
|
||||
**Step 4** (lines ~1230-2380): Generate auto-install.sh
|
||||
- Embedded installer script (~1100 lines)
|
||||
- Disk detection, partitioning, LUKS encryption, GRUB install
|
||||
- Installs GRUB + Plymouth theme on target
|
||||
|
||||
**Step 5** (lines ~2380-2460): Configure boot loaders
|
||||
- Write GRUB config (boot=live components)
|
||||
- Write ISOLINUX config
|
||||
- Both reference kernel at /live/vmlinuz
|
||||
|
||||
**Step 6** (lines ~2460-2540): Create final ISO
|
||||
- xorriso with hybrid BIOS+UEFI boot
|
||||
- Uses proven MBR from `branding/isohdpfx.bin`
|
||||
- `-partition_offset 16` for UEFI compatibility
|
||||
|
||||
## CI Workflow
|
||||
|
||||
**Branch**: `dev-iso` → `.gitea/workflows/build-iso-dev.yml`
|
||||
**Branch**: `main` → `.gitea/workflows/build-iso.yml`
|
||||
|
||||
Dev CI includes a smoke test step that verifies:
|
||||
- All critical files present in ISO
|
||||
- Initrd contains live-boot scripts
|
||||
- grub.cfg has boot=live
|
||||
- Fails build before copying to Builds if any check fails
|
||||
|
||||
## Critical Rules
|
||||
|
||||
1. **MBR**: Always use `branding/isohdpfx.bin` (Debian Live MBR, starts with `4552`). The ISOLINUX generic MBR (`33ed`) doesn't boot on all hardware.
|
||||
|
||||
2. **live-boot**: Must be installed via `chroot /installer apt-get install` AFTER debootstrap completes. The `--include` flag silently fails for live-boot.
|
||||
|
||||
3. **Initramfs**: `update-initramfs` needs `/proc`, `/sys`, `/dev` bind-mounted in the chroot. Without them, the initramfs is broken.
|
||||
|
||||
4. **scripts/live is a FILE**: Verify with `[ -e ]` not `[ -d ]`.
|
||||
|
||||
5. **Kernel params**: Must include `boot=live components`. Without `boot=live`, live-boot hooks never activate.
|
||||
|
||||
6. **partition_offset 16**: Required in xorriso for UEFI firmware to recognize the USB.
|
||||
|
||||
7. **Never push during a running CI build**: The gitea-runner kills in-progress builds when a new commit arrives on the same branch.
|
||||
|
||||
## Quick Commands
|
||||
|
||||
```bash
|
||||
# Build locally (on .228):
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228
|
||||
cd ~/archy/image-recipe
|
||||
sudo UNBUNDLED=1 DEV_SERVER=localhost BUILD_FROM_SOURCE=0 ./build-auto-installer-iso.sh
|
||||
|
||||
# Check build status:
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||
'cd ~/archy/image-recipe && sudo ./build-unbundled-iso.sh'
|
||||
"ps aux | grep build-auto | grep -v grep"
|
||||
|
||||
# Check latest ISO:
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||
"ls -lt /var/lib/archipelago/filebrowser/Builds/archipelago-dev-*.iso | head -3"
|
||||
|
||||
# Verify ISO:
|
||||
# See /iso-debug skill for the full verification checklist
|
||||
|
||||
# Iterate on branding without rebuilding:
|
||||
./image-recipe/dev-branding.sh [path-to-iso]
|
||||
# Or: ./scripts/dev-start.sh → option 0
|
||||
```
|
||||
|
||||
Output: `results/archipelago-installer-unbundled-x86_64.iso`
|
||||
## Key Files
|
||||
|
||||
### Full bundled ISO (~11GB)
|
||||
All container images pre-bundled for offline install.
|
||||
|
||||
```bash
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||
'cd ~/archy/image-recipe && sudo ./build-auto-installer-iso.sh'
|
||||
```
|
||||
|
||||
Output: `results/archipelago-installer-x86_64.iso`
|
||||
|
||||
## Post-build: ALWAYS publish to FileBrowser
|
||||
|
||||
After EVERY successful build, copy the ISO to the FileBrowser `Builds` folder so it's downloadable from the web UI. This is mandatory — do not skip.
|
||||
|
||||
**FileBrowser data root**: `/var/lib/archipelago/filebrowser/`
|
||||
|
||||
```bash
|
||||
# For unbundled:
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||
'sudo mkdir -p /var/lib/archipelago/filebrowser/Builds && \
|
||||
sudo cp ~/archy/image-recipe/results/archipelago-installer-unbundled-x86_64.iso /var/lib/archipelago/filebrowser/Builds/ && \
|
||||
sudo chown 1000:1000 /var/lib/archipelago/filebrowser/Builds/archipelago-installer-unbundled-x86_64.iso'
|
||||
|
||||
# For bundled:
|
||||
ssh -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228 \
|
||||
'sudo mkdir -p /var/lib/archipelago/filebrowser/Builds && \
|
||||
sudo cp ~/archy/image-recipe/results/archipelago-installer-x86_64.iso /var/lib/archipelago/filebrowser/Builds/ && \
|
||||
sudo chown 1000:1000 /var/lib/archipelago/filebrowser/Builds/archipelago-installer-x86_64.iso'
|
||||
```
|
||||
|
||||
## Post-build: Download to Mac (optional)
|
||||
|
||||
```bash
|
||||
# Unbundled:
|
||||
scp -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-installer-unbundled-x86_64.iso ~/Downloads/
|
||||
|
||||
# Bundled:
|
||||
scp -i ~/.ssh/archipelago-deploy archipelago@192.168.1.228:~/archy/image-recipe/results/archipelago-installer-x86_64.iso ~/Downloads/
|
||||
```
|
||||
|
||||
## Key paths on server
|
||||
|
||||
- Build scripts: `~/archy/image-recipe/build-auto-installer-iso.sh`, `build-unbundled-iso.sh`
|
||||
- Build output: `~/archy/image-recipe/results/`
|
||||
- Build cache (rootfs, base ISO): `~/archy/image-recipe/build/auto-installer/`
|
||||
- FileBrowser Builds: `/var/lib/archipelago/filebrowser/Builds/`
|
||||
|
||||
## Notes
|
||||
|
||||
- Use `--rebuild` flag to force rootfs rebuild (otherwise uses cached)
|
||||
- FileBrowser container mounts `/var/lib/archipelago/filebrowser` → `/srv`
|
||||
- Always `chown 1000:1000` files in FileBrowser so the app can serve them
|
||||
- **IMPORTANT**: Use `build-auto-installer-iso.sh` (or `build-unbundled-iso.sh`) only. The deprecated `build-debian-iso.sh` causes boot-to-prompt issues.
|
||||
| File | Role |
|
||||
|------|------|
|
||||
| `image-recipe/build-auto-installer-iso.sh` | Main build script (~2600 lines) |
|
||||
| `image-recipe/build-unbundled-iso.sh` | Wrapper: sets UNBUNDLED=1 |
|
||||
| `image-recipe/branding/isohdpfx.bin` | Proven MBR (432 bytes) |
|
||||
| `image-recipe/branding/grub-theme/` | GRUB theme + background |
|
||||
| `image-recipe/branding/plymouth-theme/` | Plymouth boot splash |
|
||||
| `scripts/image-versions.sh` | Pinned container image versions |
|
||||
| `.gitea/workflows/build-iso-dev.yml` | CI for dev-iso branch |
|
||||
| `image-recipe/test-iso-qemu.sh` | QEMU test script |
|
||||
| `image-recipe/dev-branding.sh` | Quick branding iteration |
|
||||
|
||||
146
.claude/skills/iso-branding/SKILL.md
Normal file
146
.claude/skills/iso-branding/SKILL.md
Normal file
@ -0,0 +1,146 @@
|
||||
---
|
||||
name: iso-branding
|
||||
description: Design and implement Archipelago boot visuals — GRUB theme, Plymouth splash, ISOLINUX menu, console banners. Handles pixel-art cyberpunk aesthetic with Bitcoin orange accents. Use when working on boot screen design, splash animations, GRUB backgrounds, or installer UI appearance.
|
||||
allowed-tools: Bash, Read, Write, Edit, Grep, Glob, Agent
|
||||
---
|
||||
|
||||
# ISO Boot Branding — Archipelago
|
||||
|
||||
Design and build the visual boot experience from USB power-on to web UI.
|
||||
|
||||
## Brand Identity
|
||||
|
||||
**Archipelago** = self-sovereign Bitcoin node OS. Floating islands in the sky.
|
||||
|
||||
| Element | Value |
|
||||
|---------|-------|
|
||||
| Primary accent | `#fb923c` (Bitcoin orange) |
|
||||
| Secondary accent | `#f7931a` (deeper orange) |
|
||||
| Success | `#4ade80` (green) |
|
||||
| Background | `#0a0a0a` → `#050505` (near-black) |
|
||||
| Text | `#ffffff` (white), `#aaaaaa` (dim), `#555555` (subtle) |
|
||||
| Glass | `rgba(255,255,255,0.06)` frost overlay |
|
||||
| Style | Pixel art cyberpunk, dark glass morphism, CRT scanlines |
|
||||
| Logo | Pixel-art lowercase "a" (from SVG favicon) |
|
||||
|
||||
## Boot Stages & What's Customizable
|
||||
|
||||
### 1. GRUB Menu (UEFI boot)
|
||||
- **Background**: `branding/grub-theme/background.png` — any PNG, GRUB scales it
|
||||
- **Theme**: `branding/grub-theme/theme.txt` — colors, layout, labels
|
||||
- **Fonts**: Generated with `grub-mkfont` during build, .pf2 format
|
||||
- **Config**: Written by build script in Step 5 (`grub.cfg` heredoc)
|
||||
|
||||
GRUB theme.txt properties that work:
|
||||
```
|
||||
desktop-color: "#rrggbb" # Fallback if no background
|
||||
desktop-image: "background.png" # Background image
|
||||
title-text: "" # Empty = no title
|
||||
|
||||
+ boot_menu {
|
||||
left/top/width/height = N%
|
||||
item_color = "#rrggbb"
|
||||
selected_item_color = "#rrggbb"
|
||||
item_height = N
|
||||
item_spacing = N
|
||||
scrollbar = false
|
||||
}
|
||||
|
||||
+ label {
|
||||
left/top/width = N%
|
||||
text = "string"
|
||||
color = "#rrggbb"
|
||||
align = "center"
|
||||
}
|
||||
```
|
||||
|
||||
**IMPORTANT**: Do NOT reference font names in theme.txt unless you know the exact internal name from grub-mkfont output. GRUB falls back to default if a font reference fails, which causes the ENTIRE theme to not load.
|
||||
|
||||
### 2. ISOLINUX Menu (BIOS boot)
|
||||
- **Config**: Written by build script in Step 5 (`isolinux.cfg` heredoc)
|
||||
- **Colors**: ANSI-style color codes in `MENU COLOR` directives
|
||||
- **Title**: `MENU TITLE` string
|
||||
- Text-only — no background image (use `vesamenu.c32` for graphical, but `menu.c32` is more compatible)
|
||||
|
||||
### 3. Plymouth Splash (kernel boot → login)
|
||||
- **Theme**: `branding/plymouth-theme/archipelago.script`
|
||||
- **Logo**: `branding/plymouth-theme/logo.png` (PNG with transparency)
|
||||
- **Config**: `branding/plymouth-theme/archipelago.plymouth`
|
||||
- Supports: animated progress bar, logo sprites, LUKS password prompt
|
||||
- Kernel param `splash` must be present (added to GRUB_CMDLINE_LINUX_DEFAULT)
|
||||
|
||||
Plymouth script language:
|
||||
```javascript
|
||||
Window.SetBackgroundTopColor(r, g, b); // 0.0-1.0
|
||||
logo = Image("logo.png");
|
||||
sprite = Sprite(logo);
|
||||
sprite.SetX(x); sprite.SetY(y);
|
||||
Plymouth.SetRefreshFunction(callback);
|
||||
Plymouth.SetBootProgressFunction(callback);
|
||||
Plymouth.SetDisplayPasswordFunction(callback);
|
||||
```
|
||||
|
||||
### 4. Console Banner (TTY login)
|
||||
- ASCII art + system info in `/etc/profile.d/archipelago.sh`
|
||||
- Generated in auto-install.sh (Step 4, the INSTALLER_SCRIPT heredoc)
|
||||
- Uses ANSI escape codes for color
|
||||
|
||||
### 5. Installer Prompt
|
||||
- "ARCHIPELAGO BITCOIN NODE OS / Automatic Installer"
|
||||
- In the systemd service wrapper: `/usr/local/bin/archipelago-start-installer`
|
||||
- Built inside the debootstrap container in Step 2
|
||||
|
||||
## Dev Workflow
|
||||
|
||||
### Quick preview (no ISO needed)
|
||||
```bash
|
||||
# Edit background, see it instantly:
|
||||
open image-recipe/branding/grub-theme/background.png
|
||||
|
||||
# Generate procedural background:
|
||||
python3 image-recipe/branding/generate-grub-background.py /tmp/bg.png && open /tmp/bg.png
|
||||
|
||||
# Generate Plymouth logo:
|
||||
python3 image-recipe/branding/generate-plymouth-logo.py /tmp/logo.png && open /tmp/logo.png
|
||||
```
|
||||
|
||||
### Full boot test (needs base ISO)
|
||||
```bash
|
||||
./image-recipe/dev-branding.sh [path-to-iso]
|
||||
# Or via dev-start.sh option 0
|
||||
```
|
||||
Extracts ISO → patches branding → repackages → boots QEMU. ~30 seconds.
|
||||
|
||||
### What to edit
|
||||
| File | Affects |
|
||||
|------|---------|
|
||||
| `branding/grub-theme/background.png` | GRUB boot screen image |
|
||||
| `branding/grub-theme/theme.txt` | GRUB menu colors, layout |
|
||||
| `branding/plymouth-theme/logo.png` | Plymouth boot logo |
|
||||
| `branding/plymouth-theme/archipelago.script` | Plymouth animation/progress |
|
||||
| `branding/generate-grub-background.py` | Procedural background generator |
|
||||
| `branding/generate-plymouth-logo.py` | Procedural logo generator |
|
||||
|
||||
## Image Specs
|
||||
|
||||
| Asset | Format | Size | Notes |
|
||||
|-------|--------|------|-------|
|
||||
| GRUB background | PNG | 1024x768 recommended | GRUB scales any size, but large images slow boot |
|
||||
| Plymouth logo | PNG (RGBA) | 256x256 recommended | Transparent background |
|
||||
| GRUB fonts | .pf2 | Generated | `grub-mkfont -s SIZE -o out.pf2 input.ttf` |
|
||||
|
||||
## Build Integration
|
||||
|
||||
GRUB theme is installed in Step 2 (after artifacts placed):
|
||||
- Static `background.png` copied from `branding/grub-theme/`
|
||||
- Falls back to Python generator if static file missing
|
||||
- Fonts generated in debootstrap container with `grub-mkfont`
|
||||
|
||||
Plymouth theme installed in Step 3 (component copy) + Step 4 (auto-install.sh):
|
||||
- Files copied to `$ARCH_DIR/plymouth-theme/` in ISO
|
||||
- Auto-install.sh copies to target at `/usr/share/plymouth/themes/archipelago/`
|
||||
- Sets as default via `plymouth-set-default-theme`
|
||||
|
||||
GRUB theme also installed on TARGET system (not just installer):
|
||||
- Auto-install.sh copies theme to `/mnt/target/boot/grub/themes/archipelago/`
|
||||
- Adds `GRUB_THEME=` to `/mnt/target/etc/default/grub`
|
||||
175
.claude/skills/iso-debug/SKILL.md
Normal file
175
.claude/skills/iso-debug/SKILL.md
Normal file
@ -0,0 +1,175 @@
|
||||
---
|
||||
name: iso-debug
|
||||
description: Diagnose and fix Archipelago ISO boot failures. Covers hybrid MBR/GPT, UEFI/BIOS boot chains, live-boot initramfs, GRUB/ISOLINUX configuration, xorriso packaging, and USB boot compatibility. Use when ISO doesn't boot, installer doesn't start, kernel panics, or USB isn't recognized by BIOS/UEFI.
|
||||
allowed-tools: Bash, Read, Grep, Glob, Agent, Edit
|
||||
---
|
||||
|
||||
# ISO Boot Debugging — Archipelago Custom Base
|
||||
|
||||
Systematic diagnosis of ISO boot failures for the Archipelago debootstrap-based installer.
|
||||
|
||||
## Architecture
|
||||
|
||||
The ISO boot chain has 5 stages. Failure at any stage has distinct symptoms:
|
||||
|
||||
| Stage | Component | Symptom if broken |
|
||||
|-------|-----------|-------------------|
|
||||
| 1. BIOS/UEFI recognition | Hybrid MBR + GPT | USB not in boot menu at all |
|
||||
| 2. Bootloader | ISOLINUX (BIOS) or GRUB EFI (UEFI) | Black screen after selecting USB |
|
||||
| 3. Kernel + initramfs | vmlinuz + initrd.img with live-boot | Kernel panic or initramfs shell |
|
||||
| 4. Root filesystem | live-boot mounts filesystem.squashfs | "No root device" or blank screen |
|
||||
| 5. Installer | systemd service + auto-install.sh | Boots to shell but no installer prompt |
|
||||
|
||||
## Stage 1: USB Not Recognized
|
||||
|
||||
**Most common cause**: Wrong MBR code in the ISO hybrid boot sector.
|
||||
|
||||
### Diagnosis
|
||||
```bash
|
||||
# Compare first 16 bytes of working vs broken ISO
|
||||
xxd -l 16 working.iso
|
||||
xxd -l 16 broken.iso
|
||||
|
||||
# Check for valid boot signature at offset 510
|
||||
xxd -s 510 -l 2 broken.iso
|
||||
# Must show: 55aa
|
||||
```
|
||||
|
||||
### Known MBR codes
|
||||
- `4552` — Debian Live MBR (extracted from Debian Live ISO). **Works on all tested hardware.**
|
||||
- `33ed` — ISOLINUX package generic isohdpfx.bin. **Does NOT work on some UEFI hardware.**
|
||||
|
||||
### Fix
|
||||
The project ships the proven MBR at `image-recipe/branding/isohdpfx.bin` (432 bytes, starts with `4552`).
|
||||
Build script uses it via: `-isohybrid-mbr "$SCRIPT_DIR/branding/isohdpfx.bin"`
|
||||
|
||||
### xorriso flags that matter
|
||||
- `-isohybrid-mbr <file>` — Embeds MBR code for USB hybrid boot
|
||||
- `-isohybrid-gpt-basdat` — Adds GPT partition entry for EFI (REQUIRED for UEFI USB boot)
|
||||
- `-partition_offset 16` — Reserves space for GPT table (REQUIRED — without this some UEFI firmware won't see the USB)
|
||||
- `-eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot` — EFI boot catalog entry
|
||||
|
||||
### Balena Etcher
|
||||
Writes raw ISO to USB — no special formatting. If the ISO boots in QEMU but not on hardware, the MBR code is the issue, not Etcher.
|
||||
|
||||
## Stage 2: Bootloader Failure
|
||||
|
||||
### BIOS path: ISOLINUX
|
||||
Required files in ISO: `isolinux/isolinux.bin`, `isolinux/ldlinux.c32`, `isolinux/boot.cat`
|
||||
Config: `isolinux/isolinux.cfg`
|
||||
|
||||
### UEFI path: GRUB
|
||||
Required files: `EFI/BOOT/BOOTX64.EFI`, `boot/grub/efi.img`, `boot/grub/grub.cfg`
|
||||
The EFI image is a FAT32 filesystem containing the GRUB binary, built with:
|
||||
```bash
|
||||
grub-mkimage -O x86_64-efi -o BOOTX64.EFI -p /boot/grub \
|
||||
part_gpt part_msdos fat iso9660 udf normal boot linux search \
|
||||
search_fs_uuid search_fs_file search_label configfile echo cat \
|
||||
ls test true loopback gfxterm gfxmenu font png all_video video \
|
||||
video_bochs video_cirrus efi_gop efi_uga
|
||||
```
|
||||
**Critical**: `all_video`, `efi_gop`, `efi_uga` needed for display on real hardware.
|
||||
|
||||
### Diagnosis
|
||||
```bash
|
||||
# Mount ISO and verify files
|
||||
sudo mount -o loop,ro broken.iso /mnt
|
||||
ls -la /mnt/isolinux/
|
||||
ls -la /mnt/EFI/BOOT/
|
||||
cat /mnt/boot/grub/grub.cfg
|
||||
cat /mnt/isolinux/isolinux.cfg
|
||||
sudo umount /mnt
|
||||
```
|
||||
|
||||
## Stage 3: Kernel / Initramfs
|
||||
|
||||
### live-boot
|
||||
The initramfs must contain live-boot hooks. Without them, the kernel boots but can't find root.
|
||||
|
||||
**Kernel params required**: `boot=live components`
|
||||
- `boot=live` — triggers live-boot's initramfs scripts
|
||||
- `components` — tells live-boot to scan live/ for squashfs files
|
||||
|
||||
### Verify initramfs has live-boot
|
||||
```bash
|
||||
TMPDIR=$(mktemp -d)
|
||||
unmkinitramfs /path/to/initrd.img $TMPDIR
|
||||
# live-boot installs scripts/live as a FILE (not directory)
|
||||
ls -la $TMPDIR/scripts/live # or $TMPDIR/main/scripts/live
|
||||
file $TMPDIR/scripts/live # Should say "ASCII text"
|
||||
```
|
||||
|
||||
### Common initramfs failures
|
||||
1. **live-boot not installed**: debootstrap `--include` can't resolve its deps. Must install via `chroot apt-get` after debootstrap.
|
||||
2. **Broken initramfs from container build**: `update-initramfs` needs `/proc`, `/sys`, `/dev` mounted in the chroot.
|
||||
3. **scripts/live is a FILE not directory**: Verification code must use `[ -e ]` not `[ -d ]`.
|
||||
|
||||
## Stage 4: Root Filesystem
|
||||
|
||||
live-boot searches for squashfs files in `live/` on the boot media.
|
||||
- Mounts boot media (USB/CDROM) at `/run/live/medium`
|
||||
- Finds `live/filesystem.squashfs`
|
||||
- Mounts it read-only, creates tmpfs overlay
|
||||
- pivot_root into the combined root
|
||||
|
||||
### Diagnosis
|
||||
If you get an initramfs shell prompt `(initramfs)`:
|
||||
```bash
|
||||
# Inside initramfs shell:
|
||||
ls /run/live/medium/ # Is boot media mounted?
|
||||
ls /run/live/medium/live/ # Is squashfs there?
|
||||
cat /proc/cmdline # Does it have boot=live?
|
||||
```
|
||||
|
||||
## Stage 5: Installer Not Starting
|
||||
|
||||
The installer auto-starts via:
|
||||
1. Getty auto-login on tty1 (root, no password)
|
||||
2. systemd service `archipelago-installer.service`
|
||||
3. Wrapper script searches for boot media at: `/run/live/medium`, `/run/archiso`, `/cdrom`
|
||||
|
||||
### Diagnosis
|
||||
If you get a shell but no installer prompt:
|
||||
```bash
|
||||
systemctl status archipelago-installer.service
|
||||
cat /usr/local/bin/archipelago-start-installer
|
||||
ls /run/live/medium/archipelago/auto-install.sh
|
||||
```
|
||||
|
||||
## Quick Verification Checklist
|
||||
|
||||
Run against any ISO before flashing:
|
||||
```bash
|
||||
ISO=path/to/iso
|
||||
MNT=$(mktemp -d)
|
||||
sudo mount -o loop,ro $ISO $MNT
|
||||
|
||||
echo "=== MBR ===" && xxd -l 4 $ISO
|
||||
echo "=== Boot sig ===" && xxd -s 510 -l 2 $ISO
|
||||
echo "=== Files ===" && for f in live/vmlinuz live/initrd.img live/filesystem.squashfs isolinux/isolinux.bin EFI/BOOT/BOOTX64.EFI boot/grub/grub.cfg archipelago/auto-install.sh; do [ -e $MNT/$f ] && echo "OK: $f" || echo "MISSING: $f"; done
|
||||
echo "=== Kernel params ===" && grep "boot=live" $MNT/boot/grub/grub.cfg && echo OK || echo MISSING
|
||||
echo "=== live-boot ===" && INITRD=$(mktemp -d) && unmkinitramfs $MNT/live/initrd.img $INITRD 2>/dev/null && ([ -e $INITRD/scripts/live ] && echo "OK" || echo "MISSING")
|
||||
|
||||
sudo umount $MNT
|
||||
```
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `image-recipe/build-auto-installer-iso.sh` | Main build script (~2600 lines) |
|
||||
| `image-recipe/branding/isohdpfx.bin` | Proven MBR code (432 bytes) |
|
||||
| `image-recipe/branding/grub-theme/` | GRUB theme (theme.txt + background.png) |
|
||||
| `image-recipe/branding/plymouth-theme/` | Plymouth boot splash |
|
||||
| `.gitea/workflows/build-iso-dev.yml` | CI workflow with smoke test |
|
||||
| `image-recipe/test-iso-qemu.sh` | QEMU testing script |
|
||||
| `image-recipe/dev-branding.sh` | Quick branding iteration (patch + repackage) |
|
||||
|
||||
## Infrastructure
|
||||
|
||||
| What | Where |
|
||||
|------|-------|
|
||||
| CI runner | gitea-runner.service on 192.168.1.228 |
|
||||
| ISO builds | FileBrowser at http://192.168.1.228:8083 → Builds/ |
|
||||
| Dev branch | dev-iso (separate CI: build-iso-dev.yml) |
|
||||
| Main branch | main (CI: build-iso.yml) — DO NOT break |
|
||||
367
.claude/skills/iso-debug/references/boot-chain-reference.md
Normal file
367
.claude/skills/iso-debug/references/boot-chain-reference.md
Normal file
@ -0,0 +1,367 @@
|
||||
# Custom Debian ISO Boot Chain — Technical Reference
|
||||
|
||||
Expert reference for building and debugging custom bootable Debian-based ISOs.
|
||||
Covers hybrid MBR/GPT, live-boot, debootstrap, GRUB, ISOLINUX, Plymouth, and xorriso.
|
||||
|
||||
---
|
||||
|
||||
## 1. Hybrid MBR/GPT for USB Boot
|
||||
|
||||
### What is isohdpfx.bin?
|
||||
The first 432 bytes of a hybrid-bootable ISO. Contains the Master Boot Record code that BIOS firmware executes when booting from USB. Different sources produce different MBR code:
|
||||
|
||||
| Source | First bytes | Compatibility |
|
||||
|--------|-------------|---------------|
|
||||
| Debian Live ISO (`dd if=debian-live.iso bs=1 count=432`) | `45 52` | Best — works on all tested hardware |
|
||||
| `/usr/lib/ISOLINUX/isohdpfx.bin` | `33 ed` | Generic — fails on some UEFI hardware |
|
||||
| Manually built with `isohybrid` | Varies | Unpredictable |
|
||||
|
||||
**Rule**: Always extract MBR from a known-working ISO. Never rely on the generic ISOLINUX one.
|
||||
|
||||
### xorriso flags for hybrid boot
|
||||
```bash
|
||||
xorriso -as mkisofs -o output.iso \
|
||||
-isohybrid-mbr isohdpfx.bin \ # Embeds MBR for BIOS USB boot
|
||||
-c isolinux/boot.cat \ # El Torito boot catalog
|
||||
-b isolinux/isolinux.bin \ # BIOS bootloader
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
-eltorito-alt-boot \ # Second boot entry (EFI)
|
||||
-e boot/grub/efi.img \ # EFI boot image
|
||||
-no-emul-boot \
|
||||
-isohybrid-gpt-basdat \ # Adds GPT partition for EFI
|
||||
-partition_offset 16 \ # Space for GPT table — REQUIRED for UEFI
|
||||
/path/to/iso/contents
|
||||
```
|
||||
|
||||
**Critical flags**:
|
||||
- `-isohybrid-gpt-basdat`: Without this, UEFI firmware won't see the EFI partition
|
||||
- `-partition_offset 16`: Reserves 16 sectors for GPT. Without it, some UEFI firmware ignores the USB entirely
|
||||
- `-isohybrid-mbr`: Without this, the ISO won't boot from USB at all (only CD-ROM)
|
||||
|
||||
### Balena Etcher
|
||||
Writes the ISO byte-for-byte to USB — no reformatting, no special partition creation. If the ISO works with `dd`, it works with Etcher. If BIOS doesn't see the USB, the MBR code is wrong, not Etcher.
|
||||
|
||||
### Verifying hybrid structure
|
||||
```bash
|
||||
xxd -l 4 image.iso # MBR code (should be 45 52 for Debian Live)
|
||||
xxd -s 510 -l 2 image.iso # Boot signature (must be 55 aa)
|
||||
xxd -s 512 -l 8 image.iso # GPT signature at LBA 1 (should be "EFI PART")
|
||||
file image.iso # Should say "DOS/MBR boot sector" and "bootable"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. live-boot Package
|
||||
|
||||
### What it does
|
||||
Provides initramfs hooks that mount a squashfs file as the root filesystem using overlayfs. This is how every Debian/Ubuntu live ISO works.
|
||||
|
||||
Boot flow: kernel → initramfs → live-boot scripts → find squashfs → mount overlayfs → pivot_root → systemd
|
||||
|
||||
### Package structure
|
||||
- `live-boot` (~29KB): Main package, boot scripts
|
||||
- `live-boot-initramfs-tools` (~6KB): Initramfs hooks that get baked into initrd.img
|
||||
|
||||
**Critical**: `scripts/live` is a **FILE**, not a directory. Verification must use `[ -e ]` not `[ -d ]`.
|
||||
|
||||
### Kernel parameters
|
||||
| Parameter | Required | Effect |
|
||||
|-----------|----------|--------|
|
||||
| `boot=live` | YES | Activates live-boot's initramfs hooks |
|
||||
| `components` | YES | Scans live/ for additional squashfs modules |
|
||||
| `toram` | No | Copies squashfs to RAM (faster, allows USB removal) |
|
||||
| `persistence` | No | Enables writable overlay on a partition labeled "persistence" |
|
||||
| `quiet` | No | Suppresses boot messages |
|
||||
| `splash` | No | Enables Plymouth splash screen |
|
||||
| `console=ttyS0,115200` | No | Serial console for QEMU debugging |
|
||||
|
||||
### Where live-boot mounts things
|
||||
- `/run/live/medium` — The boot media (USB/CDROM) mount point
|
||||
- `/run/live/rootfs/filesystem.squashfs` — The mounted squashfs
|
||||
- `/run/live/overlay` — The tmpfs overlay for writes
|
||||
|
||||
### Verifying live-boot in initramfs
|
||||
```bash
|
||||
TMPDIR=$(mktemp -d)
|
||||
unmkinitramfs /path/to/initrd.img $TMPDIR
|
||||
# Check for live-boot scripts
|
||||
file $TMPDIR/scripts/live # Should be "ASCII text"
|
||||
# OR (some initramfs have main/ prefix)
|
||||
file $TMPDIR/main/scripts/live
|
||||
```
|
||||
|
||||
### Common failures
|
||||
1. **live-boot not in initrd**: Installed in rootfs but initramfs not regenerated after
|
||||
2. **Missing kernel params**: `boot=live` not in GRUB/ISOLINUX config
|
||||
3. **Broken initramfs**: Built without /proc /sys /dev mounted in chroot
|
||||
4. **Wrong verification**: `[ -d scripts/live ]` fails because it's a file
|
||||
|
||||
---
|
||||
|
||||
## 3. debootstrap for Installer Environments
|
||||
|
||||
### Variants
|
||||
- `--variant=minbase`: Absolute minimum (~150MB). Only essential + apt. Good for installer squashfs.
|
||||
- Default (no variant): Full base system (~300MB). More packages, fewer missing deps.
|
||||
|
||||
### --include limitations
|
||||
debootstrap's minbase resolver is simplified and **cannot resolve complex dependency chains**. Packages like `live-boot` that depend on `initramfs-tools` which depends on many other packages will silently fail or be skipped.
|
||||
|
||||
**Fix**: Install complex packages via `chroot apt-get` after debootstrap completes:
|
||||
```bash
|
||||
debootstrap --variant=minbase --include=basic,packages bookworm /installer http://deb.debian.org/debian
|
||||
# Then:
|
||||
mount --bind /proc /installer/proc
|
||||
mount --bind /sys /installer/sys
|
||||
mount --bind /dev /installer/dev
|
||||
chroot /installer apt-get update
|
||||
chroot /installer apt-get install -y live-boot live-boot-initramfs-tools
|
||||
umount /installer/dev /installer/sys /installer/proc
|
||||
```
|
||||
|
||||
### Initramfs generation inside containers
|
||||
`update-initramfs` REQUIRES `/proc`, `/sys`, `/dev` to be mounted in the chroot. Without them:
|
||||
- Module detection fails (can't read /proc/modules)
|
||||
- Device nodes missing (can't detect hardware)
|
||||
- The resulting initramfs boots but can't load kernel modules
|
||||
|
||||
### Container-in-container considerations
|
||||
When running debootstrap inside a Podman/Docker container on a CI runner:
|
||||
- `--privileged` flag needed for chroot to work
|
||||
- The container runtime may kill the container after debootstrap exits if using `set -e`
|
||||
- proc/sys/dev mounts inside the debootstrapped chroot work fine with `--privileged`
|
||||
|
||||
---
|
||||
|
||||
## 4. GRUB Theming
|
||||
|
||||
### theme.txt format
|
||||
```
|
||||
desktop-color: "#0a0a0a" # Fallback background color
|
||||
desktop-image: "background.png" # Background image (any PNG, GRUB scales)
|
||||
title-text: "" # Empty = hide title
|
||||
|
||||
+ boot_menu {
|
||||
left = 25%
|
||||
top = 40%
|
||||
width = 50%
|
||||
height = 30%
|
||||
item_color = "#aaaaaa" # Normal menu item color
|
||||
selected_item_color = "#fb923c" # Selected item color
|
||||
item_height = 36
|
||||
item_spacing = 8
|
||||
scrollbar = false
|
||||
}
|
||||
|
||||
+ label {
|
||||
left = 25%
|
||||
top = 20%
|
||||
width = 50%
|
||||
text = "Some Text"
|
||||
color = "#f7931a"
|
||||
align = "center"
|
||||
}
|
||||
```
|
||||
|
||||
**IMPORTANT**: Do NOT specify `font = "Name Size"` in theme elements unless you know the exact internal font name. If GRUB can't find the font, the ENTIRE theme fails to load and you get the ugly default.
|
||||
|
||||
### Font handling
|
||||
```bash
|
||||
# Generate .pf2 font file
|
||||
grub-mkfont -s 16 -o dejavu_16.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
|
||||
|
||||
# In grub.cfg, load fonts BEFORE setting theme:
|
||||
loadfont /boot/grub/font.pf2
|
||||
loadfont /boot/grub/themes/archipelago/dejavu_16.pf2
|
||||
set theme=/boot/grub/themes/archipelago/theme.txt
|
||||
```
|
||||
|
||||
### Background images
|
||||
- Any PNG works, GRUB scales to screen resolution
|
||||
- Smaller images (1024x768) load faster
|
||||
- Large images (3000x2000+) add seconds to boot and may fail on limited GRUB heap
|
||||
|
||||
### grub-mkimage — essential modules for ISO boot
|
||||
```bash
|
||||
grub-mkimage -O x86_64-efi -o BOOTX64.EFI -p /boot/grub \
|
||||
part_gpt part_msdos fat iso9660 udf \ # Filesystem access
|
||||
normal boot linux search search_fs_uuid search_fs_file search_label \
|
||||
configfile echo cat ls test true \ # Basic commands
|
||||
loopback \ # Loop device support
|
||||
gfxterm gfxmenu font png \ # Graphical display
|
||||
all_video video video_bochs video_cirrus \ # Video drivers
|
||||
efi_gop efi_uga # EFI display protocols
|
||||
```
|
||||
|
||||
Missing `all_video`/`efi_gop` = black screen on real hardware (works in QEMU).
|
||||
|
||||
### EFI boot image creation
|
||||
```bash
|
||||
dd if=/dev/zero of=efi.img bs=1M count=4
|
||||
mkfs.vfat efi.img
|
||||
mmd -i efi.img ::/EFI ::/EFI/BOOT
|
||||
mcopy -i efi.img BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Plymouth Boot Splash
|
||||
|
||||
### Theme types
|
||||
- **script**: Most flexible. Lua-like scripting with sprites, animations, callbacks.
|
||||
- **two-step**: Simple logo + spinner. Less customizable but easier.
|
||||
- **fade-in**: Logo fades in. Minimal.
|
||||
|
||||
### Script theme structure
|
||||
```
|
||||
/usr/share/plymouth/themes/mytheme/
|
||||
mytheme.plymouth # Theme metadata
|
||||
mytheme.script # Animation script
|
||||
logo.png # Logo image (PNG with alpha)
|
||||
```
|
||||
|
||||
### mytheme.plymouth
|
||||
```ini
|
||||
[Plymouth Theme]
|
||||
Name=MyTheme
|
||||
Description=Custom boot splash
|
||||
ModuleName=script
|
||||
|
||||
[script]
|
||||
ImageDir=/usr/share/plymouth/themes/mytheme
|
||||
ScriptFile=/usr/share/plymouth/themes/mytheme/mytheme.script
|
||||
```
|
||||
|
||||
### Script language key functions
|
||||
```javascript
|
||||
Window.SetBackgroundTopColor(r, g, b); // 0.0-1.0 floats
|
||||
Window.SetBackgroundBottomColor(r, g, b);
|
||||
image = Image("logo.png");
|
||||
sprite = Sprite(image);
|
||||
sprite.SetX(x); sprite.SetY(y); sprite.SetOpacity(0.0-1.0);
|
||||
Plymouth.SetRefreshFunction(fn); // Called every frame
|
||||
Plymouth.SetBootProgressFunction(fn); // fn(duration, progress)
|
||||
Plymouth.SetDisplayPasswordFunction(fn); // fn(prompt, bullets)
|
||||
Plymouth.SetQuitFunction(fn);
|
||||
screen_w = Window.GetWidth();
|
||||
screen_h = Window.GetHeight();
|
||||
```
|
||||
|
||||
### Setting default theme
|
||||
```bash
|
||||
plymouth-set-default-theme mytheme
|
||||
# OR manually:
|
||||
ln -sf /usr/share/plymouth/themes/mytheme/mytheme.plymouth /etc/alternatives/default.plymouth
|
||||
```
|
||||
|
||||
### Kernel params
|
||||
- `splash` in GRUB_CMDLINE_LINUX_DEFAULT enables Plymouth
|
||||
- `quiet` suppresses text that would overlay Plymouth
|
||||
|
||||
---
|
||||
|
||||
## 6. ISOLINUX/SYSLINUX
|
||||
|
||||
### Required files
|
||||
| File | Source | Purpose |
|
||||
|------|--------|---------|
|
||||
| `isolinux.bin` | `/usr/lib/ISOLINUX/isolinux.bin` | BIOS bootloader |
|
||||
| `ldlinux.c32` | `/usr/lib/syslinux/modules/bios/ldlinux.c32` | Core library (REQUIRED) |
|
||||
| `menu.c32` | `/usr/lib/syslinux/modules/bios/menu.c32` | Text menu UI |
|
||||
| `libutil.c32` | `/usr/lib/syslinux/modules/bios/libutil.c32` | Utility library |
|
||||
| `boot.cat` | Auto-generated by xorriso | El Torito boot catalog |
|
||||
| `isohdpfx.bin` | Extracted from working ISO | Hybrid MBR code |
|
||||
|
||||
### Configuration (isolinux.cfg)
|
||||
```
|
||||
UI menu.c32
|
||||
PROMPT 0
|
||||
TIMEOUT 50 # 5 seconds (units of 1/10 second)
|
||||
DEFAULT install
|
||||
|
||||
MENU TITLE MY INSTALLER
|
||||
MENU COLOR border 30;44 #40ffffff #00000000 std
|
||||
MENU COLOR title 1;36;44 #ff00b7ff #00000000 std
|
||||
MENU COLOR sel 7;37;40 #ffffffff #ff333333 std
|
||||
MENU COLOR unsel 37;44 #ffaaaaaa #00000000 std
|
||||
|
||||
LABEL install
|
||||
MENU LABEL Install System
|
||||
KERNEL /live/vmlinuz
|
||||
APPEND initrd=/live/initrd.img boot=live components quiet
|
||||
MENU DEFAULT
|
||||
```
|
||||
|
||||
### menu.c32 vs vesamenu.c32
|
||||
- `menu.c32`: Text-mode menu. More compatible, no background image.
|
||||
- `vesamenu.c32`: VESA graphical menu. Supports background PNG, but some hardware/VMs don't support VESA.
|
||||
|
||||
---
|
||||
|
||||
## 7. Testing Without Real Hardware
|
||||
|
||||
### QEMU UEFI boot
|
||||
```bash
|
||||
qemu-system-x86_64 \
|
||||
-machine q35 \
|
||||
-drive if=pflash,format=raw,readonly=on,file=/path/to/OVMF_CODE.fd \
|
||||
-m 4G -smp 2 \
|
||||
-boot d -cdrom image.iso \
|
||||
-drive if=virtio,format=qcow2,file=test-disk.qcow2 \
|
||||
-vga virtio -display default
|
||||
```
|
||||
|
||||
### QEMU BIOS boot (sees ISOLINUX)
|
||||
```bash
|
||||
qemu-system-x86_64 \
|
||||
-machine pc \
|
||||
-m 4G -smp 2 \
|
||||
-boot d -cdrom image.iso \
|
||||
-drive if=virtio,format=qcow2,file=test-disk.qcow2 \
|
||||
-vga virtio -display default
|
||||
```
|
||||
|
||||
### Serial console capture
|
||||
Add to QEMU: `-serial file:/tmp/serial.log`
|
||||
Add to kernel params: `console=ttyS0,115200 console=tty0`
|
||||
|
||||
### ISO structure verification (no boot required)
|
||||
```bash
|
||||
MNT=$(mktemp -d)
|
||||
sudo mount -o loop,ro image.iso $MNT
|
||||
|
||||
# Check all critical files
|
||||
for f in live/vmlinuz live/initrd.img live/filesystem.squashfs \
|
||||
isolinux/isolinux.bin EFI/BOOT/BOOTX64.EFI boot/grub/grub.cfg; do
|
||||
[ -e $MNT/$f ] && echo "OK: $f" || echo "MISSING: $f"
|
||||
done
|
||||
|
||||
# Check initramfs for live-boot
|
||||
INITRD=$(mktemp -d)
|
||||
unmkinitramfs $MNT/live/initrd.img $INITRD
|
||||
[ -e $INITRD/scripts/live ] && echo "live-boot: OK" || echo "live-boot: MISSING"
|
||||
|
||||
# Check kernel params
|
||||
grep "boot=live" $MNT/boot/grub/grub.cfg && echo "params: OK"
|
||||
|
||||
sudo umount $MNT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Security Considerations for Custom ISOs
|
||||
|
||||
### Supply chain
|
||||
- Pin the Debian mirror URL (don't use redirectors in production)
|
||||
- Verify package signatures (debootstrap does this by default)
|
||||
- Pin kernel and GRUB package versions for reproducibility
|
||||
|
||||
### Installer security
|
||||
- Auto-install.sh runs as root — validate all inputs before path construction
|
||||
- LUKS key generation must use CSPRNG (`/dev/urandom`, never `/dev/random` which blocks)
|
||||
- Drop the LUKS key file after writing to crypttab (or store in root-only location with 0400)
|
||||
|
||||
### Boot security
|
||||
- Secure Boot requires signed GRUB EFI binary (shim-signed package)
|
||||
- Without Secure Boot, the unsigned BOOTX64.EFI works but users must disable Secure Boot in BIOS
|
||||
- The MBR code (isohdpfx.bin) is not signed — Secure Boot only validates EFI path
|
||||
@ -637,16 +637,25 @@ umount /installer/proc 2>/dev/null || true
|
||||
echo " [container] Creating installer squashfs..."
|
||||
mksquashfs /installer /output/filesystem.squashfs -comp xz -Xbcj x86 -noappend -quiet
|
||||
|
||||
# Build GRUB EFI image
|
||||
# Build GRUB EFI image with embedded bootstrap config (grub-mkstandalone)
|
||||
# This ensures GRUB can find its config on real hardware, not just QEMU
|
||||
echo " [container] Building GRUB EFI image..."
|
||||
grub-mkimage -O x86_64-efi -o /output/BOOTX64.EFI -p /boot/grub \
|
||||
part_gpt part_msdos fat iso9660 udf normal boot linux search \
|
||||
search_fs_uuid search_fs_file search_label configfile echo cat \
|
||||
ls test true loopback gfxterm gfxmenu font png all_video video \
|
||||
video_bochs video_cirrus efi_gop efi_uga
|
||||
cat > /tmp/grub-embed.cfg <<GRUBEMBED
|
||||
search --no-floppy --set=root --label ARCHIPELAGO
|
||||
set prefix=(\\\$root)/boot/grub
|
||||
configfile \\\$prefix/grub.cfg
|
||||
GRUBEMBED
|
||||
|
||||
# Create EFI FAT image (4MB)
|
||||
dd if=/dev/zero of=/output/efi.img bs=1M count=4 2>/dev/null
|
||||
grub-mkstandalone -O x86_64-efi \
|
||||
--modules="part_gpt part_msdos fat iso9660 all_video font gfxterm" \
|
||||
--locales="" \
|
||||
--themes="" \
|
||||
--fonts="" \
|
||||
--output=/output/BOOTX64.EFI \
|
||||
"boot/grub/grub.cfg=/tmp/grub-embed.cfg"
|
||||
|
||||
# Create EFI FAT image (20MB — includes GRUB binary + grub.cfg)
|
||||
dd if=/dev/zero of=/output/efi.img bs=1M count=20 2>/dev/null
|
||||
mkfs.vfat /output/efi.img >/dev/null
|
||||
mmd -i /output/efi.img ::/EFI ::/EFI/BOOT
|
||||
mcopy -i /output/efi.img /output/BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
|
||||
@ -683,7 +692,7 @@ cp "$WORK_DIR/vmlinuz" "$INSTALLER_ISO/live/vmlinuz"
|
||||
cp "$WORK_DIR/initrd.img" "$INSTALLER_ISO/live/initrd.img"
|
||||
cp "$WORK_DIR/filesystem.squashfs" "$INSTALLER_ISO/live/filesystem.squashfs"
|
||||
cp "$WORK_DIR/BOOTX64.EFI" "$INSTALLER_ISO/EFI/BOOT/BOOTX64.EFI"
|
||||
cp "$WORK_DIR/efi.img" "$INSTALLER_ISO/boot/grub/efi.img"
|
||||
# Note: efi.img stays in $WORK_DIR — it gets appended as GPT partition 2 by xorriso
|
||||
cp "$WORK_DIR/isolinux.bin" "$INSTALLER_ISO/isolinux/isolinux.bin"
|
||||
cp "$WORK_DIR/ldlinux.c32" "$INSTALLER_ISO/isolinux/ldlinux.c32"
|
||||
cp "$WORK_DIR/menu.c32" "$INSTALLER_ISO/isolinux/menu.c32" 2>/dev/null || true
|
||||
@ -2501,11 +2510,19 @@ echo "Step 5: Configuring boot loaders..."
|
||||
# Create GRUB configuration
|
||||
echo " Writing GRUB config..."
|
||||
cat > "$INSTALLER_ISO/boot/grub/grub.cfg" <<'GRUBCFG'
|
||||
insmod part_gpt
|
||||
insmod part_msdos
|
||||
insmod fat
|
||||
insmod iso9660
|
||||
insmod all_video
|
||||
|
||||
search --no-floppy --set=root --label ARCHIPELAGO
|
||||
|
||||
set timeout=5
|
||||
set default=0
|
||||
|
||||
# Load font for graphical menu
|
||||
if loadfont /boot/grub/font.pf2; then
|
||||
if loadfont ($root)/boot/grub/font.pf2; then
|
||||
set gfxmode=auto
|
||||
insmod gfxterm
|
||||
insmod png
|
||||
@ -2513,26 +2530,25 @@ if loadfont /boot/grub/font.pf2; then
|
||||
fi
|
||||
|
||||
# Archipelago GRUB theme
|
||||
if [ -f /boot/grub/themes/archipelago/theme.txt ]; then
|
||||
# Load theme fonts
|
||||
loadfont /boot/grub/themes/archipelago/dejavu_12.pf2
|
||||
loadfont /boot/grub/themes/archipelago/dejavu_14.pf2
|
||||
loadfont /boot/grub/themes/archipelago/dejavu_16.pf2
|
||||
loadfont /boot/grub/themes/archipelago/dejavu_24.pf2
|
||||
set theme=/boot/grub/themes/archipelago/theme.txt
|
||||
if [ -f ($root)/boot/grub/themes/archipelago/theme.txt ]; then
|
||||
loadfont ($root)/boot/grub/themes/archipelago/dejavu_12.pf2
|
||||
loadfont ($root)/boot/grub/themes/archipelago/dejavu_14.pf2
|
||||
loadfont ($root)/boot/grub/themes/archipelago/dejavu_16.pf2
|
||||
loadfont ($root)/boot/grub/themes/archipelago/dejavu_24.pf2
|
||||
set theme=($root)/boot/grub/themes/archipelago/theme.txt
|
||||
else
|
||||
set menu_color_normal=light-gray/black
|
||||
set menu_color_highlight=white/dark-gray
|
||||
fi
|
||||
|
||||
menuentry "Install Archipelago" --hotkey=i {
|
||||
linux /live/vmlinuz boot=live components quiet console=ttyS0,115200 console=tty0
|
||||
initrd /live/initrd.img
|
||||
linux ($root)/live/vmlinuz boot=live components quiet console=ttyS0,115200 console=tty0
|
||||
initrd ($root)/live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "Install Archipelago (verbose)" --hotkey=v {
|
||||
linux /live/vmlinuz boot=live components
|
||||
initrd /live/initrd.img
|
||||
linux ($root)/live/vmlinuz boot=live components console=ttyS0,115200 console=tty0
|
||||
initrd ($root)/live/initrd.img
|
||||
}
|
||||
|
||||
menuentry "Boot from local disk" --hotkey=b {
|
||||
@ -2541,6 +2557,14 @@ menuentry "Boot from local disk" --hotkey=b {
|
||||
}
|
||||
GRUBCFG
|
||||
|
||||
# Copy grub.cfg to EFI/BOOT on ISO filesystem AND into the FAT EFI image
|
||||
# The embedded grub bootstrap does configfile "${cmdpath}/grub.cfg"
|
||||
cp "$INSTALLER_ISO/boot/grub/grub.cfg" "$INSTALLER_ISO/EFI/BOOT/grub.cfg"
|
||||
if [ -f "$WORK_DIR/efi.img" ]; then
|
||||
mcopy -oi "$WORK_DIR/efi.img" "$INSTALLER_ISO/boot/grub/grub.cfg" ::/EFI/BOOT/grub.cfg 2>/dev/null || \
|
||||
echo " WARNING: Could not copy grub.cfg into efi.img (mtools required)"
|
||||
fi
|
||||
|
||||
# Create ISOLINUX configuration (legacy BIOS boot)
|
||||
echo " Writing ISOLINUX config..."
|
||||
cat > "$INSTALLER_ISO/isolinux/isolinux.cfg" <<'ISOCFG'
|
||||
@ -2607,35 +2631,44 @@ if [ ! -f "$ISOHDPFX" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
# EFI boot image was built in Step 2
|
||||
EFI_IMG="$INSTALLER_ISO/boot/grub/efi.img"
|
||||
# EFI boot image was built in Step 2 and placed at staging/efiboot.img
|
||||
# The Will Haley / Debian live-build approach: append EFI as GPT partition 2
|
||||
# This is what makes USB boot work on real UEFI hardware (not just QEMU)
|
||||
EFIBOOT="$WORK_DIR/efi.img"
|
||||
|
||||
if [ ! -f "$EFI_IMG" ]; then
|
||||
if [ ! -f "$EFIBOOT" ]; then
|
||||
echo " WARNING: No EFI boot image — ISO will only support Legacy BIOS boot"
|
||||
xorriso -as mkisofs -o "$OUTPUT_ISO" \
|
||||
-volid "ARCHIPELAGO" \
|
||||
-iso-level 3 \
|
||||
-J -joliet-long -R \
|
||||
-full-iso9660-filenames \
|
||||
--mbr-force-bootable -partition_offset 16 \
|
||||
-joliet -joliet-long -rational-rock \
|
||||
-isohybrid-mbr "$ISOHDPFX" \
|
||||
-c isolinux/boot.cat \
|
||||
-b isolinux/isolinux.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
-partition_offset 16 \
|
||||
-eltorito-boot isolinux/isolinux.bin \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--eltorito-catalog isolinux/isolinux.cat \
|
||||
"$INSTALLER_ISO"
|
||||
else
|
||||
xorriso -as mkisofs -o "$OUTPUT_ISO" \
|
||||
-volid "ARCHIPELAGO" \
|
||||
-iso-level 3 \
|
||||
-J -joliet-long -R \
|
||||
-full-iso9660-filenames \
|
||||
--mbr-force-bootable -partition_offset 16 \
|
||||
-joliet -joliet-long -rational-rock \
|
||||
-isohybrid-mbr "$ISOHDPFX" \
|
||||
-c isolinux/boot.cat \
|
||||
-b isolinux/isolinux.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
-eltorito-boot isolinux/isolinux.bin \
|
||||
-no-emul-boot \
|
||||
-boot-load-size 4 \
|
||||
-boot-info-table \
|
||||
--eltorito-catalog isolinux/isolinux.cat \
|
||||
-eltorito-alt-boot \
|
||||
-e boot/grub/efi.img \
|
||||
-e --interval:appended_partition_2:all:: \
|
||||
-no-emul-boot \
|
||||
-isohybrid-gpt-basdat \
|
||||
-partition_offset 16 \
|
||||
-append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B "$EFIBOOT" \
|
||||
"$INSTALLER_ISO"
|
||||
fi
|
||||
|
||||
|
||||
@ -1,173 +1,207 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Quick-iterate on boot branding without rebuilding the ISO.
|
||||
# Boot branding dev — iterate on GRUB theme, Plymouth, and installer visuals
|
||||
# without rebuilding the ISO. Patches an existing ISO and boots in QEMU.
|
||||
#
|
||||
# Usage:
|
||||
# ./dev-branding.sh <base-iso>
|
||||
# ./dev-branding.sh [path-to-iso]
|
||||
#
|
||||
# What it does:
|
||||
# 1. Regenerates GRUB background and Plymouth logo from Python scripts
|
||||
# 2. Extracts the existing ISO
|
||||
# 3. Swaps in updated branding files (theme, background, Plymouth)
|
||||
# 4. Repackages as a new ISO
|
||||
# 5. Boots it in QEMU for testing
|
||||
#
|
||||
# This takes ~10 seconds instead of 20 minutes.
|
||||
#
|
||||
# For design-only iteration (no QEMU boot):
|
||||
# python3 branding/generate-grub-background.py /tmp/grub-bg.png && open /tmp/grub-bg.png
|
||||
# If no ISO is found locally, downloads the latest from the build server.
|
||||
# Edit files in branding/, re-run, see changes in ~10 seconds.
|
||||
#
|
||||
|
||||
set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ISO="${1:-}"
|
||||
|
||||
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
||||
# Auto-detect latest dev ISO on Desktop
|
||||
ISO=$(ls -t ~/Desktop/archipelago-dev-*.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 "No ISO found. Provide a path or place one on Desktop/results."
|
||||
echo "Usage: $0 <base-iso>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
WORK="/tmp/archipelago-dev-branding"
|
||||
PATCHED="$SCRIPT_DIR/results/archipelago-dev-patched.iso"
|
||||
CACHED_ISO="$SCRIPT_DIR/results/archipelago-dev-base.iso"
|
||||
DEV_SERVER="archipelago@192.168.1.228"
|
||||
SSH_KEY="$HOME/.ssh/archipelago-deploy"
|
||||
|
||||
echo "=== Archipelago Branding Dev ==="
|
||||
echo " Base ISO: $ISO"
|
||||
echo ""
|
||||
echo " Archipelago Boot Branding Dev"
|
||||
echo ""
|
||||
|
||||
# Step 1: Regenerate assets
|
||||
echo "[1/4] Generating assets..."
|
||||
python3 "$SCRIPT_DIR/branding/generate-grub-background.py" /tmp/grub-bg.png 2>/dev/null && \
|
||||
echo " GRUB background: OK" || echo " GRUB background: FAILED"
|
||||
python3 "$SCRIPT_DIR/branding/generate-plymouth-logo.py" /tmp/plymouth-logo.png 2>/dev/null && \
|
||||
echo " Plymouth logo: OK" || echo " Plymouth logo: FAILED"
|
||||
# --- Find or download an ISO ---
|
||||
ISO="${1:-}"
|
||||
|
||||
# Also show the background for quick visual check
|
||||
if command -v open >/dev/null 2>&1; then
|
||||
open /tmp/grub-bg.png 2>/dev/null &
|
||||
# Search locally
|
||||
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
||||
for pattern in \
|
||||
"$HOME/Desktop/archipelago-dev-"*.iso \
|
||||
"$HOME/Desktop/archipelago-unbundled-"*.iso \
|
||||
"$HOME/Desktop/archipelago-"*.iso \
|
||||
"$SCRIPT_DIR/results/archipelago-dev-base.iso" \
|
||||
"$SCRIPT_DIR/results/archipelago-"*.iso; do
|
||||
found=$(ls -t $pattern 2>/dev/null | head -1)
|
||||
if [ -n "$found" ] && [ -f "$found" ]; then
|
||||
ISO="$found"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Step 2: Extract ISO
|
||||
echo "[2/4] Extracting ISO..."
|
||||
# Download from server if not found
|
||||
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
||||
echo " No ISO found locally. Downloading latest from build server..."
|
||||
REMOTE_ISO=$(ssh -i "$SSH_KEY" "$DEV_SERVER" \
|
||||
"ls -t /var/lib/archipelago/filebrowser/Builds/archipelago-dev-*.iso 2>/dev/null | head -1" 2>/dev/null)
|
||||
if [ -z "$REMOTE_ISO" ]; then
|
||||
REMOTE_ISO=$(ssh -i "$SSH_KEY" "$DEV_SERVER" \
|
||||
"ls -t /var/lib/archipelago/filebrowser/Builds/archipelago-unbundled-*.iso 2>/dev/null | head -1" 2>/dev/null)
|
||||
fi
|
||||
if [ -n "$REMOTE_ISO" ]; then
|
||||
mkdir -p "$SCRIPT_DIR/results"
|
||||
echo " Downloading: $(basename "$REMOTE_ISO")..."
|
||||
scp -i "$SSH_KEY" "$DEV_SERVER:$REMOTE_ISO" "$CACHED_ISO"
|
||||
ISO="$CACHED_ISO"
|
||||
echo " Saved to: $ISO"
|
||||
else
|
||||
echo " No ISO on server either. Run a CI build first."
|
||||
echo " Or place an ISO on your Desktop."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " Base ISO: $(basename "$ISO") ($(du -h "$ISO" | cut -f1))"
|
||||
echo ""
|
||||
|
||||
# --- Extract ISO ---
|
||||
echo " [1/3] Extracting ISO..."
|
||||
if [ -d "$WORK" ]; then
|
||||
chmod -R u+w "$WORK" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK"
|
||||
|
||||
xorriso -osirrox on -indev "$ISO" -extract / "$WORK" 2>/dev/null || {
|
||||
# Fallback: mount + copy
|
||||
echo " xorriso extraction failed, trying hdiutil..."
|
||||
MNT=$(mktemp -d)
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
hdiutil attach "$ISO" -mountpoint "$MNT" -readonly -nobrowse 2>/dev/null
|
||||
else
|
||||
sudo mount -o loop,ro "$ISO" "$MNT"
|
||||
fi
|
||||
hdiutil attach "$ISO" -mountpoint "$MNT" -readonly -nobrowse 2>/dev/null || {
|
||||
echo " Could not mount ISO. Is it corrupt?"
|
||||
exit 1
|
||||
}
|
||||
cp -a "$MNT"/* "$WORK/" 2>/dev/null || true
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
hdiutil detach "$MNT" 2>/dev/null
|
||||
else
|
||||
sudo umount "$MNT"
|
||||
fi
|
||||
hdiutil detach "$MNT" 2>/dev/null || true
|
||||
rmdir "$MNT" 2>/dev/null || true
|
||||
}
|
||||
# Ensure files are writable after extraction
|
||||
chmod -R u+w "$WORK" 2>/dev/null || true
|
||||
|
||||
# Step 3: Patch branding
|
||||
echo "[3/4] Patching branding..."
|
||||
# --- Patch branding ---
|
||||
echo " [2/3] Patching branding..."
|
||||
THEME_DST="$WORK/boot/grub/themes/archipelago"
|
||||
mkdir -p "$THEME_DST"
|
||||
|
||||
# GRUB theme
|
||||
cp "$SCRIPT_DIR/branding/grub-theme/theme.txt" "$THEME_DST/" 2>/dev/null && \
|
||||
echo " theme.txt: OK"
|
||||
cp /tmp/grub-bg.png "$THEME_DST/background.png" 2>/dev/null && \
|
||||
echo " background.png: OK"
|
||||
# GRUB theme.txt
|
||||
if [ -f "$SCRIPT_DIR/branding/grub-theme/theme.txt" ]; then
|
||||
cp "$SCRIPT_DIR/branding/grub-theme/theme.txt" "$THEME_DST/"
|
||||
echo " theme.txt"
|
||||
fi
|
||||
|
||||
# GRUB background — use static file from branding dir
|
||||
if [ -f "$SCRIPT_DIR/branding/grub-theme/background.png" ]; then
|
||||
cp "$SCRIPT_DIR/branding/grub-theme/background.png" "$THEME_DST/background.png"
|
||||
echo " background.png (static)"
|
||||
elif [ -f "$SCRIPT_DIR/branding/generate-grub-background.py" ]; then
|
||||
python3 "$SCRIPT_DIR/branding/generate-grub-background.py" "$THEME_DST/background.png" 2>/dev/null
|
||||
echo " background.png (generated)"
|
||||
fi
|
||||
|
||||
# Plymouth theme
|
||||
if [ -d "$WORK/archipelago/plymouth-theme" ]; then
|
||||
cp "$SCRIPT_DIR/branding/plymouth-theme/"* "$WORK/archipelago/plymouth-theme/" 2>/dev/null
|
||||
cp /tmp/plymouth-logo.png "$WORK/archipelago/plymouth-theme/logo.png" 2>/dev/null
|
||||
echo " Plymouth theme: OK"
|
||||
PLYMOUTH_DST="$WORK/archipelago/plymouth-theme"
|
||||
mkdir -p "$PLYMOUTH_DST"
|
||||
if [ -d "$SCRIPT_DIR/branding/plymouth-theme" ]; then
|
||||
cp "$SCRIPT_DIR/branding/plymouth-theme/"* "$PLYMOUTH_DST/" 2>/dev/null || true
|
||||
echo " plymouth theme"
|
||||
fi
|
||||
|
||||
# GRUB config (in case you edited it)
|
||||
if [ -f "$SCRIPT_DIR/branding/grub.cfg" ]; then
|
||||
cp "$SCRIPT_DIR/branding/grub.cfg" "$WORK/boot/grub/grub.cfg"
|
||||
echo " grub.cfg: OK (custom)"
|
||||
fi
|
||||
|
||||
# ISOLINUX config
|
||||
if [ -f "$SCRIPT_DIR/branding/isolinux.cfg" ]; then
|
||||
cp "$SCRIPT_DIR/branding/isolinux.cfg" "$WORK/isolinux/isolinux.cfg"
|
||||
echo " isolinux.cfg: OK (custom)"
|
||||
fi
|
||||
|
||||
# Step 4: Repackage ISO
|
||||
echo "[4/4] Repackaging ISO..."
|
||||
# --- Repackage ISO ---
|
||||
echo " [3/3] Repackaging ISO..."
|
||||
mkdir -p "$SCRIPT_DIR/results"
|
||||
|
||||
# Find isohdpfx.bin
|
||||
# Find isohdpfx.bin — project copy first, then system
|
||||
ISOHDPFX=""
|
||||
for p in "$WORK/isolinux/isohdpfx.bin" \
|
||||
for p in "$SCRIPT_DIR/branding/isohdpfx.bin" \
|
||||
"$WORK/isolinux/isohdpfx.bin" \
|
||||
/usr/lib/ISOLINUX/isohdpfx.bin \
|
||||
/usr/share/syslinux/isohdpfx.bin \
|
||||
/opt/homebrew/share/syslinux/isohdpfx.bin; do
|
||||
[ -f "$p" ] && ISOHDPFX="$p" && break
|
||||
done
|
||||
|
||||
# Check for EFI image
|
||||
EFI_IMG="$WORK/boot/grub/efi.img"
|
||||
if [ -z "$ISOHDPFX" ]; then
|
||||
echo " ERROR: No isohdpfx.bin found. Cannot create bootable ISO."
|
||||
echo " Preview only — open the background:"
|
||||
open "$THEME_DST/background.png" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$ISOHDPFX" ] && [ -f "$EFI_IMG" ]; then
|
||||
EFI_IMG="$WORK/boot/grub/efi.img"
|
||||
if [ -f "$EFI_IMG" ]; then
|
||||
xorriso -as mkisofs -o "$PATCHED" \
|
||||
-volid "ARCHIPELAGO" \
|
||||
-iso-level 3 \
|
||||
-J -joliet-long -R \
|
||||
-iso-level 3 -J -joliet-long -R \
|
||||
-isohybrid-mbr "$ISOHDPFX" \
|
||||
-c isolinux/boot.cat \
|
||||
-b isolinux/isolinux.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
-eltorito-alt-boot \
|
||||
-e boot/grub/efi.img \
|
||||
-no-emul-boot \
|
||||
-isohybrid-gpt-basdat \
|
||||
-no-emul-boot -isohybrid-gpt-basdat \
|
||||
-partition_offset 16 \
|
||||
"$WORK" 2>/dev/null
|
||||
elif [ -n "$ISOHDPFX" ]; then
|
||||
else
|
||||
xorriso -as mkisofs -o "$PATCHED" \
|
||||
-volid "ARCHIPELAGO" \
|
||||
-iso-level 3 \
|
||||
-J -joliet-long -R \
|
||||
-iso-level 3 -J -joliet-long -R \
|
||||
-isohybrid-mbr "$ISOHDPFX" \
|
||||
-c isolinux/boot.cat \
|
||||
-b isolinux/isolinux.bin \
|
||||
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||
-partition_offset 16 \
|
||||
"$WORK" 2>/dev/null
|
||||
else
|
||||
echo "Cannot repackage: no isohdpfx.bin found."
|
||||
echo "Install xorriso and isolinux: brew install xorriso"
|
||||
echo ""
|
||||
echo "You can still preview the assets:"
|
||||
echo " open /tmp/grub-bg.png"
|
||||
echo " open /tmp/plymouth-logo.png"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Patched: $PATCHED ($(du -h "$PATCHED" | cut -f1))"
|
||||
echo ""
|
||||
|
||||
# --- Boot in QEMU ---
|
||||
if ! command -v qemu-system-x86_64 >/dev/null 2>&1; then
|
||||
echo " QEMU not found. Install: brew install qemu"
|
||||
echo " Opening background preview instead..."
|
||||
open "$THEME_DST/background.png" 2>/dev/null || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " Patched ISO: $PATCHED ($(du -h "$PATCHED" | cut -f1))"
|
||||
echo " Booting in QEMU (BIOS mode — shows ISOLINUX menu)..."
|
||||
echo " Press Ctrl+C to stop."
|
||||
echo ""
|
||||
|
||||
# Auto-boot in QEMU if available
|
||||
if command -v qemu-system-x86_64 >/dev/null 2>&1; then
|
||||
read -p "Boot in QEMU? [Y/n] " -n 1 -r
|
||||
echo ""
|
||||
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
||||
exec "$SCRIPT_DIR/test-iso-qemu.sh" "$PATCHED" --bios
|
||||
fi
|
||||
else
|
||||
echo "Install QEMU to test: brew install qemu"
|
||||
# Create test disk (use separate disk from other QEMU instances)
|
||||
DISK="/tmp/archipelago-branding-test.qcow2"
|
||||
# Kill any leftover QEMU from previous branding test
|
||||
pkill -f "archipelago-branding-test" 2>/dev/null || true
|
||||
sleep 1
|
||||
if [ ! -f "$DISK" ]; then
|
||||
qemu-img create -f qcow2 "$DISK" 20G 2>/dev/null
|
||||
fi
|
||||
|
||||
# Boot with BIOS to see the ISOLINUX/GRUB menu
|
||||
qemu-system-x86_64 \
|
||||
-machine pc \
|
||||
-m 4G \
|
||||
-smp 2 \
|
||||
-boot d \
|
||||
-cdrom "$PATCHED" \
|
||||
-drive if=virtio,format=qcow2,file="$DISK" \
|
||||
-net nic,model=virtio -net user,hostfwd=tcp::2222-:22,hostfwd=tcp::8100-:80 \
|
||||
-vga virtio \
|
||||
-display default \
|
||||
-serial file:/tmp/archipelago-qemu-serial.log
|
||||
|
||||
echo ""
|
||||
echo " QEMU stopped. Serial log: /tmp/archipelago-qemu-serial.log"
|
||||
echo " Re-run to test again after editing branding files."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user