From d1b3364471bc4a09a5856bea5ba07a81b7645268 Mon Sep 17 00:00:00 2001 From: Kroese Date: Sat, 25 Jul 2026 02:46:26 +0200 Subject: [PATCH] feat: Merge client and server edition selection (#1955) --- src/define.sh | 29 +++- src/image.sh | 409 +++++++++++++++++++++++++++----------------------- 2 files changed, 251 insertions(+), 187 deletions(-) diff --git a/src/define.sh b/src/define.sh index 7903d058..7436ea25 100644 --- a/src/define.sh +++ b/src/define.sh @@ -37,11 +37,38 @@ USERNAME=$(strip "$USERNAME") DOMAIN_OU=$(strip "$DOMAIN_OU") WORKGROUP=$(strip "$WORKGROUP") +EDITION_ORDER=( + "-enterprise|enterprise|enterprise enterprise-*" + "-ultimate|ultimate|ultimate ultimate-*" + "|default|@default n pro pro-* professional professional-* business business-*" + "-iot|iot|iot iot-* enterprise-iot enterprise-iot-*" + "-ltsc|ltsc|ltsc ltsc-* enterprise-ltsc enterprise-ltsc-*" + "-education|education|education education-* pro-education pro-education-*" + "-home|home|home home-*" + "-home-premium|home|home-premium home-premium-*" + "-home-basic|home|home-basic home-basic-*" + "-starter|starter|starter starter-*" +) + +SERVER_EDITION_ORDER=( + "|default|@default" + "-datacenter|datacenter|datacenter datacenter-*" + "-enterprise|enterprise|enterprise enterprise-*" + "-web|web|web web-*" + "-foundation|foundation|foundation foundation-*" + "-essentials|essentials|essentials essentials-*" + "-standard-core|standard-core|standard-core standard-core-*" + "-datacenter-core|datacenter-core|datacenter-core datacenter-core-*" + "-enterprise-core|enterprise-core|enterprise-core enterprise-core-*" + "-web-core|web-core|web-core web-core-*" + "-hv|hv|hv hv-*" +) + MIRRORS=3 -SUGGEST="" parseVersion() { + SUGGEST="" VERSION=$(strip "$VERSION") [ -z "$VERSION" ] && VERSION="win11" diff --git a/src/image.sh b/src/image.sh index 19d3d5f9..e4f96eea 100644 --- a/src/image.sh +++ b/src/image.sh @@ -80,6 +80,7 @@ hasVersion() { local selected_id="${selected[$i]}" for actual in "${actuals[@]}"; do + [[ "${actual,,}" == "${expected_id,,}" ]] || continue local file="/run/assets/$selected_id.xml" @@ -100,9 +101,8 @@ hasVersion() { # Editions without a dedicated template can use the generic template. case "${selected_id,,}" in - "win7"* | "win8"* | "win10"* | "win11"* | "winvista"* | \ - "win2003"* | "win2008"* | "win2012"* | "win2016"* | \ - "win2019"* | "win2022"* | "win2025"* ) + "win7"* | "win8"* | "win10"* | "win11"* | "winvista"* | "win20"* ) + file="/run/assets/${selected_id%%-*}.xml" if [ -s "$file" ]; then @@ -111,6 +111,7 @@ hasVersion() { fi ;; esac + done done @@ -119,108 +120,79 @@ hasVersion() { getVersionPriority() { - local id="${1%-eval}" - local base="$2" - local edition="${id#"$base"}" + local id="${1,,}" + local base="${2,,}" + local order_name="EDITION_ORDER" + local edition entry priority patterns pattern + local result="other" prefix score best_score=-1 - edition="${edition#-}" + id="${id%-eval}" - case "$edition" in - "iot" | "iot-"* | "enterprise-iot" | "enterprise-iot-"* ) - echo "iot" - ;; - "ltsc" | "ltsc-"* | "enterprise-ltsc" | "enterprise-ltsc-"* ) - echo "ltsc" - ;; - "enterprise" | "enterprise-"* ) - echo "enterprise" - ;; - "ultimate" | "ultimate-"* ) - echo "ultimate" - ;; - "education" | "education-"* | "pro-education" | "pro-education-"* ) - echo "education" - ;; - "home" | "home-"* ) - echo "home" - ;; - "starter" | "starter-"* ) - echo "starter" - ;; - "hv" | "hv-"* ) - echo "hv" - ;; - "" | "n" | "pro" | "pro-"* | "professional" | "professional-"* | \ - "business" | "business-"* ) - echo "default" - ;; - * ) - echo "other" + case "$base" in + "win20"* ) + order_name="SERVER_EDITION_ORDER" ;; esac + local -n order_ref="$order_name" + + edition="${id#"$base"}" + edition="${edition#-}" + + # Use the most specific matching pattern. This prevents broad patterns + # such as enterprise-* from taking precedence over enterprise-iot-*. + for entry in "${order_ref[@]}"; do + + IFS='|' read -r _ priority patterns <<< "$entry" + + for pattern in $patterns; do + + if [ "$pattern" = "@default" ]; then + [ -z "$edition" ] || continue + score=1 + elif [[ "$pattern" == *"*" ]]; then + prefix="${pattern%\*}" + [[ "$edition" == "$prefix"* ]] || continue + score="${#pattern}" + elif [ "$edition" = "$pattern" ]; then + score="${#pattern}" + else + continue + fi + + if (( score > best_score )); then + result="$priority" + best_score="$score" + fi + + done + + done + + echo "$result" return 0 } -detectVersion() { +getVersions() { local xml="$1" - local suggested="${2:-}" - local result_name="$3" - local index_name="$4" - local -n result="$result_name" - local -n result_index="$index_name" - - local -a bases=() - local -a groups=() - local -a versions=() - local -A image_indexes=() - - local -a priorities=( - "enterprise" - "ultimate" - "default" - "iot" - "ltsc" - "education" - "home" - "starter" - "hv" - ) - - local -a suffixes=( - "-enterprise" - "-ultimate" - "" - "-iot" - "-ltsc" - "-education" - "-home" - "-home-premium" - "-home-basic" - "-starter" - "-hv" - ) - - local -a server_suffixes=( - "" - "-datacenter" - "-enterprise" - "-web" - "-foundation" - "-essentials" - "-standard-core" - "-datacenter-core" - "-enterprise-core" - "-web-core" - ) - - result="" - result_index="" + local versions_name="$2" + local bases_name="$3" + local groups_name="$4" + local indexes_name="$5" + local -n versions_ref="$versions_name" + local -n bases_ref="$bases_name" + local -n groups_ref="$groups_name" + local -n indexes_ref="$indexes_name" local image image_index key local display product platform + versions_ref=() + bases_ref=() + groups_ref=() + indexes_ref=() + platform=$(getPlatform "$xml") while IFS='|' read -r image_index display product image; do @@ -243,12 +215,12 @@ detectVersion() { found="Y" key="${candidate_id,,}" - [[ -v "image_indexes[$key]" ]] && continue + [[ -v "indexes_ref[$key]" ]] && continue - image_indexes["$key"]="$image_index" - versions+=("$candidate_id") - bases+=("$candidate_base") - groups+=("$(getVersionPriority "$candidate_id" "$candidate_base")") + indexes_ref["$key"]="$image_index" + versions_ref+=("$candidate_id") + bases_ref+=("$candidate_base") + groups_ref+=("$(getVersionPriority "$candidate_id" "$candidate_base")") done @@ -291,128 +263,193 @@ detectVersion() { ' <<< "$xml" ) - [ "${#versions[@]}" -eq 0 ] && return 0 + return 0 +} - local base match prefer +selectVersion() { + + local versions_name="$1" + local indexes_name="$2" + local preferred_name="$3" + local result_name="$4" + local index_name="$5" + local -n version_list="$versions_name" + local -n index_map="$indexes_name" + local -n preference_list="$preferred_name" + local -n selected_version="$result_name" + local -n selected_image_index="$index_name" + + local wanted match key + + for wanted in "${preference_list[@]}"; do + + [ -n "$wanted" ] || continue + + if match=$(hasVersion "$wanted" "${version_list[@]}"); then + key="${match,,}" + selected_version="$match" + selected_image_index="${index_map[$key]}" + return 0 + fi + + done + + return 1 +} + +selectEdition() { + + local versions_name="$1" + local bases_name="$2" + local groups_name="$3" + local indexes_name="$4" + local suggested="$5" + local result_name="$6" + local index_name="$7" + local normalize_name="$8" + local order_name="$9" + local -n edition_versions="$versions_name" + local -n edition_bases="$bases_name" + local -n edition_groups="$groups_name" + local -n edition_order="$order_name" + + local base edition entry suffix priority i + local -a preferred=() + local -A seen=() if [ -n "$EDITION" ]; then - local edition tried="" - - for base in "${bases[@]}"; do - - case "${base,,}" in - "win20"* ) - edition=$(normalizeServerEditionID "$EDITION") - ;; - * ) - edition=$(normalizeEditionID "$EDITION" "$base") - ;; - esac - - tried="Y" - prefer="$base" - [ -n "$edition" ] && prefer+="-$edition" - - if match=$(hasVersion "$prefer" "${versions[@]}"); then - key="${match,,}" - result="$match" - result_index="${image_indexes[$key]}" - - return 0 - fi - + for base in "${edition_bases[@]}"; do + edition=$("$normalize_name" "$EDITION" "$base") + preferred+=("$base${edition:+-$edition}") done - if [ -n "$tried" ]; then - warn "edition '$EDITION' is not supported by this image, using automatic selection instead." + if selectVersion \ + "$versions_name" \ + "$indexes_name" \ + preferred \ + "$result_name" \ + "$index_name"; then + return 0 fi + warn "edition '$EDITION' is not supported by this image, using automatic selection instead." fi - # For reused automatic media, prefer the edition selected by parseVersion() - # when that edition is actually present in the image. An explicit EDITION - # remains authoritative because it is handled above. if [ -n "$suggested" ]; then - if match=$(hasVersion "$suggested" "${versions[@]}"); then - key="${match,,}" - result="$match" - result_index="${image_indexes[$key]}" + preferred=("$suggested") + if selectVersion \ + "$versions_name" \ + "$indexes_name" \ + preferred \ + "$result_name" \ + "$index_name"; then return 0 fi fi - # Server media defaults to the normal Standard GUI edition. If Standard - # is absent, prefer another known GUI edition before any Core variant. - - local suffix + # First try each canonical edition in its configured order. + preferred=() - for suffix in "${server_suffixes[@]}"; do - for base in "${bases[@]}"; do + for entry in "${edition_order[@]}"; do - case "${base,,}" in - "win20"* ) - - prefer="$base$suffix" - - if match=$(hasVersion "$prefer" "${versions[@]}"); then - key="${match,,}" - result="$match" - result_index="${image_indexes[$key]}" - return 0 - fi - ;; - esac + IFS='|' read -r suffix _ _ <<< "$entry" + for base in "${edition_bases[@]}"; do + preferred+=("$base$suffix") done + done - # Prefer the normal edition within each selection family. hasVersion() - # still allows its Evaluation counterpart when the normal variant is absent. - for suffix in "${suffixes[@]}"; do - for base in "${bases[@]}"; do + if selectVersion \ + "$versions_name" \ + "$indexes_name" \ + preferred \ + "$result_name" \ + "$index_name"; then + return 0 + fi - prefer="$base$suffix" + # Then try noncanonical editions from the same preference groups. + preferred=() + seen=() - if match=$(hasVersion "$prefer" "${versions[@]}"); then - key="${match,,}" - result="$match" - result_index="${image_indexes[$key]}" - return 0 - fi - + for entry in "${edition_order[@]}"; do + + IFS='|' read -r _ priority _ <<< "$entry" + + [[ -v "seen[$priority]" ]] && continue + seen["$priority"]="Y" + + for ((i=0;i<${#edition_versions[@]};i++)); do + [[ "${edition_groups[$i]}" == "$priority" ]] || continue + preferred+=("${edition_versions[$i]}") done + done - # When the normal edition is absent, select another compatible member of - # that family, such as N, Workstations, or a future dynamic variant. - local priority i + selectVersion \ + "$versions_name" \ + "$indexes_name" \ + preferred \ + "$result_name" \ + "$index_name" +} - for priority in "${priorities[@]}"; do - for (( i=0; i<${#versions[@]}; i++ )); do +detectVersion() { - [[ "${groups[$i]}" == "$priority" ]] || continue + local xml="$1" + local suggested="${2:-}" + local result_name="$3" + local index_name="$4" + local -n result_ref="$result_name" + local -n index_ref="$index_name" - local actual="${versions[$i]}" + local order_name="EDITION_ORDER" + local normalize_name="normalizeEditionID" - if match=$(hasVersion "$actual" "${versions[@]}"); then - key="${match,,}" - result="$match" - result_index="${image_indexes[$key]}" - return 0 - fi - - done - done + local -a bases=() + local -a groups=() + local -a versions=() + local -A image_indexes=() - # Future or unusual editions that do not belong to a known selection - # family use the first recognized WIM image. - result="${versions[0]}" - key="${result,,}" - result_index="${image_indexes[$key]}" + result_ref="" + index_ref="" + + getVersions \ + "$xml" \ + versions \ + bases \ + groups \ + image_indexes + + [ "${#versions[@]}" -eq 0 ] && return 0 + + case "${bases[0],,}" in + "win20"* ) + order_name="SERVER_EDITION_ORDER" + normalize_name="normalizeServerEditionID" + ;; + esac + + selectEdition \ + versions \ + bases \ + groups \ + image_indexes \ + "$suggested" \ + "$result_name" \ + "$index_name" \ + "$normalize_name" \ + "$order_name" && return 0 + + result_ref="${versions[0]}" + + local key="${result_ref,,}" + index_ref="${image_indexes[$key]}" return 0 } @@ -610,8 +647,8 @@ detectImage() { local edition case "${DETECTED,,}" in - "win2003"* | "win2008"* | "win2012"* | "win2016"* | \ - "win2019"* | "win2022"* | "win2025"* ) + "win20"* ) + edition=$(normalizeServerEditionID "$EDITION") if [ -n "$edition" ] &&