#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

## Intentional, throughout this file: a leading '$' on variables already in
## arithmetic context (e.g. $(( ${x} + 1 ))) is a harmless style choice.
# shellcheck disable=SC2004

# shellcheck source=../packages/kicksecure/helper-scripts/usr/libexec/helper-scripts/check_runtime.bsh

#set -x
set -o errexit
set -o nounset
set -o pipefail
set -o errtrace
shopt -s inherit_errexit
shopt -s shift_verbose
export LC_ALL=C

HELPER_SCRIPTS_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../packages/kicksecure/helper-scripts"
export HELPER_SCRIPTS_PATH
source "${HELPER_SCRIPTS_PATH}/usr/libexec/helper-scripts/check_runtime.bsh"

if was_executed "${BASH_SOURCE[0]}"; then
   parse_cmd_was_sourced='false'
else
   parse_cmd_was_sourced='true'
fi

implicit_dist_type() {
   local implicit_type
   implicit_type="$1"
   if printf '%s\n' "${args[@]}" | grep --fixed-strings --line-regexp -- "--type" &>/dev/null; then
      return 0
   fi
   if [ ! "${dist_build_type:-}" = "" ]; then
      return 0
   fi
   printf '%s\n' "INFO: implicitly setting '--type ${implicit_type}' because using '${1:-}'."
   dist_build_type="${implicit_type}"
   export dist_build_type
}

parse_cmd_type_error() {
   printf '%s\n' "${red}${bold}ERROR: --type must be either 'vm' or 'host'.${reset}"
   exit 1
}

parse_cmd_target_error() {
   printf '%s\n' "${red}${bold}ERROR: --target must be either virtualbox, qcow2, utm, iso, raw, dist-installer-cli, windows or root and can be used multiple times.${reset}"
   exit 1
}

parse_cmd_flavor_error() {
   printf '%s\n' "${red}${bold}You must add either:
--flavor whonix-gateway-lxqt
--flavor whonix-gateway-rpi
--flavor whonix-gateway-cli
--flavor whonix-workstation-lxqt
--flavor whonix-workstation-cli
--flavor whonix-custom-workstation
--flavor whonix-host-lxqt
--flavor whonix-host-cli
--flavor kicksecure-cli
--flavor kicksecure-lxqt
--flavor kicksecure-ci-tiny-do-not-use (DANGEROUS, DO NOT USE)
--flavor dist-installer-cli
--flavor source
${reset}"
   exit 1
}

parse_cmd_freedom_versus_nonfreedom_firmware_choice_check() {
   if [ "${build_freedom_only:-}" = "true" ]; then
      true "${BASH_SOURCE[0]} INFO: --freedom true, therefore omitting package firmware-nonfreedom."
   elif [ "${build_freedom_only:-}" = "false" ]; then
      true "${BASH_SOURCE[0]} INFO: --freedom false, therefore adding package firmware-nonfreedom, ok. See also: https://www.kicksecure.com/wiki/Dev/nonfree"
   else
      error "${red}${bold}When building '--arch amd64', you must select firmware. Add either '--freedom true' or '--freedom false'!${reset}"
   fi
}

error_dangerous_option_maybe() {
  if [ "${dist_build_unlock_dangerous_options:-}" = 'true' ]; then
    return 0
  fi
  error "\
${red}${bold}${1} is marked as a DANGEROUS option. You should NOT use this unless you
know exactly what you are doing.

Options that are marked as dangerous will not cause damage to the builder, but
will enable behavior that is unintuitive, potentially misleading, or that has
security implications that users must fully understand before use. These
options should only be used by experienced developers.

To enable the use of dangerous options, export
'dist_build_unlock_dangerous_options=true' in the environment.${reset}"
  exit 1
}

