14303b2522
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4YVo4UMopqT5DzicqpPjL
98 lines
6.3 KiB
Bash
Executable File
98 lines
6.3 KiB
Bash
Executable File
#!/bin/sh
|
||
# Сборка образа ISO Bridge&Join на основе официального Debian 13 netinst.
|
||
#
|
||
# deploy/iso/build-iso.sh <debian-13.x-amd64-netinst.iso> <bj-installer> <версия> [канал] [выходной.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"
|