From 5cb4530e6b0884ed0c28306aeb24ccdde0feca12 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 22:14:26 +0000 Subject: [PATCH 01/11] feat(quickget): detect host architecture and add --arch flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add HOST_ARCH detection and set ARCH default (arm64 → arm64, others → amd64) - Add parse_arch_flag() and support --arch/-arch before or after operation args - Inject arch="aarch64" into generated VM configs when ARCH=arm64 - Use QEMU_ARCH in distro helpers (AlmaLinux, Alpine, etc.) to build correct ISO names/URLs - Update help text to document --arch and reorder flags display - Tidy Alpine release parsing (use first match) and simplify Rocky URL assignment Note: quickget now defaults ARCH from the host; pass --arch to override if you need a different target architecture. Signed-off-by: Martin Wimpress --- quickget | 77 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/quickget b/quickget index 988279ffec..a5eb8e05cf 100755 --- a/quickget +++ b/quickget @@ -8,6 +8,13 @@ export LC_ALL=C # Detect host OS for checksum tool compatibility HOST_OS=$(uname -s) +# Default architecture based on host +HOST_ARCH=$(uname -m) +case "${HOST_ARCH}" in + aarch64|arm64) ARCH="arm64";; + *) ARCH="amd64";; +esac + function cleanup() { if [ -n "$(jobs -p)" ]; then kill "$(jobs -p)" 2>/dev/null @@ -1623,10 +1630,15 @@ function make_vm_config() { if [ ! -e "${CONF_FILE}" ]; then echo "Making ${CONF_FILE}" + local ARCH_LINE="" + if [ "${ARCH}" == "arm64" ]; then + ARCH_LINE="arch=\"aarch64\"" + fi cat << EOF > "${CONF_FILE}" #!${QUICKEMU} --vm guest_os="${GUEST}" -disk_img="${VM_PATH}/disk.qcow2" +${ARCH_LINE:+${ARCH_LINE} +}disk_img="${VM_PATH}/disk.qcow2" ${IMAGE_TYPE}="${VM_PATH}/${IMAGE_FILE}" EOF echo " - Setting ${CONF_FILE} executable" @@ -1741,8 +1753,10 @@ EOF function get_alma() { local HASH="" - local ISO="AlmaLinux-${RELEASE}-latest-x86_64-${EDITION}.iso" - local URL="https://repo.almalinux.org/almalinux/${RELEASE}/isos/x86_64" + local QEMU_ARCH="x86_64" + [ "${ARCH}" == "arm64" ] && QEMU_ARCH="aarch64" + local ISO="AlmaLinux-${RELEASE}-latest-${QEMU_ARCH}-${EDITION}.iso" + local URL="https://repo.almalinux.org/almalinux/${RELEASE}/isos/${QEMU_ARCH}" HASH="$(web_pipe "${URL}/CHECKSUM" | grep "(${ISO}" | cut -d' ' -f4)" echo "${URL}/${ISO} ${HASH}" } @@ -1750,11 +1764,13 @@ function get_alma() { function get_alpine() { local HASH="" local ISO="" - local URL="https://dl-cdn.alpinelinux.org/alpine/${RELEASE}/releases/x86_64" + local QEMU_ARCH="x86_64" + [ "${ARCH}" == "arm64" ] && QEMU_ARCH="aarch64" + local URL="https://dl-cdn.alpinelinux.org/alpine/${RELEASE}/releases/${QEMU_ARCH}" local VERSION="" - VERSION=$(web_pipe "${URL}/latest-releases.yaml" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'version:' | awk '{print $2}') - ISO="alpine-virt-${VERSION}-x86_64.iso" - HASH=$(web_pipe "${URL}/latest-releases.yaml" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'sha256:' | awk '{print $2}') + VERSION=$(web_pipe "${URL}/latest-releases.yaml" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'version:' | head -1 | awk '{print $2}') + ISO="alpine-virt-${VERSION}-${QEMU_ARCH}.iso" + HASH=$(web_pipe "${URL}/latest-releases.yaml" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'sha256:' | head -1 | awk '{print $2}') echo "${URL}/${ISO} ${HASH}" } @@ -2692,8 +2708,7 @@ function get_rockylinux() { fi local HASH="" local ISO="Rocky-${RELEASE}-x86_64-${EDITION}.iso" - local URL="" - URL="https://dl.rockylinux.org/vault/rocky/${RELEASE}/isos/x86_64" + local URL="https://dl.rockylinux.org/vault/rocky/${RELEASE}/isos/x86_64" HASH=$(web_pipe "${URL}/CHECKSUM" | grep "SHA256" | grep "${ISO})" | cut -d' ' -f4) echo "${URL}/${ISO} ${HASH}" } @@ -3748,12 +3763,13 @@ Advanced usage: quickget --download ubuntu 22.04 Arguments: - --download [edition] : Download image; no VM configuration - --create-config [path/url] [flags] : Create VM config for an OS image - --open-homepage : Open homepage for the OS - --show [os] : Show OS information - --version : Show version - --help : Show this help message + --arch : Set architecture (arm64, aarch64, amd64, x86_64) + --download [edition] : Download image; no VM configuration + --create-config [path/url] [flags] : Create VM config for an OS image + --open-homepage : Open homepage for the OS + --show [os] : Show OS information + --version : Show version + --help : Show this help message ------------------------------------ Flags ------------------------------------- --create-config: --disable-unattended : Force quickget not to set up an unattended installation @@ -3787,10 +3803,33 @@ fi CURL_VERSION=$("${CURL}" --version | head -n 1 | cut -d' ' -f2) #TODO: Deprecate `list`, `list_csv`, and `list_json` in favor of `--list`, `--list-csv`, and `--list-json` + +# Function to process --arch flag +function parse_arch_flag() { + if [ "${1}" == "--arch" ] || [ "${1}" == "-arch" ]; then + case "${2}" in + arm64|aarch64) ARCH="arm64";; + amd64|x86_64) ARCH="amd64";; + *) echo "ERROR! Unsupported architecture: ${2}"; exit 1;; + esac + return 0 # Flag was found + fi + return 1 # Flag was not found +} + +# Process --arch flag first if present (can be combined with other flags) +if parse_arch_flag "${1}" "${2}"; then + shift 2 +fi + case "${1}" in --download|-download) OPERATION="download" shift + # Handle --arch after --download + if parse_arch_flag "${1}" "${2}"; then + shift 2 + fi ;; --create-config|-create-config) OPERATION="config" @@ -3821,6 +3860,10 @@ case "${1}" in --url|-url) OPERATION="show" shift + # Handle --arch after --url + if parse_arch_flag "${1}" "${2}"; then + shift 2 + fi if [ -z "${1}" ]; then for OS in $(os_support); do (test_all "${OS}") @@ -3833,6 +3876,10 @@ case "${1}" in --check|-check) OPERATION="test" shift + # Handle --arch after --check + if parse_arch_flag "${1}" "${2}"; then + shift 2 + fi if [ -z "${1}" ]; then for OS in $(os_support); do (test_all "${OS}") From 8495a01e4dbf4fd540535af0c496ee1fe8e33647 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 22:22:39 +0000 Subject: [PATCH 02/11] fix(quickget): select correct arch for Fedora and Ubuntu server ISOs - Add FEDORA_ARCH and set to aarch64 when ARCH == arm64 - Use FEDORA_ARCH in jq filter to pick the correct Fedora ISO/sha256 - Add UBUNTU_ARCH and switch Ubuntu daily/releases URL for arm64 - Use UBUNTU_ARCH when parsing SHA256SUMS/MD5SUMS instead of hardcoded amd64 Fixes incorrect ISO selection on ARM64 hosts and enables arm64 downloads. Signed-off-by: Martin Wimpress --- quickget | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/quickget b/quickget index a5eb8e05cf..6422a2eee2 100755 --- a/quickget +++ b/quickget @@ -2068,11 +2068,15 @@ function get_endless() { } function get_fedora() { + local FEDORA_ARCH="x86_64" local HASH="" local ISO="" local JSON="" local URL="" local VARIANT="" + + [ "${ARCH}" == "arm64" ] && FEDORA_ARCH="aarch64" + case ${EDITION} in Server|Kinoite|Onyx|Silverblue|Sericea|Workstation|KDE) VARIANT="${EDITION}";; *) VARIANT="Spins";; @@ -2090,12 +2094,10 @@ function get_fedora() { RELEASE="${RELEASE/_/ }" fi - - # shellcheck disable=SC2086 # Fedora may promote variants from Spins to Editions, in which case we want to accept either "Spins" or the specific edition name to preserve backwards compatibility # For example, Fedora 42 KDE is now an edition, while previous releases are spins - JSON=$(web_pipe "https://getfedora.org/releases.json" | jq '.[] | select((.variant=="'"${VARIANT}"'" or .variant=="'"${EDITION}"'") and .subvariant=="'"${EDITION}"'" and .arch=="x86_64" and .version=="'"${RELEASE}"'" and (.link | endswith(".iso")))') + JSON=$(web_pipe "https://getfedora.org/releases.json" | jq '.[] | select((.variant=="'"${VARIANT}"'" or .variant=="'"${EDITION}"'") and .subvariant=="'"${EDITION}"'" and .arch=="'"${FEDORA_ARCH}"'" and .version=="'"${RELEASE}"'" and (.link | endswith(".iso")))') URL=$(echo "${JSON}" | jq -r '.link' | head -n1) HASH=$(echo "${JSON}" | jq -r '.sha256' | head -n1) echo "${URL} ${HASH}" @@ -2853,13 +2855,18 @@ function get_tuxedo-os() { } function get_ubuntu-server() { + local DATA="" local HASH="" local ISO="" local NAME="live-server" + local UBUNTU_ARCH="${ARCH}" local URL="" if [[ "${RELEASE}" == "daily"* ]]; then URL="https://cdimage.ubuntu.com/${OS}/${RELEASE}/current" + elif [ "${UBUNTU_ARCH}" == "arm64" ]; then + # ARM64 ISOs are hosted on cdimage.ubuntu.com + URL="https://cdimage.ubuntu.com/releases/${RELEASE}/release" else URL="https://releases.ubuntu.com/${RELEASE}" fi @@ -2869,11 +2876,11 @@ function get_ubuntu-server() { esac if web_check "${URL}/SHA256SUMS"; then - DATA=$(web_pipe "${URL}/SHA256SUMS" | grep "${NAME}" | grep amd64 | grep iso | tail -n 1 ) + DATA=$(web_pipe "${URL}/SHA256SUMS" | grep "${NAME}" | grep "${UBUNTU_ARCH}" | grep iso | tail -n 1 ) ISO=$(cut -d'*' -f2 <<<"${DATA}") HASH=$(cut -d' ' -f1 <<<"${DATA}") else - DATA=$(web_pipe "${URL}/MD5SUMS" | grep "${NAME}" | grep amd64 | grep iso | tail -n 1 ) + DATA=$(web_pipe "${URL}/MD5SUMS" | grep "${NAME}" | grep "${UBUNTU_ARCH}" | grep iso | tail -n 1 ) ISO=$(cut -d' ' -f3 <<<"${DATA}") HASH=$(cut -d' ' -f1 <<<"${DATA}") fi From a9e4d4bdf22f2ff5d06f30376e8b5a7edf7c0ee2 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 22:29:35 +0000 Subject: [PATCH 03/11] fix(quickget): use ARCH for Debian ISOs and validate arm64 editions - Use ARCH as DEBIAN_ARCH when constructing ISO filename and cdimage URL - Update DEBCURRENT handling to select arch-specific iso-hybrid path - Add explicit error and exit if arm64 is requested with a non-netinst edition (Debian provides netinst images only for ARM64) Signed-off-by: Martin Wimpress --- quickget | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/quickget b/quickget index 6422a2eee2..698c59f5fe 100755 --- a/quickget +++ b/quickget @@ -1943,13 +1943,21 @@ function get_crunchbang++() { } function get_debian() { + local DEBIAN_ARCH="${ARCH}" local DEBCURRENT="" local HASH="" - local ISO="debian-live-${RELEASE}-amd64-${EDITION}.iso" - local URL="https://cdimage.debian.org/cdimage/archive/${RELEASE}-live/amd64/iso-hybrid" + local ISO="debian-live-${RELEASE}-${DEBIAN_ARCH}-${EDITION}.iso" + local URL="https://cdimage.debian.org/cdimage/archive/${RELEASE}-live/${DEBIAN_ARCH}/iso-hybrid" + + # Debian only provides netinst images for ARM64 + if [ "${DEBIAN_ARCH}" == "arm64" ] && [ "${EDITION}" != "netinst" ]; then + echo "ERROR! Debian ${EDITION} is not available for ARM64. Use 'netinst' edition." + exit 1 + fi + DEBCURRENT=$(web_pipe "https://cdimage.debian.org/debian-cd/" | grep '\.[0-9]/' | cut -d'>' -f 9 | cut -d'/' -f 1) case "${RELEASE}" in - "${DEBCURRENT}") URL="https://cdimage.debian.org/debian-cd/${RELEASE}-live/amd64/iso-hybrid";; + "${DEBCURRENT}") URL="https://cdimage.debian.org/debian-cd/${RELEASE}-live/${DEBIAN_ARCH}/iso-hybrid";; esac if [ "${EDITION}" == "netinst" ]; then URL="${URL/-live/}" From f846c0c6ff0f76e48ac94eb23cf649f57ea36501 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 22:45:46 +0000 Subject: [PATCH 04/11] feat(quickget): add per-distro architecture support and validation - Add arch_*() functions declaring supported architectures for several distros - Add is_arch_supported() helper; default to amd64 when no arch_* exists - Validate architecture before generating URLs or running tests (web_get, zsync_get, test_all) - Update test_result() to accept and display an optional reason when skipping - Prevent false failures by skipping tests for unsupported architectures IMPACT: quickget now respects requested architectures and reports clear SKIP reasons for distros that do not support the selected arch. Signed-off-by: Martin Wimpress --- quickget | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/quickget b/quickget index 698c59f5fe..939721000d 100755 --- a/quickget +++ b/quickget @@ -537,6 +537,7 @@ function test_result() { local EDITION="${3:-}" local URL="${4:-}" local RESULT="${5:-}" + local REASON="${6:-}" if [ -n "${EDITION}" ]; then OS="${OS}-${RELEASE}-${EDITION}" else @@ -546,7 +547,11 @@ function test_result() { if [ -n "${RESULT}" ]; then # Pad the OS string for consistent output OS=$(printf "%-35s" "${OS}") - echo -e "${RESULT}: ${OS} ${URL}" + if [ -n "${REASON}" ]; then + echo -e "${RESULT}: ${OS} ${REASON}" + else + echo -e "${RESULT}: ${OS} ${URL}" + fi else OS=$(printf "%-36s" "${OS}:") echo -e "${OS} ${URL}" @@ -567,6 +572,11 @@ function test_all() { for RELEASE in $("releases_${FUNC}"); do if [[ $(type -t "editions_${OS}") == function ]]; then for EDITION in $(editions_"${OS}"); do + # Check architecture support before generating URL + if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" + continue + fi validate_release releases_"${OS}" URL=$(get_"${OS}" | cut -d' ' -f1 | head -n 1) if [ "${OPERATION}" == "show" ]; then @@ -590,12 +600,27 @@ function test_all() { validate_release releases_macos (get_macos) elif [ "${OS}" == "ubuntu-server" ]; then + # Check architecture support before generating URL + if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" + continue + fi validate_release releases_ubuntu-server (get_ubuntu-server) elif [[ "${OS}" == *ubuntu* ]]; then + # Ubuntu desktop is amd64 only (no arch function = amd64 default) + if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" + continue + fi validate_release releases_ubuntu (get_ubuntu) else + # Check architecture support before generating URL + if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" + continue + fi validate_release releases_"${OS}" URL=$(get_"${OS}" | cut -d' ' -f1 | head -n 1) if [ "${OPERATION}" == "show" ]; then @@ -1343,6 +1368,51 @@ function releases_zorin() { echo 18 17 16 } +# Architecture support functions +# These declare which architectures each distro supports. +# Distros without an arch_*() function default to "amd64" only. + +function arch_alma() { + echo "amd64 arm64" +} + +function arch_alpine() { + echo "amd64 arm64" +} + +function arch_debian() { + case "${EDITION}" in + netinst) echo "amd64 arm64";; + *) echo "amd64";; + esac +} + +function arch_fedora() { + echo "amd64 arm64" +} + +function arch_ubuntu-server() { + echo "amd64 arm64" +} + +# Check if a given architecture is supported for an OS +function is_arch_supported() { + local OS="${1}" + local CHECK_ARCH="${2}" + local SUPPORTED="" + + # Check if arch function exists for this OS + if [[ $(type -t "arch_${OS}") == function ]]; then + SUPPORTED=$(arch_"${OS}") + else + # Default: amd64 only + SUPPORTED="amd64" + fi + + # Check if requested arch is in supported list + [[ " ${SUPPORTED} " == *" ${CHECK_ARCH} "* ]] +} + function editions_zorin() { # Lite edition not available for Zorin 18 (Pro-only starting from 18) # When RELEASE is unset (e.g. csv_data context), return all editions @@ -1455,6 +1525,11 @@ function web_get() { test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" exit 0 elif [ "${OPERATION}" == "test" ]; then + # Check architecture support before testing URL + if ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" + exit 0 + fi CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL") test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}" exit 0 @@ -1520,6 +1595,11 @@ function zsync_get() { test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" exit 0 elif [ "${OPERATION}" == "test" ]; then + # Check architecture support before testing URL + if ! is_arch_supported "${OS}" "${ARCH}"; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" + exit 0 + fi CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL") test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}" exit 0 From fdbf0d42309f1c0f31c526743fdff9b4630f6b50 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 22:59:07 +0000 Subject: [PATCH 05/11] feat(quickget): add --check-all-arch to test amd64 and arm64 - Add CLI flag --check-all-arch with help text and CHECK_ALL_ARCH var - Implement loop to run checks for both amd64 and arm64, setting ARCH per run - Validate architecture and release/edition before generating URLs; skip Windows - Generate and check URLs per-arch, reporting PASS / FAIL / SKIP per entry - Update CI workflow to call ./quickget --check-all-arch for distro matrix tests Signed-off-by: Martin Wimpress --- .github/workflows/test-quickget.yml | 2 +- quickget | 66 +++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-quickget.yml b/.github/workflows/test-quickget.yml index 7b08b48122..0d4e9709fc 100644 --- a/.github/workflows/test-quickget.yml +++ b/.github/workflows/test-quickget.yml @@ -50,7 +50,7 @@ jobs: id: check run: | mkdir -p results - ./quickget --check "${{ matrix.distro }}" | tee results/check.txt + ./quickget --check-all-arch "${{ matrix.distro }}" | tee results/check.txt # Count results (use wc -l to avoid grep exit code issues) PASSED=$(grep '^PASS' results/check.txt | wc -l | tr -d ' ') diff --git a/quickget b/quickget index 939721000d..9fe1760ab3 100755 --- a/quickget +++ b/quickget @@ -3871,6 +3871,7 @@ Arguments: -------------------------- For testing & development --------------------------- --url [os] [release] [edition] : Show image URL(s) --check [os] [release] [edition] : Check image URL(s) + --check-all-arch [os] [release] [edition]: Check downloads for all architectures (amd64 and arm64) --list : List all supported systems --list-csv : List everything in csv format --list-json : List everything in json format @@ -3890,6 +3891,7 @@ fi QUICKEMU=$(resolve_quickemu) I18NS=() OPERATION="" +CHECK_ALL_ARCH="false" CURL=$(command -v curl) if [ ! -x "${CURL}" ]; then echo "ERROR! curl not found. Please install curl" @@ -3984,6 +3986,25 @@ case "${1}" in test_all "${1}" exit 0 fi;; + --check-all-arch|-check-all-arch) + OPERATION="test" + CHECK_ALL_ARCH="true" + shift + if [ -z "${1}" ]; then + for CHECK_ARCH in amd64 arm64; do + ARCH="${CHECK_ARCH}" + for OS in $(os_support); do + (test_all "${OS}") + done + done + exit 0 + elif [ -z "${2}" ]; then + for CHECK_ARCH in amd64 arm64; do + ARCH="${CHECK_ARCH}" + test_all "${1}" + done + exit 0 + fi;; --list-csv|-list-csv|list|list_csv) list_csv;; --list-json|-list-json|list_json) list_json;; --list|-list) list_supported;; @@ -3998,6 +4019,51 @@ fi os_supported +# Handle --check-all-arch for specific release/edition +if [ "${CHECK_ALL_ARCH}" == "true" ] && [ -n "${2}" ]; then + RELEASE="${2}" + EDITION="${3:-}" + for CHECK_ARCH in amd64 arm64; do + ARCH="${CHECK_ARCH}" + # Check architecture support before testing URL + if ! is_arch_supported "${OS}" "${ARCH}"; then + if [ -n "${EDITION}" ]; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" + else + test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" + fi + continue + fi + # Validate release + case ${OS} in + *ubuntu-server*) validate_release releases_ubuntu-server;; + *ubuntu*) validate_release releases_ubuntu;; + *) validate_release "releases_${OS}";; + esac + # Generate and check URL + if [[ $(type -t "editions_${OS}") == function ]] && [ -n "${EDITION}" ]; then + URL=$(get_"${OS}" | cut -d' ' -f1 | head -n 1) + elif [ "${OS}" == "macos" ]; then + (get_macos) + continue + elif [[ "${OS}" == *"ubuntu-server"* ]]; then + (get_ubuntu-server) + continue + elif [[ "${OS}" == *"ubuntu"* ]]; then + (get_ubuntu) + continue + elif [[ "${OS}" == "windows"* ]]; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(Windows not supported in check mode)" + continue + else + URL=$(get_"${OS}" | cut -d' ' -f1 | head -n 1) + fi + CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL") + test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}" + done + exit 0 +fi + if [ -n "${2}" ]; then RELEASE="${2}" VM_PATH="${OS}-${RELEASE}" From d40dd70b3b1dae2ab168ae22efef6213851efdf2 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 23:18:47 +0000 Subject: [PATCH 06/11] fix(quickget): validate architecture before attempting download Signed-off-by: Martin Wimpress --- quickget | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/quickget b/quickget index 9fe1760ab3..501e1ac4c1 100755 --- a/quickget +++ b/quickget @@ -4064,6 +4064,17 @@ if [ "${CHECK_ALL_ARCH}" == "true" ] && [ -n "${2}" ]; then exit 0 fi +# Validate architecture support before attempting download +if ! is_arch_supported "${OS}" "${ARCH}"; then + if [ "${OPERATION}" == "test" ] || [ "${OPERATION}" == "show" ]; then + test_result "${OS}" "${2:-}" "${3:-}" "" "SKIP" "(not available for ${ARCH})" + exit 0 + else + echo "ERROR! $(pretty_name "${OS}") is not available for ${ARCH} architecture." + exit 1 + fi +fi + if [ -n "${2}" ]; then RELEASE="${2}" VM_PATH="${OS}-${RELEASE}" From efd0d6d91343e2f4fed881a6635d204d607d244d Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sat, 24 Jan 2026 23:28:33 +0000 Subject: [PATCH 07/11] fix(quickget): skip unsupported architectures for all operations - Remove the OPERATION == "test" guard so is_arch_supported() is evaluated unconditionally - Apply the unconditional arch check in test_all() for editions loop, ubuntu-server, ubuntu desktop (ubuntu*), and the default case - Skip generating URLs / running further logic when an OS is not available for the requested ARCH IMPACT: quickget will no longer attempt to generate or test downloads for architectures that the distro does not support, preventing incorrect URL generation and false-positive test attempts. Signed-off-by: Martin Wimpress --- quickget | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/quickget b/quickget index 501e1ac4c1..bcd2a3afa0 100755 --- a/quickget +++ b/quickget @@ -573,7 +573,7 @@ function test_all() { if [[ $(type -t "editions_${OS}") == function ]]; then for EDITION in $(editions_"${OS}"); do # Check architecture support before generating URL - if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + if ! is_arch_supported "${OS}" "${ARCH}"; then test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" continue fi @@ -601,7 +601,7 @@ function test_all() { (get_macos) elif [ "${OS}" == "ubuntu-server" ]; then # Check architecture support before generating URL - if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + if ! is_arch_supported "${OS}" "${ARCH}"; then test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" continue fi @@ -609,7 +609,7 @@ function test_all() { (get_ubuntu-server) elif [[ "${OS}" == *ubuntu* ]]; then # Ubuntu desktop is amd64 only (no arch function = amd64 default) - if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + if ! is_arch_supported "${OS}" "${ARCH}"; then test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" continue fi @@ -617,7 +617,7 @@ function test_all() { (get_ubuntu) else # Check architecture support before generating URL - if [ "${OPERATION}" == "test" ] && ! is_arch_supported "${OS}" "${ARCH}"; then + if ! is_arch_supported "${OS}" "${ARCH}"; then test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${ARCH})" continue fi @@ -1388,7 +1388,10 @@ function arch_debian() { } function arch_fedora() { - echo "amd64 arm64" + case "${EDITION}" in + Server) echo "amd64 arm64";; + *) echo "amd64";; + esac } function arch_ubuntu-server() { From 8383d23c10c6701d216a790cddd36a6b32af972d Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sun, 25 Jan 2026 00:18:12 +0000 Subject: [PATCH 08/11] fix(quickget): validate architecture support after edition selection - Skip early architecture check for OSes with editions_ to avoid rejecting valid edition-specific combinations - Perform architecture validation after EDITION is known and show a descriptive error when the chosen edition isn't available on ARCH - Preserve test/show behaviour (report SKIP) and keep previous flow for OSes without editions_ Signed-off-by: Martin Wimpress --- quickget | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/quickget b/quickget index bcd2a3afa0..6771228d88 100755 --- a/quickget +++ b/quickget @@ -4068,13 +4068,17 @@ if [ "${CHECK_ALL_ARCH}" == "true" ] && [ -n "${2}" ]; then fi # Validate architecture support before attempting download -if ! is_arch_supported "${OS}" "${ARCH}"; then - if [ "${OPERATION}" == "test" ] || [ "${OPERATION}" == "show" ]; then - test_result "${OS}" "${2:-}" "${3:-}" "" "SKIP" "(not available for ${ARCH})" - exit 0 - else - echo "ERROR! $(pretty_name "${OS}") is not available for ${ARCH} architecture." - exit 1 +# Skip for OSes with editions_* functions - they have edition-dependent arch support +# and will be validated after EDITION is parsed +if [[ $(type -t "editions_${OS}") != function ]]; then + if ! is_arch_supported "${OS}" "${ARCH}"; then + if [ "${OPERATION}" == "test" ] || [ "${OPERATION}" == "show" ]; then + test_result "${OS}" "${2:-}" "${3:-}" "" "SKIP" "(not available for ${ARCH})" + exit 0 + else + echo "ERROR! $(pretty_name "${OS}") is not available for ${ARCH} architecture." + exit 1 + fi fi fi @@ -4102,6 +4106,11 @@ if [ -n "${2}" ]; then echo -e "\nERROR! You must specify an edition." exit 1 fi + # Now that EDITION is known, validate architecture support + if ! is_arch_supported "${OS}" "${ARCH}"; then + echo "ERROR! $(pretty_name "${OS}") ${EDITION} is not available for ${ARCH} architecture." + exit 1 + fi handle_missing VM_PATH="${OS}-${RELEASE}-${EDITION}" create_vm "$("get_${OS}" "${EDITION}")" From 2639d6eff1b38997e597e9c44a6483dba79c8362 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sun, 25 Jan 2026 00:26:50 +0000 Subject: [PATCH 09/11] fix(quickget): skip unsupported architectures for test/show operations - If architecture is unsupported and OPERATION is "test" or "show", call test_result with SKIP and exit 0 - Keep existing error and exit 1 for non-test/show operations Signed-off-by: Martin Wimpress --- quickget | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quickget b/quickget index 6771228d88..5c27e4e40d 100755 --- a/quickget +++ b/quickget @@ -4108,8 +4108,13 @@ if [ -n "${2}" ]; then fi # Now that EDITION is known, validate architecture support if ! is_arch_supported "${OS}" "${ARCH}"; then - echo "ERROR! $(pretty_name "${OS}") ${EDITION} is not available for ${ARCH} architecture." - exit 1 + if [ "${OPERATION}" == "test" ] || [ "${OPERATION}" == "show" ]; then + test_result "${OS}" "${RELEASE}" "${EDITION}" "" "SKIP" "(not available for ${ARCH})" + exit 0 + else + echo "ERROR! $(pretty_name "${OS}") ${EDITION} is not available for ${ARCH} architecture." + exit 1 + fi fi handle_missing VM_PATH="${OS}-${RELEASE}-${EDITION}" From 211065a8d2554ed969b20ed709e0a68295139991 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sun, 25 Jan 2026 00:34:07 +0000 Subject: [PATCH 10/11] feat(quickget): support Ubuntu desktop ARM64 from 25.10 - Add arch_ubuntu() to enumerate supported architectures and gate ARM64 by release - Validate architecture once RELEASE is known and exit/skip if unsupported - Use UBUNTU_ARCH when selecting ISO lines and hashes instead of hardcoded amd64 - Use cdimage.ubuntu.com releases path for Ubuntu ARM64 desktop ISOs - Default non-numeric releases (daily, dvd, etc.) to amd64 only Signed-off-by: Martin Wimpress --- quickget | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/quickget b/quickget index 5c27e4e40d..cbf331a601 100755 --- a/quickget +++ b/quickget @@ -1398,6 +1398,30 @@ function arch_ubuntu-server() { echo "amd64 arm64" } +function arch_ubuntu() { + # Ubuntu Desktop ARM64 only available from 25.10 onwards + local MAJOR MINOR + # When RELEASE is unset (initial arch check), return all supported architectures + # Actual validation happens in get_ubuntu() when release is known + if [ -z "${RELEASE}" ]; then + echo "amd64 arm64" + return + fi + # Non-numeric releases (daily-live, etc.) default to amd64 only + if [[ ! "${RELEASE}" =~ ^[0-9]+\.[0-9]+$ ]]; then + echo "amd64" + return + fi + MAJOR="${RELEASE%%.*}" + MINOR="${RELEASE##*.}" + # 25.10 and later support ARM64 + if [ "${MAJOR}" -gt 25 ] || { [ "${MAJOR}" -eq 25 ] && [ "${MINOR}" -ge 10 ]; }; then + echo "amd64 arm64" + else + echo "amd64" + fi +} + # Check if a given architecture is supported for an OS function is_arch_supported() { local OS="${1}" @@ -2986,10 +3010,22 @@ function get_ubuntu-server() { } function get_ubuntu() { - local ISO="" + local DATA="" local HASH="" + local ISO="" + local UBUNTU_ARCH="${ARCH}" local URL="" - local DATA="" + + # Validate architecture support now that RELEASE is known + if ! is_arch_supported "${OS}" "${UBUNTU_ARCH}"; then + if [ "${OPERATION}" == "test" ] || [ "${OPERATION}" == "show" ]; then + test_result "${OS}" "${RELEASE}" "" "" "SKIP" "(not available for ${UBUNTU_ARCH})" + exit 0 + else + echo "ERROR! $(pretty_name "${OS}") ${RELEASE} is not available for ${UBUNTU_ARCH} architecture." + exit 1 + fi + fi if [[ "${RELEASE}" == "daily"* ]] && [ "${OS}" == "ubuntustudio" ]; then # Ubuntu Studio daily-live images are in the dvd directory @@ -3005,17 +3041,20 @@ function get_ubuntu() { elif [[ "${RELEASE}" == "daily"* ]] || [ "${RELEASE}" == "dvd" ]; then URL="https://cdimage.ubuntu.com/${OS}/${RELEASE}/current" VM_PATH="${OS}-${RELEASE}" + elif [ "${OS}" == "ubuntu" ] && [ "${UBUNTU_ARCH}" == "arm64" ]; then + # ARM64 desktop ISOs are hosted on cdimage.ubuntu.com + URL="https://cdimage.ubuntu.com/releases/${RELEASE}/release" elif [ "${OS}" == "ubuntu" ]; then URL="https://releases.ubuntu.com/${RELEASE}" else URL="https://cdimage.ubuntu.com/${OS}/releases/${RELEASE}/release" fi if web_check "${URL}/SHA256SUMS"; then - DATA=$(web_pipe "${URL}/SHA256SUMS" | grep 'desktop\|dvd\|install' | grep amd64 | grep iso | grep -v "+mac" | tail -n 1 ) + DATA=$(web_pipe "${URL}/SHA256SUMS" | grep 'desktop\|dvd\|install' | grep "${UBUNTU_ARCH}" | grep iso | grep -v "+mac" | tail -n 1 ) ISO=$(cut -d'*' -f2 <<<"${DATA}" | sed '1q;d') HASH=$(cut -d' ' -f1 <<<"${DATA}" | sed '1q;d') else - DATA=$(web_pipe "${URL}/MD5SUMS" | grep 'desktop\|dvd\|install' | grep amd64 | grep iso | grep -v "+mac" | tail -n 1 ) + DATA=$(web_pipe "${URL}/MD5SUMS" | grep 'desktop\|dvd\|install' | grep "${UBUNTU_ARCH}" | grep iso | grep -v "+mac" | tail -n 1 ) ISO=$(cut -d'*' -f2 <<<"${DATA}") HASH=$(cut -d' ' -f1 <<<"${DATA}") fi From cc8e8a197c51225fac310eed9f5a51d61e59e248 Mon Sep 17 00:00:00 2001 From: Martin Wimpress Date: Sun, 25 Jan 2026 00:51:50 +0000 Subject: [PATCH 11/11] fix(quickget): prefer macOS-friendly hash commands in check_hash - Prefer GNU coreutils g* hash commands on macOS when available; fall back to native shasum/md5 otherwise - Handle MD5 on macOS using 'md5 -r' and parse its output for comparison - Warn and skip b2sum verification when GNU b2sum is not installed on macOS - Use a selected hash command variable when printing status and performing checks Signed-off-by: Martin Wimpress --- quickget | 56 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/quickget b/quickget index cbf331a601..217e14dc82 100755 --- a/quickget +++ b/quickget @@ -1488,19 +1488,59 @@ function check_hash() { esac fi - # Use GNU coreutils on macOS/Darwin (prefixed with 'g') + # On macOS/Darwin, prefer GNU coreutils (prefixed with 'g') if available, + # otherwise fall back to native 'shasum' command + local hash_cmd="${hash_algo}" if [ "${HOST_OS}" = "Darwin" ]; then case ${hash_algo} in - md5sum) hash_algo=gmd5sum;; - sha1sum) hash_algo=gsha1sum;; - sha256sum) hash_algo=gsha256sum;; - sha512sum) hash_algo=gsha512sum;; - b2sum) hash_algo=gb2sum;; + md5sum) + if command -v gmd5sum &>/dev/null; then + hash_cmd=gmd5sum + else + # MD5 not directly supported by shasum; use native md5 command + hash_cmd="md5 -r" + fi;; + sha1sum) + if command -v gsha1sum &>/dev/null; then + hash_cmd=gsha1sum + else + hash_cmd="shasum -a 1" + fi;; + sha256sum) + if command -v gsha256sum &>/dev/null; then + hash_cmd=gsha256sum + else + hash_cmd="shasum -a 256" + fi;; + sha512sum) + if command -v gsha512sum &>/dev/null; then + hash_cmd=gsha512sum + else + hash_cmd="shasum -a 512" + fi;; + b2sum) + if command -v gb2sum &>/dev/null; then + hash_cmd=gb2sum + else + echo "WARNING! b2sum not available on macOS without GNU coreutils, not checking ${iso} hash." + return + fi;; esac fi - echo -n "Checking ${iso} with ${hash_algo}... " - if ! printf '%s %s\n' "${hash}" "${iso}" | ${hash_algo} --check --status; then + echo -n "Checking ${iso} with ${hash_cmd}... " + # Handle MD5 on macOS specially (md5 -r outputs "hash filename" format) + if [ "${hash_cmd}" = "md5 -r" ]; then + local computed_hash + computed_hash=$(md5 -r "${iso}" | cut -d' ' -f1) + if [ "${computed_hash}" != "${hash}" ]; then + echo "ERROR!" + echo "${iso} doesn't match ${hash}. Try running 'quickget' again." + exit 1 + else + echo "Good!" + fi + elif ! printf '%s %s\n' "${hash}" "${iso}" | ${hash_cmd} --check --status; then echo "ERROR!" echo "${iso} doesn't match ${hash}. Try running 'quickget' again." exit 1