dist_build_one_script_help() {
   local cmd_name

   cmd_name="${1:-}";
   if [ -n "${cmd_name}" ]; then
      cmd_name="$(basename "$(realpath "${cmd_name}")")"
   fi

   if [ "${cmd_name}" = 'derivative-maker' ]; then
      printf '%s\n' "\
derivative-maker

Syntax:
  --flavor [flavor] --target [target]

Description:
  Creates a separate build folder at \$HOMEVAR/derivative-binary.

  For detailed information on build configuration parameters, please refer to the full build documentation.

Flavors:
  --flavor [flavor_option]
  Options:
    whonix-gateway-lxqt         : Builds Whonix-Gateway LXQt VM.
    whonix-gateway-rpi          : Builds Whonix-Gateway CLI RPi 3 VM.
    whonix-gateway-cli          : Builds Whonix-Gateway CLI VM.
    whonix-workstation-lxqt     : Builds Whonix-Workstation LXQt VM.
    whonix-workstation-cli      : Builds Whonix-Workstation CLI VM.
    whonix-custom-workstation   : Builds Whonix-Custom-Workstation VM.
    whonix-host-cli             : Builds Whonix-Host CLI.
    whonix-host-lxqt            : Builds Whonix-Host LXQt.
    kicksecure-cli              : Builds Kicksecure VM CLI VM.
    kicksecure-lxqt             : Builds Kicksecure VM LXQt VM.
    source                      : Build helpers (e.g. cowbuilder chroot, source packages) without targeting a specific VM flavor.

Targets:
  --target [target_option]
  Options:
    virtualbox              : Builds VirtualBox .ova files.
    qcow2                   : Builds qcow2 images.
    utm                     : Builds UTM images.
    iso                     : Builds ISO images.
    raw                     : Builds raw disk images.
    dist-installer-cli      : Builds dist-installer-cli.
    windows                 : Builds the Windows Installer.
    root                    : Builds for physical installations.
    source                  : Builds a xz source archive.

Types:
  --type [type_option]
  Options:
    host                    : Specifies that the build is for a host system.
    vm                      : Specifies that the build is for a virtual machine.

Optional Parameters:
  --vmram [size]           : Set VM RAM size (e.g., --vmram 128).
  --vram [size]            : Set VM video RAM size (e.g., --vram 12).
  --vmsize [size]          : Set VM disk size (e.g., --vmsize 200G).

  --freshness [option]     : Choose between 'frozen' (reproducible build with frozen sources) or
                             'current' (current sources). Mandatory when building an image.
  --build-slot [name]      : Build under derivative-binary/<name> so multiple builds can run
                             concurrently. Name chars: [A-Za-z0-9_-]. Default: unset.
  --connection [option]    : Select 'clearnet' for clearnet apt sources or 'onion' for onion apt sources.
  --repo [true|false]      : Enable or disable derivative remote repository (default: false).

Environment Variables:
  - flavor_meta_packages_to_install: Define meta packages to be installed.
    Examples:
      flavor_meta_packages_to_install='none'
      flavor_meta_packages_to_install='kicksecure-baremetal-gui-lxqt kicksecure-baremetal-server'

  - install_package_list: Specify additional custom packages for installation.
    Examples:
      install_package_list='gparted'
      install_package_list='gparted gedit'

  - DERIVATIVE_APT_REPOSITORY_OPTS: Set options for the Derivative APT Repository.
    Examples:
      DERIVATIVE_APT_REPOSITORY_OPTS='--enable --repository stable'
      DERIVATIVE_APT_REPOSITORY_OPTS='--enable --repository testers'
      DERIVATIVE_APT_REPOSITORY_OPTS='--enable --repository developers'
      DERIVATIVE_APT_REPOSITORY_OPTS='--enable --codename trixie'

   - vm_names_to_be_exported: Specify for unified images which images to combine.
    Default:
      For CLI:
        vm_names_to_be_exported='Whonix-Gateway-CLI Whonix-Workstation-CLI'
      For LXQt:
        vm_names_to_be_exported='Whonix-Gateway-LXQt Whonix-Workstation-LXQt'
    Examples mixing CLI gateway with LXQt workstation:
        vm_names_to_be_exported='Whonix-Gateway-CLI Whonix-Workstation-LXQt'

Advanced Options:
  --report [true|false]           : Enable or disable build reports (default: false).
  --serial-console-enable [true|false] : Install serial-console-enable and enable serial console on ISO builds (default: false).
  --grub-bios-theme [true|false]  : Force the 4x3/BIOS grub theme and resolution in the built image regardless of build-host firmware for reproducible builds (default: true).
  --smbios-reader [true|false]    : DANGEROUS, DO NOT USE. Allow injecting kernel parameters via the SMBIOS serial number field. This will allow attackers with physical access to inject arbitrary kernel parameters into the boot process! Only useful for CI and AI agents.
  --sanity-tests [true|false]     : Enable or disable chroot script sanity tests for faster build speed (default: false).
  --only-packages 'p1 p2'         : Rebuild only listed packages into the persistent local repo, reuse the rest from the prior build.
  --skip-packages                 : Skip package building entirely and reuse the whole local package repo from the prior build.
  --skip-published-packages       : Rebuild only packages that changed since the last build, reuse unchanged packages from the prior build.
  --package-jobs N                : Build up to N derivative packages concurrently. Default: 1.
  --skip-prepare-build-machine    : Skip build-steps.d/*_prepare-build-machine. Only safe when the build machine was already prepared by an earlier run in the same session.
  --skip-cowbuilder-setup         : Skip build-steps.d/*_cowbuilder-setup. Only safe when the cowbuilder chroot was already created by an earlier run in the same session.
  --reproducible-dist-build-version : DANGEROUS, DO NOT USE. Drops the '-<n>-g<commit>' version suffix on builds from untagged commits. This will result in the built image carrying a misleading version number! Only useful for AI agents, for reproducing CI-built images outside of CI. Humans should tolerate the version suffix or create a release tag.
  --reuse-cowbuilder-base         : Reuse an already-present cowbuilder base chroot instead of rebuilding it, if one exists.
  --skip-local-dependencies       : Skip build-steps.d/*_local-dependencies. Only safe when the local dependencies were already installed by an earlier run in the same session.
  --base-image-role <create|consume> : Creates or reuses a shared base image for building multiple images quicker. Mainly useful for building release images.
  --retry-max [attempts]          : Set maximum retry attempts. (default: 2)
  --retry-wait [seconds]          : Set wait time between retry attempts.
  --retry-before [script]         : Specify a script to run before retry. [default: none)
  --retry-after [script]          : Specify a script to run after retry. [default: none)
  --allow-uncommitted [true|false]: Permit builds with uncommitted changes (default: false).
  --allow-untagged [true|false]   : Permit builds from non-tagged sources (default: false).
  --allow-unsigned [true|false]   : Skip git_sanity_test commit-signature verification (default: false).
  --sign-and-tag [true|false]     : DANGEROUS, DO NOT USE. Amend, sign, and tag HEAD and submodules. This may sign code you did not intend to sign! It may break your ability to pull from git! Only useful for AI agents to test signing verification without access to sensitive keys. Humans should either use --allow-unsigned or set up a GPG key properly. (default: false).
  --kernel [packages]             : Specify kernel packages (e.g., 'linux-image-amd64' or 'none').
  --headers [packages]            : Specify kernel header packages.
  --remote-derivative-packages    : Choose to use remote derivative packages instead of building derivative packages from source code. (default: false).
  --release [unsupported_option]  : Set release option (unsupported). (trixie|bookworm)
  --arch [architecture]           : Set architecture (pick one of the following)
    ${architecture_all_list[*]}
    (Note: amd64 also works with most Intel CPUs.)
  --debug                         : Does not disable verbose/debug (xtrace) during help-steps pre, variables and parse-cmd.
  --dry-run [true|false]          : Does not actually build real images. Just an empty text file. Useful for debugging dm-prepare-release script.
  --unsupported-os [true|false]   : Skip the build-host operating system / codename sanity check. NOT for release builds; for dev / AI / CI smoke tests on non-Debian hosts only.

For VMs only:
  --initramfs [packages]          : Specify initramfs packages. (default: \$BUILD_INITRAMFS_PKGS)

Configuration Files:
  --confdir [/path/to/config/dir] : Specify an additional configuration directory.
  --conffile [/path/to/config/file]: Specify an additional configuration file.
  --grmlbin [/path/to/grml-debootstrap]: Set the grml-debootstrap path (default: grml-debootstrap).

Miscellaneous:
  --unsafe-io [true|false]        : Toggle unsafe IO options (default: false).
  --interactive [true|false]      : Force interactive (continue/retry menus) or non-interactive mode. Default: auto-detect (TTY on stdin and stdout, and not CI).
  --freedom [true|false]          : Choose between pure or impure builds (required for host builds).
  --tb [none|closed|open]         : Configure Tor Browser installation options (default: open).
  none: Do not install Tor Browser.
  closed: Abort build, fail closed if Tor Browser cannot be installed.
  open: Do not abort build, fail open if Tor Browser cannot be installed.

Please use the options as per your requirements. For any assistance, refer to the full documentation.
"
   elif [ "${cmd_name}" = 'derivative-update' ]; then
      printf '%s\n' "\
derivative-update

Syntax:
  (-t|--tag|-r|--ref ref)

Description:
  Updates the derivative-maker repository to the specified ref in a secure
  fashion. Imports the derivative-maker signing keys if they are not already
  imported into GPG. Fetches new commits and refs from the remote repository.

Parameters:
  -t|--tag                   : Check out the specified tag. Specify 'latest'
                               to check out the most recently created tag in
                               the repository.
  -r|--ref                   : Check out the specified branch.
  -u|--update-only           : No check out. Update git submodules only.
"
   fi
   exit 0
}

dist_build_one_parse_cmd() {
   ## Thanks to:
   ## http://mywiki.wooledge.org/BashFAQ/035

   args=("$@")

   if ! [ "${dist_build_source_run:-}" = "true" ]; then
      if [ "${#args[@]}" -eq 0 ]; then
         printf '%s\n' "${red}${bold}No option chosen! Use '--help'.${reset}"
         exit 1
      fi
   fi

   local build_machines_counter="0"
   local build_target_counter="0"

   ## Using export, so scripts run by run-parts (run by derivative-maker) can read
   ## these variables.

   ## Note, short and combined options such as '-tpu' are unsupported and not planned.
   while :
   do
       case "${1:-}" in
           -h | --help | -\?)
               if [ "${parse_cmd_was_sourced}" = 'true' ]; then
                   ## Script was sourced.
                   dist_build_one_script_help "$0"
               else
                   dist_build_one_script_help "derivative-maker"
               fi
               ;;
           --flavor)
               true "${cyan}INFO: --flavor ${2:-} chosen.${reset}"
               dist_build_flavor="${2:-}"
               export dist_build_flavor
               if [ "${dist_build_flavor:-}" = "whonix-gateway-lxqt" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-gateway-rpi" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-gateway-cli" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-workstation-lxqt" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-workstation-cli" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-custom-workstation" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-host-cli" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "whonix-host-lxqt" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "kicksecure-cli" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "kicksecure-lxqt" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "kicksecure-ci-tiny-do-not-use" ]; then
                  error_dangerous_option_maybe "kicksecure-ci-tiny-do-not-use"
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
               elif [ "${dist_build_flavor:-}" = "dist-installer-cli" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
                  export dist_build_installer_dist="true"
                  dist_build_image_upload_supported="true"
               elif [ "${dist_build_flavor:-}" = "source" ]; then
                  build_machines_counter="$(( ${build_machines_counter} + 1 ))"
                  export dist_build_source_run="true"
               else
                  parse_cmd_flavor_error
               fi
               shift 2
               ;;
           --type)
               true "${cyan}INFO: --type ${2:-} chosen.${reset}"
               if [ "${2:-}" = "host" ]; then
                  dist_build_type="host"
               elif [ "${2:-}" = "vm" ]; then
                  dist_build_type="vm"
               else
                  parse_cmd_type_error
               fi
               export dist_build_type
               shift 2
               ;;
           --target)
               true "${cyan}INFO: --target ${2:-} chosen.${reset}"
               ## dist_build_image_upload_supported must not be flipped back to
               ## false if one target set it to true.
               if [ "${2:-}" = "virtualbox" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_virtualbox="true"
                  dist_build_image_upload_supported="true"
                  implicit_dist_type vm "--target ${2:-}"
               elif [ "${2:-}" = "qcow2" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_qcow2="true"
                  dist_build_image_upload_supported="true"
                  implicit_dist_type vm "--target ${2:-}"
               elif [ "${2:-}" = "utm" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_raw="true"
                  export dist_build_utm="true"
                  [ "${dist_build_image_upload_supported:-}" = "true" ] || dist_build_image_upload_supported="false"
                  implicit_dist_type vm "--target ${2:-}"
               elif [ "${2:-}" = "iso" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_iso="true"
                  dist_build_image_upload_supported="true"
                  implicit_dist_type host "--target ${2:-}"
               elif [ "${2:-}" = "raw" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_raw="true"
                  [ "${dist_build_image_upload_supported:-}" = "true" ] || dist_build_image_upload_supported="false"
                  implicit_dist_type vm "--target ${2:-}"
               elif [ "${2:-}" = "root" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_install_to_root="true"
                  [ "${dist_build_image_upload_supported:-}" = "true" ] || dist_build_image_upload_supported="false"
               elif [ "${2:-}" = "windows" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_windows_installer="true"
                  dist_build_image_upload_supported="true"
                  implicit_dist_type vm "--target ${2:-}"
               elif [ "${2:-}" = "source" ]; then
                  build_target_counter="$(( ${build_target_counter} + 1 ))"
                  export dist_build_source_archive="true"
                  dist_build_image_upload_supported="true"
               else
                  parse_cmd_target_error
               fi
               shift 2
               ;;
           --fast)
               true "${cyan}INFO: --fast ${2:-} chosen.${reset}"
               if [ "${2:-}" = "1" ]; then
                  export dist_build_fast1="1"
               elif [ "${2:-}" = "2" ]; then
                  export dist_build_fast1="1"
                  export dist_build_fast2="1"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --fast are '1' and '2'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --vmram)
               export VMRAM="${2:-}"
               shift 2
               if [ "${VMRAM:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: You forgot to specify how much MB to use for --vmram.${reset}"
                  exit 1
               fi
               ;;
           --vram)
               export VRAM="${2:-}"
               shift 2
               if [ "${VRAM:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: You forgot to specify how much MB to use for --vram.${reset}"
                  exit 1
               fi
               ;;
           --vmsize)
               export VMSIZE="${2:-}"
               shift 2
               if [ "${VMSIZE:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: You forgot to specify how much GB to use for --vmsize.${reset}"
                  exit 1
               fi
               ;;
           --freshness)
               if [ "${2:-}" = "frozen" ]; then
                  export dist_build_apt_freshness="frozen"
                  true "${cyan}INFO: Using frozen sources, build should be reproducible.${reset}"
               elif [ "${2:-}" = "current" ]; then
                  export dist_build_apt_freshness="current"
                  true "${cyan}INFO: Using current sources.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --freshness are 'frozen' or 'current'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --build-slot)
               if [ -z "${2:-}" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --build-slot requires a non-empty name.${reset}"
                  exit 1
               fi
               case "${2}" in
                  -*|*[!A-Za-z0-9_-]*)
                     printf '%s\n' "${red}${bold}ERROR: --build-slot name '${2}' is invalid: use [A-Za-z0-9_-] only, no leading '-'.${reset}"
                     exit 1
                     ;;
               esac
               dist_build_slot="${2}"
               export dist_build_slot
               true "${cyan}INFO: --build-slot ${dist_build_slot} chosen.${reset}"
               shift 2
               ;;
           --connection)
               if [ "${2:-}" = "clearnet" ]; then
                  true "${cyan}INFO: Using clearnet apt sources.${reset}"
                  dist_build_sources_clearnet_or_onion="clearnet"
               elif [ "${2:-}" = "onion" ]; then
                  true "${cyan}INFO: Using onion apt sources.${reset}"
                  dist_build_sources_clearnet_or_onion="onion"
                  ## tb-updater
                  ## https://phabricator.whonix.org/T678
                  tb_onion=true
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --connection are 'clearnet' or 'onion'.${reset}"
                  exit 1
               fi
               export tb_onion
               export dist_build_sources_clearnet_or_onion
               shift 2
               ;;
           --release)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --release must not be empty.${reset}"
                  exit 1
               else
                  export BUILD_RELEASE="${2:-}"
                  true "${cyan}BUILD_RELEASE set to ${BUILD_RELEASE}.${reset}"
               fi
               shift 2
               ;;
           ## TODO
           --testing-frozen-sources)
               export dist_build_sources_list_primary="build_sources/debian_testing_frozen.sources"
               shift
               ;;
           --debug)
               ## Implemented in help-steps/pre. Cannot be implemented here, because it runs too late.
               shift
               ;;
           --arch)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --arch must not be empty.${reset}"
                  exit 1
               else
                  dist_build_target_arch="${2:-}"
                  true "${cyan}dist_build_target_arch set to ${dist_build_target_arch}.${reset}"
               fi
               shift 2
               ;;
           --policy-file)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --policy-file must not be empty.${reset}"
                  exit 1
               fi
               export sq_git_policy_file="${2:-}"
               true "${cyan}sq_git_policy_file set to ${sq_git_policy_file}.${reset}"
               shift 2
               ;;
           --cert-file)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --cert-file must not be empty.${reset}"
                  exit 1
               fi
               export openpgp_cert_file_user="${2:-}"
               true "${cyan}openpgp_cert_file_user set to ${openpgp_cert_file_user}.${reset}"
               shift 2
               ;;
           --initramfs)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --initramfs must not be empty.${reset}"
                  exit 1
               elif [ "${2:-}" = "none" ]; then
                  export BUILD_INITRAMFS_PKGS="none"
                  true "${cyan}BUILD_INITRAMFS_PKGS set to ${BUILD_INITRAMFS_PKGS}.${reset}"
               else
                  export BUILD_INITRAMFS_PKGS="${BUILD_INITRAMFS_PKGS:-} ${2:-}"
                  true "${cyan}BUILD_INITRAMFS_PKGS set to ${BUILD_INITRAMFS_PKGS}.${reset}"
               fi
               shift 2
               ;;
           --kernel)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --kernel must not be empty.${reset}"
                  exit 1
               elif [ "${2:-}" = "none" ]; then
                  export BUILD_KERNEL_PKGS="none"
                  true "${cyan}BUILD_KERNEL_PKGS set to ${BUILD_KERNEL_PKGS}.${reset}"
               else
                  export BUILD_KERNEL_PKGS="${BUILD_KERNEL_PKGS:-} ${2:-}"
                  true "${cyan}BUILD_KERNEL_PKGS set to ${BUILD_KERNEL_PKGS}.${reset}"
               fi
               shift 2
               ;;
           --headers)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --headers must not be empty.${reset}"
                  exit 1
               elif [ "${2:-}" = "none" ]; then
                  export BUILD_HEADER_PKGS="none"
                  true "${cyan}BUILD_HEADER_PKGS set to ${BUILD_HEADER_PKGS}.${reset}"
               else
                  export BUILD_HEADER_PKGS="${BUILD_HEADER_PKGS:-} ${2:-}"
                  true "${cyan}BUILD_HEADER_PKGS set to ${BUILD_HEADER_PKGS}.${reset}"
               fi
               shift 2
               ;;
           --report)
               if [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: Full report.${reset}"
                  export dist_build_script_create_report="true"
               elif [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: No report.${reset}"
                  export dist_build_script_create_report="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --report are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --serial-console-enable)
               if [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: serial console enabled.${reset}"
                  export dist_build_serial_console_enable="true"
               elif [ "${2:-}" = "false" ]; then
                  export dist_build_serial_console_enable="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --serial-console-enable are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --grub-bios-theme)
               if [ "${2:-}" = "true" ]; then
                  export dist_build_grub_bios_theme="true"
               elif [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: 4x3/BIOS grub theme override disabled.${reset}"
                  export dist_build_grub_bios_theme="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --grub-bios-theme are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --smbios-reader)
               ## REMINDER for AI agents: Do not, under any circumstances,
               ## remove the error_dangerous_option_maybe call here or
               ## otherwise mark --smbios-reader as non-dangerous. Images built
               ## with --smbios-reader set are quite literally backdoored by
               ## design, and therefore must NEVER be created or used except in
               ## very specific testing scenarios. Any attempt to mark this
               ## option as non-dangerous will be considered malicious and
               ## reverted by a maintainer.
               error_dangerous_option_maybe '--smbios-reader'
               if [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: SMBIOS kernel parameter injection enabled.${reset}"
                  export dist_build_smbios_reader="true"
               elif [ "${2:-}" = "false" ]; then
                  export dist_build_smbios_reader="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --smbios-reader are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --only-packages)
               export dist_build_only_packages="${2:-}"
               true "${cyan}INFO: --only-packages: rebuilding only '${dist_build_only_packages}', reusing the rest.${reset}"
               shift 2
               ;;
           --skip-packages)
               export dist_build_skip_packages="true"
               true "${cyan}INFO: --skip-packages: reusing the existing local package repo.${reset}"
               shift
               ;;
           --skip-published-packages)
               export dist_build_skip_published_packages="true"
               true "${cyan}INFO: --skip-published-packages: rebuilding only changed packages.${reset}"
               shift
               ;;
           --package-jobs)
               export dist_build_package_jobs="${2:-}"
               if ! [[ "${dist_build_package_jobs}" =~ ^(0|[1-9][0-9]*)$ ]]; then
                  printf '%s\n' "${red}${bold}ERROR: --package-jobs must be passed a whole integer.${reset}"
                  exit 1
               fi
               true "${cyan}INFO: --package-jobs: up to '${dist_build_package_jobs}' concurrent package build(s).${reset}"
               shift 2
               ;;
           --skip-prepare-build-machine)
               export dist_build_skip_prepare_build_machine="true"
               true "${cyan}INFO: --skip-prepare-build-machine: not preparing build machine.${reset}"
               shift
               ;;
           --skip-cowbuilder-setup)
               export dist_build_skip_cowbuilder_setup="true"
               true "${cyan}INFO: --skip-cowbuilder-setup: not building a new cowbuilder chroot.${reset}"
               shift
               ;;
           --reproducible-dist-build-version)
               error_dangerous_option_maybe '--reproducible-dist-build-version'
               export dist_build_version_reproducible="true"
               true "${cyan}INFO: --reproducible-dist-build-version: version will be normalized to the nearest tag.${reset}"
               shift
               ;;
           --reuse-cowbuilder-base)
               export dist_build_reuse_cowbuilder_base="true"
               true "${cyan}INFO: --reuse-cowbuilder-base: reusing an existing cowbuilder base if present.${reset}"
               shift
               ;;
           --base-image-role)
               case "${2:-}" in
                  create|consume)
                     export dist_build_base_image_role="${2}"
                     true "${cyan}INFO: --base-image-role set to: ${dist_build_base_image_role}${reset}"
                     ;;
                  *)
                     printf '%s\n' "${red}${bold}ERROR: --base-image-role must be 'create' or 'consume', got: '${2:-}'.${reset}"
                     exit 1
                     ;;
               esac
               shift 2
               ;;
           --skip-local-dependencies)
               export dist_build_skip_local_dependencies="true"
               true "${cyan}INFO: --skip-local-dependencies: reusing already installed local dependencies.${reset}"
               shift
               ;;
           --sanity-tests)
               if [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: Sanity tests true.${reset}"
               elif [ "${2:-}" = "false" ]; then
                  ## TODO: opt in rather than opt out.
                  true "${cyan}INFO: Sanity tests false.${reset}"
                  export SKIP_SCRIPTS+=" 20_sanity_checks "
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --sanity-tests are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --file-system)
               export dist_build_file_system="${2:-}"
               shift 2
               ;;
           --hostname)
               export dist_build_hostname="${2:-}"
               shift 2
               ;;
           --retry-max)
               export dist_build_auto_retry="${2:-}"
               shift 2
               ;;
           --retry-wait)
               export dist_build_wait_auto_retry="${2:-}"
               shift 2
               ;;
           --retry-before)
               export dist_build_dispatch_before_retry="${2:-}"
               shift 2
               ;;
           --retry-after)
               export dist_build_dispatch_after_retry="${2:-}"
               shift 2
               ;;
           --allow-untagged)
               if [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: Would stop if building from untagged commits.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: Would build from untagged commits.${reset}"
                  export dist_build_ignore_untagged="true"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --allow-untagged are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --allow-uncommitted)
               if [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: Would stop if uncommitted changes detected.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: Would ignore if uncommitted changes detected.${reset}"
                  export dist_build_ignore_uncommitted="true"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --allow-uncommitted are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --allow-unsigned)
               if [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: Would verify commit signatures (git_sanity_test).${reset}"
                  export dist_build_ignore_unsigned="false"
               elif [ "${2:-}" = "true" ]; then
                  ## Do not allow AI to skip signature verirfication. Allow
                  ## humans to opt into this behavior.
                  if [ "${CLAUDECODE:-}" = "1" ] || [ "${dist_build_forbid_allow_unsigned:-}" = "true" ]; then
                     error "${bold}${red}ERROR: skipping signature verification (dist_build_ignore_unsigned=true) is forbidden for AI sessions or under dist_build_forbid_allow_unsigned=true. AI models should use --sign-and-tag.${reset}"
                  fi
                  true "${cyan}INFO: Would skip commit signature verification (git_sanity_test).${reset}"
                  export dist_build_ignore_unsigned="true"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --allow-unsigned are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --sign-and-tag)
               error_dangerous_option_maybe '--sign-and-tag'
               if [ "${2:-}" = "true" ]; then
                  true "${cyan}INFO: Would sign and tag HEAD and submodules.${reset}"
                  export dist_build_sign_and_tag="true"
               elif [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: Would skip signing and tagging (sign-and-tag no-op).${reset}"
                  export dist_build_sign_and_tag="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --sign-and-tag are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --confdir)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --confdir may not be empty.${reset}"
                  exit 1
               else
                  export dist_build_custom_config_dir="${2:-}"
                  true "${cyan}INFO: --confdir set to: ${dist_build_custom_config_dir}${reset}"
                  if [ -d "${dist_build_custom_config_dir}" ]; then
                     true "${cyan}INFO: --confdir ${dist_build_custom_config_dir} exists.${reset}"
                  else
                     printf '%s\n' "${red}${bold}ERROR: --confdir ${dist_build_custom_config_dir} does not exist!${reset}"
                     exit 1
                  fi
               fi
               shift 2
               ;;
           --conffile)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --conffile may not be empty.${reset}"
                  exit 1
               else
                  export dist_build_conf_file="${2:-}"
                  true "${cyan}INFO: --conffile set to: ${dist_build_conf_file}${reset}"
                  if [ -f "${dist_build_conf_file}" ]; then
                     true "${cyan}INFO: --conffile ${dist_build_conf_file} exists.${reset}"
                  else
                     printf '%s\n' "${red}${bold}ERROR: --conffile ${dist_build_conf_file} does not exist!${reset}"
                     exit 1
                  fi
               fi
               shift 2
               ;;
           --grmlbin)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --grmlbin may not be empty.${reset}"
                  exit 1
               else
                  export dist_build_grml_bin="${2:-}"
                  true "${cyan}INFO: --grmlbin set to: ${dist_build_grml_bin}${reset}"
                  if [ -x "${dist_build_grml_bin}" ]; then
                     true "${cyan}INFO: --grmlbin ${dist_build_grml_bin} exists.${reset}"
                  else
                     printf '%s\n' "${red}${bold}ERROR: --grmlbin ${dist_build_grml_bin} is not executable!${reset}"
                     exit 1
                  fi
               fi
               shift 2
               ;;
           --tb)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --tb may not be empty.${reset}"
                  exit 1
               else
                  export anon_shared_inst_tb="${2:-}"
                  true "${cyan}INFO: --tb set to: ${anon_shared_inst_tb}${reset}"
               fi
               shift 2
               ;;
           --unsafe-io)
               if [ "${2:-}" = "false" ]; then
                  true "${cyan}INFO: Not using unsafe io.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  dist_build_unsafe_io="true"
                  export dist_build_unsafe_io
                  true "${cyan}INFO: Using unsafe io.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --unsafe-io are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --interactive)
               ## Also implemented in help-steps/pre, as this code runs too late.
               if [ "${2:-}" = "true" ]; then
                  dist_build_interactive="true"
                  export dist_build_interactive
                  true "${cyan}INFO: Interactive mode enabled.${reset}"
               elif [ "${2:-}" = "false" ]; then
                  dist_build_interactive="false"
                  export dist_build_interactive
                  true "${cyan}INFO: Interactive mode disabled.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --interactive are 'true' or 'false'.${reset}"
                  exit 1
               fi
               shift 2
               ;;
           --repo)
               if [ "${2:-}" = "false" ]; then
                  build_remote_repo_enable="false"
                  true "${cyan}INFO: will ${under}not${eunder} enable remote repository.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  build_remote_repo_enable="true"
                  true "${cyan}INFO: will ${under}enable${eunder} remote repository.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --repo are 'true' or 'false'.${reset}"
                  exit 1
               fi
               export build_remote_repo_enable
               shift 2
               ;;
           --remote-derivative-packages)
               if [ "${2:-}" = "false" ]; then
                  build_remote_derivative_pkgs="false"
                  true "${cyan}INFO: will ${under}not${eunder} use remote derivative packages.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  build_remote_derivative_pkgs="true"
                  true "${cyan}INFO: will ${under}use${eunder} remote derivative packages.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --remote-derivative-packages are 'true' or 'false'.${reset}"
                  exit 1
               fi
               export build_remote_derivative_pkgs
               shift 2
               ;;
           --dry-run)
               if [ "${2:-}" = "false" ]; then
                  build_dry_run="false"
                  true "${cyan}INFO: ${under}not${eunder} --dry-run${reset}"
               elif [ "${2:-}" = "true" ]; then
                  build_dry_run="true"
                  true "${cyan}INFO: ${under}--dry-run${eunder}${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --dry-run are 'true' or 'false'.${reset}"
                  exit 1
               fi
               export build_dry_run
               shift 2
               ;;
           --unsupported-os)
               ## Bypass the build-steps.d/1100_sanity-tests
               ## check-operating-system-version error. Useful when
               ## smoke-testing the build pipeline on a non-Debian host
               ## (developer laptops, AI dev sandboxes, GitHub
               ## ubuntu-latest CI runners, ...) where the rest of the
               ## stack still works. Real builds should never set this:
               ## the package versions and APT codename will not match
               ## and the resulting image will be subtly broken.
               if [ "${2:-}" = "true" ]; then
                  build_unsupported_os="true"
                  true "${cyan}INFO: --unsupported-os true: bypassing OS-version sanity check (dev / AI / CI use only).${reset}"
               elif [ "${2:-}" = "false" ]; then
                  build_unsupported_os="false"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --unsupported-os are 'true' or 'false'.${reset}"
                  exit 1
               fi
               export build_unsupported_os
               shift 2
               ;;
           --freedom)
               if [ "${2:-}" = "false" ]; then
                  build_freedom_only="false"
                  true "${cyan}INFO: will include nonfreedom software packages.${reset}"
               elif [ "${2:-}" = "true" ]; then
                  build_freedom_only="true"
                  true "${cyan}INFO: will include Freedom Software packages only.${reset}"
               else
                  printf '%s\n' "${red}${bold}ERROR: supported options for --freedom are 'true' or 'false'.${reset}"
                  exit 1
               fi
               export build_freedom_only
               shift 2
               ;;
            -t|--tag)
               target_tag="${2:-}"
               shift 2
               ;;
            -r|--ref)
               target_ref="${2:-}"
               shift 2
               ;;
            -u|--update-only)
               update_only="true"
               shift 1
               ;;
           --function)
               if [ "${2:-}" = "" ]; then
                  printf '%s\n' "${red}${bold}ERROR: --function may not be empty.${reset}"
                  exit 1
               else
                  ## TODO-HUMAN-DEVELOPER-ONLY: This export was supposed to fix
                  ## dm-get-tor-from-tpo-repo, but it doesn't look like it will
                  ## do anything and a different change may have fixed the bug.
                  ## Test.
                  #export FUNCTION="${2:-}"
                  FUNCTION="${2:-}"
                  true "${cyan}INFO: --function set to ${FUNCTION}${reset}"
               fi
               shift 2
               ;;
           --)
               shift
               break
               ;;
           -*)
               if ! [ "${dist_build_source_run:-}" = "true" ]; then
                  printf '%s\n' "${red}${bold}unknown option (1): '${1:-}'${reset}"
                  exit 1
               fi
               break
               ;;
           *)
               if [ "${1:-}" = "" ]; then
                  true
               else
                  if ! [ "${dist_build_source_run:-}" = "true" ]; then
                     printf '%s\n' "${red}${bold}unknown option (2): '${1:-}'${reset}"
                     exit 1
                  fi
               fi
               break
               ;;
       esac
   done

   [[ -v target_tag ]] || target_tag=""
   [[ -v target_ref ]] || target_ref=""
   [[ -v update_only ]] || update_only=""
   [[ -v architecture_valid ]] || architecture_valid=""

   if [ "${dist_build_target_arch:-}" = "" ]; then
      if [ "${dist_build_source_archive:-}" = "true" ]; then
         architecture_valid=true
      elif [ "${dist_build_source_run:-}" = "true" ]; then
         architecture_valid=true
      else
         error "\
${red}${bold}Missing '--arch' option!

Choosing a target architecture is mandatory! Pick one of the following.${reset}
${architecture_all_list[*]}

For example for Intel CPUs (yes, this is correct!):
${bold}--arch amd64${reset}

For example for AMD CPUs:
${bold}--arch amd64${reset}

For ARM CPUs:
${bold}--arch arm64${reset}
${reset}"
      fi
   elif [ "${dist_build_target_arch:-}" = "i386" ]; then
      error "\
${red}${bold}Architecture 'i386' is no longer supported!

Debian Trixie and higher dropped support for full i386 installations.
See: https://www.debian.org/releases/bookworm/i386/release-notes/ch-information.en.html
Build for '--arch amd64' or '--arch arm64' instead.${reset}"
   elif [ "${dist_build_target_arch:-}" = "amd64" ] || [ "${dist_build_target_arch:-}" = "arm64" ]; then
      [ -n "${virtualbox_supported_architecture:-}" ] || virtualbox_supported_architecture="true"
   else
      [ -n "${virtualbox_supported_architecture:-}" ] || virtualbox_supported_architecture="false"
   fi

   if [ "${dist_build_virtualbox:-}" = "true" ]; then
      if [ "${virtualbox_supported_architecture:-}" = "false" ]; then
         error "\
${red}${bold}VirtualBox architecture support test failed!
virtualbox_supported_architecture: '${virtualbox_supported_architecture}'
dist_build_target_arch is not 'amd64' or 'arm64'.
dist_build_target_arch is '${dist_build_target_arch:-}'.
You cannot build '--target virtualbox' if not using '--arch amd64' or '--arch arm64'.
This is because at time of writing this check, VirtualBox supports 'amd64' and 'arm64' only.
Should VirtualBox meanwhile support other architectures such as 'ppc64el', then this check can simply be removed in the source code.${reset}"
      fi
   fi

   for architecture_all_item in "${architecture_all_list[@]}"; do
      if [ "${dist_build_target_arch:-}" = "${architecture_all_item}" ]; then
        architecture_valid=true
        break
      fi
   done

   if ! [ "${architecture_valid:-}" = "true" ]; then
      error "\
${red}${bold}Invalid '--arch' option!${reset}

Chosen architecture ${red}${under}'${dist_build_target_arch}'${reset} is invalid!

Choosing a target architecture is mandatory! Pick one of the following.${reset}
${architecture_all_list[*]}
${reset}"
   fi

   export BUILD_INITRAMFS_PKGS

   true "${cyan}INFO: dist_build_target_arch    (--arch): ${dist_build_target_arch:-}${reset}"
   true "${cyan}INFO: BUILD_KERNEL_PKGS       (--kernel): ${BUILD_KERNEL_PKGS:-}${reset}"
   true "${cyan}INFO: BUILD_HEADER_PKGS      (--headers): ${BUILD_HEADER_PKGS:-}${reset}"

   if [ "${dist_build_sources_clearnet_or_onion:-}" = "" ]; then
      true "${cyan}${bold}INFO: No --connection type 'clearnet' or 'onion' has been chosen. \
Using default dist_build_sources_clearnet_or_onion=${under}clearnet${eunder}.
(Alternative value would be 'onion'.)${reset}"
      export dist_build_sources_clearnet_or_onion="clearnet"
   fi

   ## If there are input files (for example) that follow the options, they
   ## will remain in the "$@" positional parameters.

   if [ "${build_machines_counter:-0}" -gt "1" ]; then
      printf '%s\n' "${red}${bold}You cannot use --flavor multiple times!${reset}"
      exit 1
   fi

   if [ "${build_machines_counter:-0}" -le "0" ]; then
      if [ "${dist_build_one_parsed:-}" = "true" ]; then
         true
      elif [ "${dist_build_source_run:-}" = "true" ] ; then
         true
      else
         parse_cmd_flavor_error
      fi
   fi

   if [ "${dist_build_install_to_root:-}" = "true" ]; then
      true
   elif [ "${dist_build_virtualbox:-}" = "true" ]; then
      true
   elif [ "${dist_build_qcow2:-}" = "true" ]; then
      true
   elif [ "${dist_build_utm:-}" = "true" ]; then
      true
   elif [ "${dist_build_raw:-}" = "true" ]; then
      true
   elif [ "${dist_build_installer_dist:-}" = "true" ]; then
      true
   elif [ "${dist_build_windows_installer:-}" = "true" ]; then
      true
   elif [ "${dist_build_iso:-}" = "true" ]; then
      true
   elif [ "${dist_build_source_run:-}" = "true" ]; then
      true
   elif [ "${dist_build_source_archive:-}" = "true" ]; then
      true
   else
      parse_cmd_target_error
   fi

   if [ "${dist_build_one_parsed:-}" = "true" ]; then
      true
   elif [ "${dist_build_source_run:-}" = "true" ]; then
      true
   elif [ "${dist_build_source_archive:-}" = "true" ]; then
      true
   elif [ -z "${dist_build_apt_freshness:-}" ]; then
      printf '%s\n' "${red}${bold}You must add '--freshness frozen' or '--freshness current'.${reset}"
      exit 1
   fi

   if [ "${dist_build_install_to_root:-}" = "true" ]; then
      if [ "${build_target_counter:-0}" -gt "1" ]; then
         printf '%s\n' "${red}${bold}You can not combine --target root with other targets.${reset}"
         exit 1
      fi
   fi

   ## --target and --type compatibility is intentionally not validated.
   if [ "${dist_build_type:-}" = "vm" ]; then
      true
   elif [ "${dist_build_type:-}" = "host" ]; then
      true
   elif [ "${dist_build_source_run:-}" = "true" ]; then
      true
   elif [ "${dist_build_source_archive:-}" = "true" ]; then
      true
   elif [ "${dist_build_installer_dist:-}" = "true" ]; then
      true
   else
      printf '%s\n' "${red}${bold}You must add either:
'--type vm'
'--type host'
${reset}"
      exit 1
   fi

   if [ "${dist_build_iso:-}" = "true" ]; then
      ## Make sure the target arch is something compatible with ISO boot.
      if ! [[ "${dist_build_target_arch}" =~ ^(amd64|arm64)$ ]]; then
         error "\
${red}${bold}ISOs cannot be built for architecture '${dist_build_target_arch:-}'!${reset}

Supported ISO architectures: amd64, arm64.
Build '--target iso' with a supported arch, or choose a different '--target'.${reset}"
      fi
      if [ "${dist_build_target_arch:-}" = "amd64" ]; then
         parse_cmd_freedom_versus_nonfreedom_firmware_choice_check
      fi
   fi

   if [ "${CI:-}" = "true" ]; then
      ## Real cross-builds time out in GitHub's CI infra, so turn them into
      ## dummy builds.
      ci_host_architecture="$(dpkg --print-architecture)"
      if [ "${dist_build_flavor:-}" = "kicksecure-cli" ]; then
         true "${BASH_SOURCE[0]} INFO: CI='${CI:-}' and dist_build_flavor='${dist_build_flavor:-}', building real image."
      elif [ "${dist_build_target_arch:-}" = "${ci_host_architecture}" ]; then
         true "${BASH_SOURCE[0]} INFO: CI='${CI:-}', native build, building real image."
      else
         ## equivalent of '--dry-run true'
         build_dry_run="true"
         export build_dry_run
         true "${BASH_SOURCE[0]} INFO: CI='${CI:-}', cross-build of dist_build_flavor='${dist_build_flavor:-}', therefore setting: build_dry_run='${build_dry_run:-}'"
      fi
   fi
}

if [ "${parse_cmd_was_sourced}" = 'true' ]; then
   true "INFO $0: script was sourced."
else
   true "INFO $0: script was executed."
   dist_build_one_parse_cmd "$@"
fi
