Add ISO build tooling and serial console support
- Add bin/yino-iso with download, build, and clean subcommands for creating a preseeded Debian installer ISO - Add --serial flag to bin/yino-vm boot for headless serial console - Add offline installation ISO spec to FSD (section 6) - Update README TODO to reflect ISO pipeline progress Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5fa89444d7
commit
17fa52824c
@ -10,8 +10,8 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [x] Set up development environment (QEMU VM for testing)
|
- [x] Set up development environment (QEMU VM for testing)
|
||||||
- [ ] Create ISO generation pipeline (Debian ISO with unattended install that triggers Yino installer)
|
- [ ] Create ISO generation pipeline (Debian ISO with unattended install). See item "6. Offline installation ISO" in
|
||||||
- [ ] Configure boot and full disk encryption (LUKS, initramfs, bootloader) as part of unattended install
|
`docs/yino-fsd.md` for full specification.
|
||||||
- [ ] Port Omarchy installer to Debian (runs post-install, equivalent of Omarchy's phase 2-5 scripts)
|
- [ ] Port Omarchy installer to Debian (runs post-install, equivalent of Omarchy's phase 2-5 scripts)
|
||||||
- [ ] Get Hyprland running via the installer with essential dotfiles
|
- [ ] Get Hyprland running via the installer with essential dotfiles
|
||||||
|
|
||||||
|
|||||||
201
bin/yino-iso
Executable file
201
bin/yino-iso
Executable file
@ -0,0 +1,201 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
ISO_DIR="$REPO_DIR/iso"
|
||||||
|
DOWNLOAD_DIR="$REPO_DIR/download"
|
||||||
|
BASE_URL="https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd"
|
||||||
|
|
||||||
|
# --- Subcommands ---
|
||||||
|
|
||||||
|
cmd_download() {
|
||||||
|
mkdir -p "$DOWNLOAD_DIR"
|
||||||
|
|
||||||
|
echo "Fetching SHA256SUMS..."
|
||||||
|
local sums_file="$DOWNLOAD_DIR/SHA256SUMS"
|
||||||
|
curl -fsSL -o "$sums_file" "$BASE_URL/SHA256SUMS"
|
||||||
|
|
||||||
|
local iso_name expected_hash
|
||||||
|
iso_name=$(grep 'debian-.*-amd64-DVD-1\.iso' "$sums_file" | awk '{print $2}')
|
||||||
|
expected_hash=$(grep 'debian-.*-amd64-DVD-1\.iso' "$sums_file" | awk '{print $1}')
|
||||||
|
|
||||||
|
if [[ -z "$iso_name" || -z "$expected_hash" ]]; then
|
||||||
|
echo "Error: Could not find DVD-1 ISO in SHA256SUMS." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local iso_path="$DOWNLOAD_DIR/$iso_name"
|
||||||
|
|
||||||
|
if [[ -f "$iso_path" ]]; then
|
||||||
|
echo "ISO already exists, verifying checksum..."
|
||||||
|
local actual_hash
|
||||||
|
actual_hash=$(shasum -a 256 "$iso_path" | awk '{print $1}')
|
||||||
|
if [[ "$actual_hash" == "$expected_hash" ]]; then
|
||||||
|
echo "Checksum OK. Skipping download."
|
||||||
|
echo " $iso_path"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Checksum mismatch, re-downloading..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Downloading $iso_name..."
|
||||||
|
curl -C - -fL -o "$iso_path" "$BASE_URL/$iso_name"
|
||||||
|
|
||||||
|
echo "Verifying checksum..."
|
||||||
|
local actual_hash
|
||||||
|
actual_hash=$(shasum -a 256 "$iso_path" | awk '{print $1}')
|
||||||
|
if [[ "$actual_hash" != "$expected_hash" ]]; then
|
||||||
|
echo "Error: Checksum verification failed." >&2
|
||||||
|
echo " Expected: $expected_hash" >&2
|
||||||
|
echo " Got: $actual_hash" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checksum OK."
|
||||||
|
echo " $iso_path"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_build() {
|
||||||
|
if ! command -v xorriso &>/dev/null; then
|
||||||
|
echo "Error: xorriso not found." >&2
|
||||||
|
echo "Install it with: brew install xorriso" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find source ISO
|
||||||
|
local src_iso
|
||||||
|
src_iso=$(find "$DOWNLOAD_DIR" -maxdepth 1 -name 'debian-*-amd64-DVD-1.iso' -print -quit 2>/dev/null || true)
|
||||||
|
if [[ -z "$src_iso" || ! -f "$src_iso" ]]; then
|
||||||
|
echo "Error: No Debian DVD-1 ISO found in $DOWNLOAD_DIR" >&2
|
||||||
|
echo "Run 'yino-iso download' first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Source ISO: $src_iso"
|
||||||
|
|
||||||
|
# Create temp working directory (not local — trap needs access after function returns)
|
||||||
|
work_dir=$(mktemp -d "${TMPDIR:-/tmp}/yino-iso.XXXXXX")
|
||||||
|
trap 'rm -rf "$work_dir"' EXIT
|
||||||
|
|
||||||
|
local extract_dir="$work_dir/iso"
|
||||||
|
|
||||||
|
# Extract ISO
|
||||||
|
echo "Extracting ISO..."
|
||||||
|
xorriso -osirrox on -indev "$src_iso" -extract / "$extract_dir"
|
||||||
|
chmod -R u+w "$extract_dir"
|
||||||
|
|
||||||
|
# Inject preseed into initrd
|
||||||
|
echo "Injecting preseed into initrd..."
|
||||||
|
gunzip "$extract_dir/install.amd/initrd.gz"
|
||||||
|
(cd "$ISO_DIR" && echo preseed.cfg | cpio -o --format newc) >> "$extract_dir/install.amd/initrd"
|
||||||
|
gzip "$extract_dir/install.amd/initrd"
|
||||||
|
|
||||||
|
# Modify boot configs — add preseed boot parameters
|
||||||
|
local boot_params="auto=true priority=critical locale=en_US.UTF-8 keymap=us console=ttyS0,115200n8"
|
||||||
|
|
||||||
|
echo "Modifying boot configuration..."
|
||||||
|
|
||||||
|
# Insert preseed params before the "---" separator in boot entries
|
||||||
|
if [[ -f "$extract_dir/isolinux/txt.cfg" ]]; then
|
||||||
|
sed -i.bak "s|--- |${boot_params} --- |" "$extract_dir/isolinux/txt.cfg"
|
||||||
|
rm -f "$extract_dir/isolinux/txt.cfg.bak"
|
||||||
|
# Set text installer as default boot entry (prepend with proper newline)
|
||||||
|
{ echo "default install"; cat "$extract_dir/isolinux/txt.cfg"; } > "$extract_dir/isolinux/txt.cfg.tmp"
|
||||||
|
mv "$extract_dir/isolinux/txt.cfg.tmp" "$extract_dir/isolinux/txt.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Fix isolinux: enable serial console, disable graphical menu, set short timeout
|
||||||
|
if [[ -f "$extract_dir/isolinux/isolinux.cfg" ]]; then
|
||||||
|
# Prepend serial directive (BSD sed '1i' doesn't insert newlines reliably)
|
||||||
|
{ echo "serial 0 115200"; cat "$extract_dir/isolinux/isolinux.cfg"; } > "$extract_dir/isolinux/isolinux.cfg.tmp"
|
||||||
|
mv "$extract_dir/isolinux/isolinux.cfg.tmp" "$extract_dir/isolinux/isolinux.cfg"
|
||||||
|
sed -i.bak 's|^default vesamenu.c32|#default vesamenu.c32|' "$extract_dir/isolinux/isolinux.cfg"
|
||||||
|
rm -f "$extract_dir/isolinux/isolinux.cfg.bak"
|
||||||
|
sed -i.bak 's|^timeout .*|timeout 50|' "$extract_dir/isolinux/isolinux.cfg"
|
||||||
|
rm -f "$extract_dir/isolinux/isolinux.cfg.bak"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$extract_dir/boot/grub/grub.cfg" ]]; then
|
||||||
|
sed -i.bak "s|--- |${boot_params} --- |" "$extract_dir/boot/grub/grub.cfg"
|
||||||
|
rm -f "$extract_dir/boot/grub/grub.cfg.bak"
|
||||||
|
# Add serial console support to GRUB and select text "Install" entry (index 1)
|
||||||
|
sed -i.bak 's|terminal_output gfxterm|terminal_output gfxterm serial|' "$extract_dir/boot/grub/grub.cfg"
|
||||||
|
rm -f "$extract_dir/boot/grub/grub.cfg.bak"
|
||||||
|
{ printf 'serial --unit=0 --speed=115200\nterminal_input serial console\nterminal_output serial console\nset timeout=5\nset default=1\n'; cat "$extract_dir/boot/grub/grub.cfg"; } > "$extract_dir/boot/grub/grub.cfg.tmp"
|
||||||
|
mv "$extract_dir/boot/grub/grub.cfg.tmp" "$extract_dir/boot/grub/grub.cfg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract MBR template for hybrid boot
|
||||||
|
echo "Extracting MBR template..."
|
||||||
|
local mbr_template="$work_dir/isohdpfx.bin"
|
||||||
|
dd if="$src_iso" bs=1 count=432 of="$mbr_template" 2>/dev/null
|
||||||
|
|
||||||
|
# Regenerate checksums
|
||||||
|
echo "Regenerating checksums..."
|
||||||
|
(cd "$extract_dir" && find . -type f ! -name 'md5sum.txt' -exec md5sum {} + > md5sum.txt)
|
||||||
|
|
||||||
|
# Rebuild ISO
|
||||||
|
local output_iso="$DOWNLOAD_DIR/yino-$(date +%Y%m%d).iso"
|
||||||
|
echo "Building ISO..."
|
||||||
|
xorriso -as mkisofs \
|
||||||
|
-r -V "Yino" \
|
||||||
|
-o "$output_iso" \
|
||||||
|
-J -joliet-long \
|
||||||
|
-isohybrid-mbr "$mbr_template" \
|
||||||
|
-partition_offset 16 \
|
||||||
|
-b isolinux/isolinux.bin \
|
||||||
|
-c isolinux/boot.cat \
|
||||||
|
-no-emul-boot -boot-load-size 4 -boot-info-table \
|
||||||
|
-eltorito-alt-boot \
|
||||||
|
-e boot/grub/efi.img \
|
||||||
|
-no-emul-boot \
|
||||||
|
-isohybrid-gpt-basdat \
|
||||||
|
"$extract_dir"
|
||||||
|
|
||||||
|
echo "Done."
|
||||||
|
echo " $output_iso"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_clean() {
|
||||||
|
local yino_isos
|
||||||
|
yino_isos=$(find "$DOWNLOAD_DIR" -maxdepth 1 -name 'yino-*.iso' 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [[ -z "$yino_isos" ]]; then
|
||||||
|
echo "Nothing to clean."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Removing built ISOs:"
|
||||||
|
while read -r f; do
|
||||||
|
echo " $f"
|
||||||
|
rm "$f"
|
||||||
|
done <<< "$yino_isos"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_help() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: yino-iso <command>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
download Download Debian 13 DVD-1 ISO and verify checksum
|
||||||
|
build Build custom Yino ISO with preseed
|
||||||
|
clean Remove built ISOs
|
||||||
|
help Show this help
|
||||||
|
|
||||||
|
Dependencies (macOS):
|
||||||
|
brew install xorriso
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main ---
|
||||||
|
|
||||||
|
case "${1:-help}" in
|
||||||
|
download) cmd_download ;;
|
||||||
|
build) cmd_build ;;
|
||||||
|
clean) cmd_clean ;;
|
||||||
|
help|--help|-h) cmd_help ;;
|
||||||
|
*)
|
||||||
|
echo "Error: Unknown command: $1" >&2
|
||||||
|
cmd_help >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
23
bin/yino-vm
23
bin/yino-vm
@ -138,8 +138,13 @@ cmd_boot() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local iso_args=()
|
local iso_args=()
|
||||||
|
local serial=false
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--serial)
|
||||||
|
serial=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
--iso)
|
--iso)
|
||||||
if [[ -z "${2:-}" ]]; then
|
if [[ -z "${2:-}" ]]; then
|
||||||
echo "Error: --iso requires a path argument." >&2
|
echo "Error: --iso requires a path argument." >&2
|
||||||
@ -154,14 +159,23 @@ cmd_boot() {
|
|||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Error: Unknown option: $1" >&2
|
echo "Error: Unknown option: $1" >&2
|
||||||
echo "Usage: yino-vm boot [--iso PATH]" >&2
|
echo "Usage: yino-vm boot [--serial] [--iso PATH]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
local display_args
|
||||||
|
if [[ "$serial" == true ]]; then
|
||||||
|
display_args=(-serial mon:stdio -nographic -display none)
|
||||||
|
else
|
||||||
|
display_args=(-display cocoa -vga virtio)
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Booting VM (accel=$accel, cpu=$cpu)..."
|
echo "Booting VM (accel=$accel, cpu=$cpu)..."
|
||||||
echo "SSH available at: ssh -p 2222 localhost"
|
if [[ "$serial" != true ]]; then
|
||||||
|
echo "SSH available at: ssh -p 2222 localhost"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
qemu-system-x86_64 \
|
qemu-system-x86_64 \
|
||||||
@ -175,8 +189,7 @@ cmd_boot() {
|
|||||||
-drive "if=pflash,format=raw,file=$UEFI_VARS" \
|
-drive "if=pflash,format=raw,file=$UEFI_VARS" \
|
||||||
-device virtio-net-pci,netdev=net0 \
|
-device virtio-net-pci,netdev=net0 \
|
||||||
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
|
-netdev user,id=net0,hostfwd=tcp::2222-:22 \
|
||||||
-display cocoa \
|
"${display_args[@]}" \
|
||||||
-vga virtio \
|
|
||||||
"${iso_args[@]}"
|
"${iso_args[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +239,7 @@ Usage: yino-vm <command> [options]
|
|||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
create Create VM disk image and UEFI vars
|
create Create VM disk image and UEFI vars
|
||||||
boot [--iso P] Boot the VM (--iso to attach installer ISO)
|
boot [opts] Boot the VM (--serial for console, --iso P for installer)
|
||||||
delete Delete VM disk image and UEFI vars
|
delete Delete VM disk image and UEFI vars
|
||||||
status Show VM status
|
status Show VM status
|
||||||
help Show this help
|
help Show this help
|
||||||
|
|||||||
@ -50,7 +50,18 @@ This should match Omarchy's architecture (see [Omarchy Analysis](Omarchy.md) for
|
|||||||
- QEMU should be used for testing and demo virtual machines
|
- QEMU should be used for testing and demo virtual machines
|
||||||
- Keep a cache of downloaded assets (e.g. Debian installation ISO) in this repository's `download` directory
|
- Keep a cache of downloaded assets (e.g. Debian installation ISO) in this repository's `download` directory
|
||||||
|
|
||||||
## 6. Upstream tracking
|
## 6. Offline installation ISO
|
||||||
|
|
||||||
|
This project should generate the tooling necessary to create a modified, offline installation ISO.
|
||||||
|
It should do this by downloading and caching the Debian iso-dvd image, extract it and apply the necessary
|
||||||
|
modifications to enable an unattended installation with the following characteristics (similiar to
|
||||||
|
how archinstall doest it):
|
||||||
|
|
||||||
|
- LUKS + btrfs
|
||||||
|
- System for a single user, automatic login because full disk decryption already authenticates user
|
||||||
|
- Graphical Wayland environment, no X11
|
||||||
|
|
||||||
|
## 7. Upstream tracking
|
||||||
|
|
||||||
- Track the upstream [basecamp/omarchy](https://github.com/basecamp/omarchy) repository for new releases and changes
|
- Track the upstream [basecamp/omarchy](https://github.com/basecamp/omarchy) repository for new releases and changes
|
||||||
- Omarchy's `dev` branch is where active development happens; `master` is the stable branch
|
- Omarchy's `dev` branch is where active development happens; `master` is the stable branch
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user