+270
@@ -0,0 +1,270 @@
|
||||
#!/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 $?
|
||||
Reference in New Issue
Block a user