#!/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 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