e577b48c6c
Signed-off-by: Stefan Knoblich <stkn@bitplumber.de>
271 lines
7.7 KiB
Bash
271 lines
7.7 KiB
Bash
#!/usr/bin/env /bin/bash
|
|
declare -r BASEDIR="`pwd`"
|
|
|
|
declare -r mode="full" # one of 'full'|'unattend'
|
|
|
|
declare -r ISO_BASEDIR="/mnt/machines/iso"
|
|
declare -r CONFIG_DIR="${BASEDIR}/configs"
|
|
|
|
declare -r UNATTEND_XML="${2:-"${CONFIG_DIR}/autounattend-builder-amd64-20260527.xml"}"
|
|
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/nano11-25H2-Pro-Eng-Arm64-20260513.iso"
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/tiny11-25H2-Pro-Eng-Arm64-20260514.iso"
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/nano11-25H2-English-Pro-2026-03-15.iso"
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/Tiny11-25H2-English-Pro-2026-05-20.iso"
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/Tiny11Core-25H2-English-Pro-2026-05-20.iso"
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/nano11-25H2-English-Pro-2026-05-20.iso"
|
|
|
|
#declare -r WINDOWS_ISO="${ISO_BASEDIR}/nano11-base-25H2-Pro-Eng-amd64-20260526.iso"
|
|
declare -r WINDOWS_ISO="${ISO_BASEDIR}/nano11-base-25H2-Pro-Eng-arm64-20260526.iso"
|
|
|
|
declare -r VIRTIO_ISO="${ISO_BASEDIR}/virtio-win-0.1.285.iso"
|
|
#declare -r VIRTIO_ISO="${BASEDIR}/virtio-win-0.1.285.iso"
|
|
|
|
|
|
######################################################################################
|
|
#
|
|
#
|
|
|
|
function detect_iso_arch {
|
|
local iso_file="${1}"
|
|
local fallback_arch="${2}"
|
|
case "${iso_file@L}" in
|
|
*-amd64-*|*-x64-*)
|
|
echo "amd64" ;;
|
|
*-arm64-*|*-aarch64-*)
|
|
echo "arm64" ;;
|
|
*) # Unknown use host arch as fallback
|
|
echo "NOTICE: Cannot detect architecture from ISO name, using fallback" >&2
|
|
echo "${fallback_arch}" ;;
|
|
esac
|
|
}
|
|
|
|
function detect_host_arch {
|
|
case "$(uname -m | tr '[:upper:]' '[:lower:]')" in
|
|
"x86_64") echo "amd64" ;;
|
|
"aarch64") echo "arm64" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
######################################################################################
|
|
# Detect host and guest architecture
|
|
declare -r HOST_ARCH="$(detect_host_arch)"
|
|
[[ -n "${HOST_ARCH}" ]] || {
|
|
echo "Failed to detect host arch" >&2;
|
|
exit 1;
|
|
}
|
|
|
|
declare -r TARGET_ARCH="$(detect_iso_arch "${WINDOWS_ISO}")"
|
|
[[ -n "${TARGET_ARCH}" ]] || {
|
|
echo "Failed to detect target arch" >&2;
|
|
exit 1;
|
|
}
|
|
|
|
case "${mode}" in
|
|
"full")
|
|
echo "Generating full ${TARGET_ARCH} installation ISO image..." >&2
|
|
declare -r DEFAULT_OUTPUT_FILE="install-${TARGET_ARCH}-$(date +%Y%m%d-%H%M00).iso"
|
|
;;
|
|
"unattend")
|
|
echo "Generating unattended ${TARGET_ARCH} ISO image..." >&2
|
|
declare -r DEFAULT_OUTPUT_FILE="unattend-${TARGET_ARCH}-$(date +%Y%m%d-%H%M00).iso"
|
|
;;
|
|
*)
|
|
echo "Invalid mode '${mode}' expected either 'full'|'unattend'" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
|
|
|
|
#
|
|
# Output file
|
|
#
|
|
declare -r OUTPUT_FILE="${1:-${DEFAULT_OUTPUT_FILE}}"
|
|
|
|
|
|
#
|
|
# Validate input XML
|
|
#
|
|
xmllint -noout "${UNATTEND_XML}" || {
|
|
echo "$(basename "${UNATTEND_XML}") contains invalid XML, aborting" >&2
|
|
exit 1
|
|
}
|
|
|
|
|
|
mkdir -p "${BASEDIR}/build" || exit $?
|
|
mkdir -p "${BASEDIR}/output" || exit $?
|
|
|
|
# Convert architecture for virtio installation media
|
|
case "${TARGET_ARCH}" in
|
|
"arm64")
|
|
VIRTIO_ARCH="${TARGET_ARCH@U}" ;; # Virtio ISO uses "ARM64"
|
|
*)
|
|
VIRTIO_ARCH="${TARGET_ARCH}" ;; # Virtio ISO uses "amd64"
|
|
esac
|
|
|
|
|
|
#
|
|
# Generate builder script
|
|
#
|
|
cat -> "${BASEDIR}/build/genimage-${TARGET_ARCH}.sh" <<-EOF
|
|
#!/usr/bin/env /bin/ash
|
|
|
|
#
|
|
# Required packages
|
|
#
|
|
echo "Installing required packages..." >&2
|
|
apk update || exit \$?
|
|
apk add xorriso 7zip || exit \$?
|
|
|
|
#
|
|
# Prepare directories / mountpoints
|
|
#
|
|
echo "Preparing build directory and mountpoints..." >&2
|
|
mkdir -p /tmp/base || exit \$?
|
|
mkdir -p /tmp/virtio || exit \$?
|
|
mkdir -p /tmp/build || exit \$?
|
|
|
|
#
|
|
# Mounting (if possible) / unpacking of ISO images
|
|
#
|
|
if [[ "x${mode}" == "xfull" ]]; then
|
|
echo "Attempting to mount windows ISO image..." >&2
|
|
mount -t udf -o loop,ro /input/windows.iso /tmp/base/ 2>/dev/null || {
|
|
echo "Unpacking windows ISO image (fallback)..." >&2
|
|
7z x -bb0 -o/tmp/base/ /input/windows.iso || exit \$?
|
|
}
|
|
else
|
|
echo "Building unattend ISO image, skipping windows mount" >&2
|
|
fi
|
|
|
|
echo "Attempting to mount virtio-win ISO image..." >&2
|
|
mount -o loop,ro /input/virtio.iso /tmp/virtio/ 2>/dev/null || {
|
|
echo "Unpacking virtio-win ISO image (fallback)..." >&2
|
|
7z x -bb0 -o/tmp/virtio/ /input/virtio.iso || exit \$?
|
|
}
|
|
|
|
#
|
|
# Autounattend XML configuration file (replaces any existing one)
|
|
#
|
|
echo "Copying autounattend.xml..." >&2
|
|
sed -e 's|processorArchitecture="[^"]\+"|processorArchitecture="${TARGET_ARCH}"|' \
|
|
< /input/autounattend.xml \
|
|
> /tmp/build/autounattend.xml || exit \$?
|
|
|
|
#
|
|
# Required drivers for installation (into "$WinPEDriver$")
|
|
#
|
|
echo "Creating \\\$WinPEDriver\\\$..." >&2
|
|
mkdir -p "/tmp/build/\\\$WinPEDriver\\\$" || exit \$?
|
|
for x in NetKVM viostor vioscsi; do
|
|
cp -vfR /tmp/virtio/\${x}/w11/${VIRTIO_ARCH} "/tmp/build/\\\$WinPEDriver\\\$/\${x}" || exit \$?
|
|
done
|
|
|
|
#
|
|
# OEM files (drivers, packages, etc.)
|
|
#
|
|
echo "Copying OEM files..." >&2
|
|
|
|
mkdir -p \
|
|
"/tmp/build/\\\$OEM\\\$/\\\$\\\$" \
|
|
"/tmp/build/\\\$OEM\\\$/\\\$1" \
|
|
|| exit \$?
|
|
|
|
# Viofs files for use in the final OS (virtiofsd)
|
|
mkdir -p "/tmp/build/\\\$OEM\\\$/\\\$1/drivers" || exit \$?
|
|
for x in /tmp/virtio/viofs/w11/${VIRTIO_ARCH}; do
|
|
drvname="\$(echo "\${x}" | cut -d'/' -f4)" || exit \$?
|
|
cp -vfR "\${x}" "/tmp/build/\\\$OEM\\\$/\\\$1/drivers/\${drvname}" || exit \$?
|
|
done
|
|
|
|
#
|
|
# Virtio drivers for auto-installation
|
|
#
|
|
echo "Copying virtio drivers for automatic installation..." >&2
|
|
|
|
mkdir -p "/tmp/build/drivers" || exit \$?
|
|
for x in /tmp/virtio/*/w11/${VIRTIO_ARCH}; do
|
|
drvname="\$(echo "\${x}" | cut -d'/' -f4)" || exit \$?
|
|
cp -vfR "\${x}" "/tmp/build/drivers/\${drvname}" || exit \$?
|
|
done
|
|
|
|
#
|
|
# ISO image generation
|
|
#
|
|
if [[ "x${mode}" == "xfull" ]]; then
|
|
echo "Generating ISO boot image..." >&2
|
|
mkisofs \\
|
|
-quiet \\
|
|
-b boot/etfsboot.com \\
|
|
-no-emul-boot \\
|
|
-boot-load-size 8 \\
|
|
-iso-level 3 -J -l -D -N -U -joliet-long -relaxed-filenames -rational-rock \\
|
|
-eltorito-alt-boot -e efi/microsoft/boot/efisys_noprompt.bin -no-emul-boot \\
|
|
-m "/tmp/base/autounattend.xml" \\
|
|
-V "Nano11-Builder" \\
|
|
-o "/output/$(basename "${OUTPUT_FILE}")" \\
|
|
"/tmp/base/" "/tmp/build/" || exit \$?
|
|
else
|
|
echo "Generating ISO unattend image..." >&2
|
|
mkisofs \\
|
|
-quiet \\
|
|
-iso-level 3 -J -l -D -N -U -joliet-long -relaxed-filenames -rational-rock \\
|
|
-V "Nano11-Builder-Unattend" \\
|
|
-o "/output/$(basename "${OUTPUT_FILE}")" \\
|
|
"/tmp/build/" || exit \$?
|
|
fi
|
|
|
|
# genisoimage \\
|
|
# -quiet \\
|
|
# -b boot/etfsboot.com \\
|
|
# -no-emul-boot \\
|
|
# -boot-load-seg 1984 \\
|
|
# -boot-load-size 8 \\
|
|
# -iso-level 3 -J -l -D -N -U -joliet-long -allow-limited-size -relaxed-filenames -rock \\
|
|
# -eltorito-alt-boot -e efi/microsoft/boot/efisys_noprompt.bin \\
|
|
# -m "/tmp/base/autounattend.xml" \\
|
|
# -V "Nano11-Builder" \\
|
|
# -o "/build/output.iso" \\
|
|
# "/tmp/base/" "/tmp/build/" || exit \$?
|
|
|
|
#
|
|
#
|
|
#
|
|
echo "Cleaning up..." >&2
|
|
umount /tmp/base/ 2>/dev/null
|
|
umount /tmp/virtio/ 2>/dev/null
|
|
EOF
|
|
|
|
chmod 0755 "${BASEDIR}/build/genimage-${TARGET_ARCH}.sh" || exit $?
|
|
|
|
declare -r CONTAINER_IMAGE="alpine:3.23"
|
|
declare -r CONTAINER_ARGS=(
|
|
"-it --rm --name isobuilder-${TARGET_ARCH}"
|
|
"-v ${VIRTIO_ISO}:/input/virtio.iso:ro,Z"
|
|
"-v ${WINDOWS_ISO}:/input/windows.iso:ro,Z"
|
|
"-v ${UNATTEND_XML}:/input/autounattend.xml:ro,Z"
|
|
"-v ${BASEDIR}/build:/build:ro,Z"
|
|
"-v ${BASEDIR}/output:/output:rw,Z"
|
|
"--privileged --network host"
|
|
)
|
|
|
|
#
|
|
# Run generation script in alpine 3.23 container
|
|
#
|
|
if type -p docker &>/dev/null; then
|
|
echo "Running isobuilder in docker container" >&2
|
|
docker run ${CONTAINER_ARGS[@]} ${CONTAINER_IMAGE} \
|
|
/build/genimage-${TARGET_ARCH}.sh || exit $?
|
|
elif type -p podman &>/dev/null; then
|
|
echo "Running isobuilder in podman container" >&2
|
|
podman run ${CONTAINER_ARGS[@]} ${CONTAINER_IMAGE} \
|
|
/build/genimage-${TARGET_ARCH}.sh || exit $?
|
|
else
|
|
echo "Failed to start build container: no runtime available" >&2
|
|
exit 1
|
|
fi
|
|
|
|
(cd "${BASEDIR}/output" && ls -sh "$(basename "${OUTPUT_FILE}")") || exit $?
|