#!/bin/sh # Сборка образа ISO Bridge&Join на основе официального Debian 13 netinst. # # deploy/iso/build-iso.sh <версия> [канал] [выходной.iso] [каталог релиза] # Каталог релиза (dist с manifest.json) — ядро bj кладётся в образ: установка без ключа продукта, ключ вводится в браузере. # # В образ добавляются: файл ответов установщику (preseed.cfg), bj-installer и служба первой загрузки. # Загрузочное меню BIOS (isolinux) и UEFI (grub) по умолчанию запускает установку с этими ответами. # Загрузочные записи исходного образа сохраняются (xorriso -boot_image any replay). # Нужен xorriso. Проверка исходного образа — по SHA256SUMS с cdimage.debian.org. set -eu SRC=${1:?исходный debian-13.x-amd64-netinst.iso} INSTALLER=${2:?файл bj-installer} VERSION=${3:?версия образа, например 2026.10.1} CHANNEL=${4:-stable} OUT=${5:-bj-debian13-$VERSION.iso} BUNDLE=${6:-} HERE=$(cd "$(dirname "$0")" && pwd) command -v xorriso >/dev/null || { echo "нужен xorriso: apt-get install xorriso"; exit 1; } WORK=$(mktemp -d) trap 'rm -rf "$WORK"' EXIT mkdir -p "$WORK/bj" "$WORK/preseed" "$WORK/isolinux" "$WORK/grub" cp "$HERE/preseed.cfg" "$WORK/preseed/bj.cfg" install -m 0755 "$INSTALLER" "$WORK/bj/bj-installer" install -m 0755 "$HERE/bj-firstboot" "$WORK/bj/bj-firstboot" cp "$HERE/bj-firstboot.service" "$WORK/bj/bj-firstboot.service" echo "$CHANNEL" > "$WORK/bj/channel" echo "$VERSION" > "$WORK/bj/version" # приветственный экран консоли (адрес веб-входа) — ставится ещё до установки bj install -m 0755 "$HERE/../linux/sbin/bj-welcome" "$WORK/bj/bj-welcome" cp "$HERE/../systemd/bj-welcome.service" "$HERE/../systemd/bj-welcome.timer" "$WORK/bj/" # ядро bj с образа: подписанный манифест и файлы компонентов ядра (bj-server, bj-component-install) if [ -n "$BUNDLE" ]; then [ -s "$BUNDLE/manifest.json" ] || { echo "в $BUNDLE нет manifest.json"; exit 1; } mkdir -p "$WORK/bj/bundle"; cp "$BUNDLE/manifest.json" "$WORK/bj/bundle/" # манифест подписан: тело — base64 в поле payload FILES=$(python3 -c 'import json,sys,base64;s=json.load(open(sys.argv[1]));m=json.loads(base64.b64decode(s["payload"]));print(" ".join(a["file"] for a in m["artifacts"] if a["name"] in ("bj-server","bj-component-install")))' "$BUNDLE/manifest.json") || { echo "не удалось прочитать манифест релиза"; exit 1; } [ -n "$FILES" ] || { echo "в релизе нет компонентов ядра (bj-server, bj-component-install)"; exit 1; } for f in $FILES; do cp "$BUNDLE/$f" "$WORK/bj/bundle/" || exit 1 done echo "в образ добавлено ядро bj: $(ls "$WORK/bj/bundle" | tr '\n' ' ')" fi BOOT="auto=true priority=high preseed/file=/cdrom/preseed/bj.cfg language=ru country=RU locale=ru_RU.UTF-8 keyboard-configuration/xkb-keymap=ru" # BIOS: пункт по умолчанию в isolinux xorriso -osirrox on -indev "$SRC" -extract /isolinux/txt.cfg "$WORK/isolinux/txt.orig" >/dev/null 2>&1 KERNEL=$(awk '/^\s*kernel/{print $2; exit}' "$WORK/isolinux/txt.orig") INITRD=$(grep -o 'initrd=[^ ]*' "$WORK/isolinux/txt.orig" | head -1 | cut -d= -f2) { echo "default bj" # Подпись — латиницей: vesamenu (BIOS) кириллицу не отображает (кракозябры). echo "label bj" echo " menu label ^Install Bridge&Join $VERSION" echo " menu default" echo " kernel $KERNEL" echo " append vga=788 initrd=$INITRD $BOOT --- quiet" sed '/menu default/d;/^default /d' "$WORK/isolinux/txt.orig" } > "$WORK/isolinux/txt.cfg" xorriso -osirrox on -indev "$SRC" -extract /isolinux/isolinux.cfg "$WORK/isolinux/isolinux.cfg" >/dev/null 2>&1 sed -i 's/^timeout .*/timeout 100/; s/^prompt .*/prompt 0/' "$WORK/isolinux/isolinux.cfg" grep -q '^timeout' "$WORK/isolinux/isolinux.cfg" || echo "timeout 100" >> "$WORK/isolinux/isolinux.cfg" # Подсказка обратного отсчёта — в конец isolinux.cfg, ПОСЛЕ include menu.cfg: внутри # menu.cfg подменю «Advanced» повторно включает stdmenu.cfg и перебивает autoboot из txt.cfg. # (файл, извлечённый xorriso, имеет права 0444: sed -i пересоздаёт его, а >> без chmod не запишет) chmod u+w "$WORK/isolinux/isolinux.cfg" echo "menu autoboot Bridge&Join install starts in # seconds, press a key to choose" >> "$WORK/isolinux/isolinux.cfg" # UEFI: первый пункт в grub xorriso -osirrox on -indev "$SRC" -extract /boot/grub/grub.cfg "$WORK/grub/grub.orig" >/dev/null 2>&1 # текстовый установщик (без gtk), как в BIOS: установка идёт без графической среды LINUX=$(awk '/^\s*linux\s/ && $2 !~ /gtk/ {print $2; exit}' "$WORK/grub/grub.orig") INITRDG=$(awk '/^\s*initrd\s/ && $2 !~ /gtk/ {print $2; exit}' "$WORK/grub/grub.orig") { echo "set default=0" echo "set timeout=10" # латиницей (шрифт grub-темы без кириллицы), горячая клавиша b — у родного «Install» занята i echo "menuentry --hotkey=b 'Install Bridge&Join $VERSION' {" echo " set background_color=black" echo " linux $LINUX vga=788 $BOOT --- quiet" echo " initrd $INITRDG" echo "}" sed '/^set default/d;/^set timeout/d' "$WORK/grub/grub.orig" } > "$WORK/grub/grub.cfg" rm -f "$OUT" xorriso -indev "$SRC" -outdev "$OUT" -boot_image any replay \ -volid "BJ_DEBIAN13" \ -map "$WORK/preseed" /preseed \ -map "$WORK/bj" /bj \ -map "$WORK/isolinux/txt.cfg" /isolinux/txt.cfg \ -map "$WORK/isolinux/isolinux.cfg" /isolinux/isolinux.cfg \ -map "$WORK/grub/grub.cfg" /boot/grub/grub.cfg \ -end >/dev/null 2>"$WORK/xorriso.log" || { cat "$WORK/xorriso.log"; exit 1; } echo "Образ собран: $OUT ($(du -h "$OUT" | cut -f1)), канал обновлений $CHANNEL"