Fix: image_deploy.sh Boot-Medium-Kopie nicht mit Phase-3-Aenderungen synchronisiert

Live in QEMU gefunden (19.09.2026, erster Testlauf der neuen Lokal-Test-ISO
mit custom-Partitionierung): "image_deploy_partition_custom: command not
found" - die Custom-Partitionierungsfunktionen (Commit 020aa66) wurden nur
in scripts/lib/image_deploy.sh ergaenzt, die zweite, tatsaechlich ins
Boot-Medium gepackte Kopie unter boot-medium/config/includes.chroot/...
blieb auf dem Phase-1/2-Stand stehen. Genau die Sync-Falle, die im Roadmap-
Dokument als Risiko benannt wurde.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWKevK2QUK5tsGmqfSD6Yy
This commit is contained in:
adminandClaude Sonnet 5 committed 2026-09-19 16:29:20 +02:00
1 parent 020aa66bfa
commit 25f6987948
1 file changed
+156
@@ -145,6 +145,145 @@ image_deploy_partition() {
fi
}
# Partitionierung fuer scheme:"custom" (Bereitstellungsvorlage.partitioning,
# siehe platform-docs/roadmap/e2e-testrunde-lokale-adressierung.md Phase 3):
# ESP/biosgrub wie image_deploy_partition(), danach je einen "extra
# partition"-Eintrag aus extra_partitions_json (kompaktes JSON-Array
# [{"mountpoint":"/home","percent":20,"filesystem":"ext4"}, ...]), Root
# nimmt den verbleibenden Rest. Prozentangaben sind bewusst Anteile der
# GESAMTEN Platte (nicht des nach ESP verbleibenden Rests) - "20% fuer
# /home" bedeutet 20% der Plattengroesse, parted interpretiert "X%" in
# mkpart ohnehin relativ zur Gesamtgroesse des Datentraegers, nicht zum
# vorherigen Partitionsende. Eigene Funktion statt Erweiterung von
# image_deploy_partition() - komplett andere Partitionsanzahl/-logik,
# und das bestehende (viel genutzte) single-Schema soll dadurch keinerlei
# Verhaltensaenderung/Risiko erfahren.
#
# Gibt ein JSON-Objekt auf stdout aus:
# {"boot_part": "...", "root_part": "...",
# "extra_partitions": [{"mountpoint": "/home", "device": "...", "filesystem": "ext4"}, ...]}
image_deploy_partition_custom() {
local disk="$1"
local is_efi="$2"
local extra_partitions_json="$3"
_image_deploy_release_disk "${disk}"
if ! parted --script "${disk}" mklabel gpt; then
image_deploy_log "parted meldete einen Fehler beim Anlegen der Partitionstabelle auf ${disk} (haeufig: eine alte Partition war noch belegt) - pruefe, ob der Kernel die neue Tabelle trotzdem uebernimmt."
fi
_image_deploy_settle_partitions "${disk}" ||
{ image_deploy_fatal "Kernel konnte die neue Partitionstabelle auf ${disk} auch nach mehreren Versuchen nicht uebernehmen."; return 1; }
parted --script "${disk}" print 2>/dev/null | grep -q "Partition Table: gpt" ||
{ image_deploy_fatal "Nach dem Partitionieren liegt kein gueltiges GPT-Label auf ${disk} vor."; return 1; }
local part_num=1 cursor boot_part=""
if [[ "${is_efi}" == "true" ]]; then
parted --script "${disk}" mkpart ESP fat32 1MiB 513MiB &&
parted --script "${disk}" set 1 esp on ||
{ image_deploy_fatal "ESP-Partitionierung fehlgeschlagen auf ${disk}"; return 1; }
boot_part="$(image_deploy_partition_path "${disk}" 1)"
cursor="513MiB"
else
parted --script "${disk}" mkpart biosgrub 1MiB 2MiB &&
parted --script "${disk}" set 1 bios_grub on ||
{ image_deploy_fatal "biosgrub-Partitionierung fehlgeschlagen auf ${disk}"; return 1; }
cursor="2MiB"
fi
local extra_partitions_result="[]"
local mountpoint percent filesystem name cumulative_percent=0 device entry
while IFS= read -r entry; do
[[ -n "${entry}" ]] || continue
part_num=$((part_num + 1))
mountpoint="$(jq --raw-output '.mountpoint' <<<"${entry}")"
percent="$(jq --raw-output '.percent' <<<"${entry}")"
filesystem="$(jq --raw-output '.filesystem' <<<"${entry}")"
# Partitionsname aus Mountpoint ableiten ("/home" -> "home",
# "/var/lib" -> "var-lib") - rein kosmetisch (parted-Label), hat
# keine funktionale Bedeutung.
name="$(sed -e 's#^/##' -e 's#/#-#g' <<<"${mountpoint}")"
cumulative_percent=$((cumulative_percent + percent))
parted --script "${disk}" mkpart "${name}" "${filesystem}" "${cursor}" "${cumulative_percent}%" ||
{ image_deploy_fatal "Partitionierung von ${mountpoint} (${percent}%) fehlgeschlagen auf ${disk}"; return 1; }
cursor="${cumulative_percent}%"
device="$(image_deploy_partition_path "${disk}" "${part_num}")"
extra_partitions_result="$(jq --compact-output \
--arg mountpoint "${mountpoint}" --arg device "${device}" --arg filesystem "${filesystem}" \
'. + [{mountpoint: $mountpoint, device: $device, filesystem: $filesystem}]' \
<<<"${extra_partitions_result}")"
done < <(jq --compact-output '.[]' <<<"${extra_partitions_json}")
part_num=$((part_num + 1))
parted --script "${disk}" mkpart root ext4 "${cursor}" 100% ||
{ image_deploy_fatal "Root-Partitionierung fehlgeschlagen auf ${disk}"; return 1; }
local root_part
root_part="$(image_deploy_partition_path "${disk}" "${part_num}")"
_image_deploy_settle_partitions "${disk}" ||
{ image_deploy_fatal "Kernel konnte die neuen Partitionen auf ${disk} nicht uebernehmen."; return 1; }
[[ -z "${boot_part}" || -b "${boot_part}" ]] && [[ -b "${root_part}" ]] ||
{ image_deploy_fatal "Partitionen nach partprobe nicht gefunden (${boot_part}, ${root_part})"; return 1; }
jq --null-input --compact-output \
--arg boot_part "${boot_part}" --arg root_part "${root_part}" \
--argjson extra_partitions "${extra_partitions_result}" \
'{boot_part: $boot_part, root_part: $root_part, extra_partitions: $extra_partitions}'
}
# Formatiert boot_part/root_part wie image_deploy_format(), zusaetzlich
# jede Extra-Partition mit ihrem eigenen Dateisystem (aus dem stdout-JSON
# von image_deploy_partition_custom()).
image_deploy_format_custom() {
local boot_part="$1"
local root_part="$2"
local root_fs="$3"
local partition_result="$4"
image_deploy_format "${boot_part}" "${root_part}" "${root_fs}" || return 1
local device filesystem entry
while IFS= read -r entry; do
[[ -n "${entry}" ]] || continue
device="$(jq --raw-output '.device' <<<"${entry}")"
filesystem="$(jq --raw-output '.filesystem' <<<"${entry}")"
case "${filesystem}" in
ext4) mkfs.ext4 -F -L "$(basename "${device}")" "${device}" ;;
btrfs) mkfs.btrfs -f -L "$(basename "${device}")" "${device}" ;;
*) image_deploy_fatal "Nicht unterstuetztes Dateisystem: ${filesystem}"; return 1 ;;
esac || { image_deploy_fatal "mkfs auf ${device} fehlgeschlagen"; return 1; }
done < <(jq --compact-output '.extra_partitions[]' <<<"${partition_result}")
}
# Mountet root/ESP wie image_deploy_mount(), zusaetzlich jede Extra-
# Partition unter ihrem Mountpoint innerhalb von target - VOR dem Golden-
# Image-Extract, damit das entpackte Archiv direkt auf den richtigen
# Partitionen landet statt hinterher umkopiert werden zu muessen.
image_deploy_mount_custom() {
local target="$1"
local boot_part="$2"
local root_part="$3"
local partition_result="$4"
image_deploy_mount "${target}" "${boot_part}" "${root_part}" || return 1
local device mountpoint entry
while IFS= read -r entry; do
[[ -n "${entry}" ]] || continue
device="$(jq --raw-output '.device' <<<"${entry}")"
mountpoint="$(jq --raw-output '.mountpoint' <<<"${entry}")"
install -d "${target}${mountpoint}" ||
{ image_deploy_fatal "Mountpunkt ${target}${mountpoint} konnte nicht angelegt werden"; return 1; }
mount "${device}" "${target}${mountpoint}" ||
{ image_deploy_fatal "${device} konnte nicht auf ${target}${mountpoint} gemountet werden"; return 1; }
done < <(jq --compact-output '.extra_partitions[]' <<<"${partition_result}")
}
image_deploy_format() {
local boot_part="$1"
local root_part="$2"
@@ -354,11 +493,16 @@ image_deploy_install_network_profile() {
{ image_deploy_fatal "Verbindungsprofil konnte nicht nach ${dest_dir} kopiert werden"; return 1; }
}
# extra_partitions_json (5. Parameter, optional): kompaktes JSON-Array wie
# von image_deploy_partition_custom() zurueckgegeben
# ([{"mountpoint":...,"device":...,"filesystem":...}, ...]) - Default "[]",
# damit bestehende Aufrufer (scheme:"single") unveraendert bleiben.
image_deploy_write_fstab() {
local target="$1"
local boot_part="$2"
local root_part="$3"
local root_fs="$4"
local extra_partitions_json="${5:-[]}"
local root_uuid
root_uuid="$(blkid -s UUID -o value "${root_part}")"
@@ -375,6 +519,18 @@ image_deploy_write_fstab() {
{ image_deploy_fatal "Keine UUID fuer ${boot_part} gefunden"; return 1; }
echo "UUID=${boot_uuid} /boot/efi vfat umask=0077 0 1"
fi
local device mountpoint filesystem uuid entry
while IFS= read -r entry; do
[[ -n "${entry}" ]] || continue
device="$(jq --raw-output '.device' <<<"${entry}")"
mountpoint="$(jq --raw-output '.mountpoint' <<<"${entry}")"
filesystem="$(jq --raw-output '.filesystem' <<<"${entry}")"
uuid="$(blkid -s UUID -o value "${device}")"
[[ -n "${uuid}" ]] ||
{ image_deploy_fatal "Keine UUID fuer ${device} gefunden"; return 1; }
echo "UUID=${uuid} ${mountpoint} ${filesystem} defaults 0 2"
done < <(jq --compact-output '.[]' <<<"${extra_partitions_json}")
} > "${target}/etc/fstab"
}