From 5b2cd04c3077fa124e77422be52d0919d4576bf0 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Sun, 10 Oct 2021 14:44:11 +0000 Subject: [PATCH 1/7] Get image to build; alez command on booted image still has zsh permissions error --- Dockerfile | 2 +- profiledef.sh | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 profiledef.sh diff --git a/Dockerfile b/Dockerfile index fd7757e..927cfbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,4 +36,4 @@ RUN ln -s "/usr/local/share/ALEZ/alez.sh" "${ALEZ_BUILD_DIR}/iso/airootfs/usr/lo VOLUME "${ALEZ_BUILD_DIR}/iso/out" WORKDIR "${ALEZ_BUILD_DIR}/iso" -CMD ["/usr/bin/mkarchiso"] +CMD ["/usr/bin/mkarchiso", "./"] diff --git a/profiledef.sh b/profiledef.sh new file mode 100644 index 0000000..4ef923e --- /dev/null +++ b/profiledef.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 + +iso_name="archlinux" +iso_label="ARCH_$(date +%Y%m)" +iso_publisher="Arch Linux " +iso_application="Arch Linux Live/Rescue CD" +iso_version="$(date +%Y.%m.%d)" +install_dir="arch" +bootmodes=() From fcae25d824226f70170e37aba840a05ad4f50d01 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Sun, 10 Oct 2021 20:01:05 +0000 Subject: [PATCH 2/7] Add basic test script that builds and runs the Docker image and then runs the resulting ISO using qemu (new deps are listed in the test file) --- .gitignore | 1 + run_archiso | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test | 8 +++ 3 files changed, 182 insertions(+) create mode 100755 run_archiso create mode 100755 test diff --git a/.gitignore b/.gitignore index e2e7327..78c1042 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /out +/iso_out diff --git a/run_archiso b/run_archiso new file mode 100755 index 0000000..6ddce15 --- /dev/null +++ b/run_archiso @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2020 David Runge +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# A simple script to run an archiso image using qemu. The image can be booted +# using BIOS or UEFI. +# +# Requirements: +# - qemu +# - edk2-ovmf (when UEFI booting) + + +set -eu + +print_help() { + local usagetext + IFS='' read -r -d '' usagetext < 0 )); then + while getopts 'abc:dhi:suv' flag; do + case "$flag" in + a) + accessibility='on' + ;; + b) + boot_type='bios' + ;; + c) + oddimage="$OPTARG" + ;; + d) + mediatype='hd' + ;; + h) + print_help + exit 0 + ;; + i) + image="$OPTARG" + ;; + u) + boot_type='uefi' + ;; + s) + secure_boot='on' + ;; + v) + display='none' + qemu_options+=(-vnc 'vnc=0.0.0.0:0,vnc=[::]:0') + ;; + *) + printf '%s\n' "Error: Wrong option. Try 'run_archiso -h'." + exit 1 + ;; + esac + done +else + print_help + exit 1 +fi + +check_image +run_image diff --git a/test b/test new file mode 100755 index 0000000..bb290c5 --- /dev/null +++ b/test @@ -0,0 +1,8 @@ +# prereqs: qemu edk2-ovmf docker +# also make sure you have VT-x emulation on if you're running this inside a VM + +docker build -t aleztest . + +docker run -it --rm --privileged -v $(realpath ./iso_out):/opt/alez/iso/out aleztest + +./run_archiso -u -i ./iso_out/archlinux-*.iso From 5b48a9a477dde7d11d6da3491548d520937a6c10 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Thu, 21 Oct 2021 18:42:09 +0000 Subject: [PATCH 3/7] Added debug/test-mode switches and utility functions to provide additional output when debugging or testing. Added missing 'dialog' dependency. Installed archlinux-keyring dependency as part of key initialization --- alez.sh | 138 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 109 insertions(+), 29 deletions(-) diff --git a/alez.sh b/alez.sh index 718c358..1b3aceb 100755 --- a/alez.sh +++ b/alez.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # shellcheck disable=SC2015 # Arch Linux Easy ZFS (ALEZ) installer 1.2 @@ -19,6 +19,20 @@ version=1.2 RED='\033[0;31m' NC='\033[0m' # No Color +# To make bash booleans "saner" +# Anything set to these values will be expected to be used like so: +# value=$true +# if [ $value ]; then ... +# if [ ! $value ]; then ... +# or +# if [ $value1 ] && [ $value2 ]; then ... +# or +# [ $value ] && code_that_only_runs_if_value_is_true +# or +# [ $value ] || code_that_only_runs_if_value_is_false +false= +true=0 + installdir="/mnt" archzfs_pgp_key="F75D9D76" zroot="zroot" @@ -29,11 +43,11 @@ repo_remote="danboid/ALEZ" HEIGHT=0 WIDTH=0 -show_path=false +show_path=$false declare -a base_packages base_packages=( - 'base' 'nano' 'linux-firmware' 'man-db' 'man-pages' 'vi' 'less' + 'base' 'nano' 'linux-firmware' 'man-db' 'man-pages' 'vi' 'less' 'dialog' ) declare -a zpool_bios_features @@ -47,6 +61,30 @@ zpool_bios_features=( 'feature@userobj_accounting=disabled' ) +# debug/test IO mode switches +# The idea is that output normally dumped to /dev/null in prod +# might want to be visibled somehow in a dev or test env. +STDOUT=/dev/stdout +STDERR=/dev/stderr +DEVNULL=/dev/null +IO_OUT=$STDOUT +IO_ERR=$STDERR +IO_NULL=$DEVNULL +# redirect all IO output to stdout in debug or test env mode +PROD=$true +if [ $DEBUG ] || [ $TEST ]; then + PROD=$false +fi + +if [ ! $PROD ]; then + IO_OUT=$STDOUT + IO_ERR=$STDOUT + IO_NULL=$STDOUT +fi + +# Echo to standard error if not in prod, otherwise do nothing (useful for debugging) +errcho() { [ ! $PROD ] && cat <<< "$@" 1>&2; } + check_latest() { # If repo doesn't exist, skip check [ -d "${repo_directory}" ] || return 0 @@ -72,7 +110,7 @@ check_latest() { } check_internet() { - ping -c 1 archlinux.org &> /dev/null || return 1 + ping -c 1 archlinux.org &>"${IO_NULL}" || return 1 return 0 } @@ -85,7 +123,7 @@ unmount_cleanup() { { umount -R "${installdir}" || : ; zfs umount -a && zpool export "${zroot}" || : ; - } &> /dev/null + } &>"${IO_NULL}" } bootloader_message() { @@ -123,7 +161,7 @@ lsdsks() { update_parts() { # shellcheck disable=SC2046 - mapfile -t partids < <(ls /dev/disk/by-id/* $(${show_path} && ls /dev/vd* || : ;)) + mapfile -t partids < <(ls /dev/disk/by-id/* $( [ $show_path ] && ls /dev/vd* || : ;)) ptcount=${#partids[@]} } @@ -152,9 +190,9 @@ lsparts() { } zap_partition(){ - vgchange -an &> /dev/null - mdadm --zero-superblock --force "${1}" &> /dev/null - sgdisk --zap-all "${1}" &> /dev/null + vgchange -an &>"${IO_NULL}" + mdadm --zero-superblock --force "${1}" &>"${IO_NULL}" + sgdisk --zap-all "${1}" &>"${IO_NULL}" } bios_partitioning(){ @@ -225,7 +263,7 @@ get_matching_kernel() { mkdir -p "${pkgdir}" { pushd "${pkgdir}" && wget "${url}" && popd - } &> /dev/null + } &>"${IO_NULL}" chrun "pacman -U --noconfirm /${pkg}" && rm "${pkgdir}/${pkg}" @@ -235,14 +273,14 @@ get_matching_kernel() { } refresh_mirrors() { - pacman -Sy --noconfirm &> /dev/null + pacman -Sy --noconfirm &>"${IO_NULL}" - if hash reflector 2> /dev/null; then + if hash reflector 2>"${IO_NULL}"; then { echo "Refreshing mirrorlist" reflector --verbose --latest 25 \ --sort rate --save /etc/pacman.d/mirrorlist || : - } 2> /dev/null | dialog --progressbox ${HEIGHT} ${WIDTH} + } 2>"${IO_NULL}" | dialog --progressbox ${HEIGHT} ${WIDTH} fi } @@ -255,12 +293,12 @@ install_arch(){ else pacstrap "${installdir}" linux-headers linux "${base_packages[@]}" fi - } 2> /dev/null + } 2>"${IO_NULL}" chrun "if ! pacman-key -r F75D9D76; then pacman-key -r F75D9D76 --keyserver hkp://pool.sks-keyservers.net:80; fi && pacman-key --lsign-key F75D9D76" \ - "Adding Arch ZFS repo key in chroot..." 2> /dev/null + "Adding Arch ZFS repo key in chroot..." 2>"${IO_NULL}" echo "Add fstab entries..." fstab_output="$(genfstab -U "${installdir}")" @@ -290,7 +328,7 @@ install_arch(){ else chrun "pacman -Sy; pacman -S --noconfirm zfs-linux" "Installing ZFS in chroot..." fi - } 2> /dev/null + } 2>"${IO_NULL}" echo -e "Enable systemd ZFS service...\n" chrun "systemctl enable zfs.target" @@ -313,7 +351,7 @@ menuentry \"Arch Linux ZFS\" {\n\ } install_grub(){ - chrun "grub-install /dev/${disks[${1}]}" "Installing GRUB to /dev/${disks[${1}]}..." 2> /dev/null + chrun "grub-install /dev/${disks[${1}]}" "Installing GRUB to /dev/${disks[${1}]}..." 2>"${IO_NULL}" } install_grub_efi(){ @@ -325,7 +363,7 @@ install_grub_efi(){ # Install GRUB EFI chrun "grub-install --target=x86_64-efi --efi-directory=${1} --bootloader-id=GRUB" \ "Installing grub-efi to ${1}" - } 2> /dev/null + } 2>"${IO_NULL}" } gen_sdboot_entry(){ @@ -338,7 +376,7 @@ EOF } install_sdboot(){ - chrun "bootctl --path=${1} install" "Installing systemd-boot to ${1}" 2> /dev/null + chrun "bootctl --path=${1} install" "Installing systemd-boot to ${1}" 2>"${IO_NULL}" mkdir -p "${installdir}/${1}/loader/entries" if [[ "${kernel_type}" =~ ^(l|L)$ ]]; then gen_sdboot_entry "${1}" "linux-lts" @@ -429,18 +467,23 @@ fetch_archzfs_key() { pacman-key --lsign-key "${archzfs_pgp_key}" && return 0 fi done - } &> /dev/null + } &>"${IO_NULL}" return 1 } init_keyring() { declare -a keyservers=( - 'hkp://pool.sks-keyservers.net:80' - # 'hkp://pgp.mit.edu:80' # Replace with working keyservers + 'hkps://keyserver.ubuntu.com' + 'hkps://pgp.mit.edu' + 'hkp://pgp.mit.edu:80' + # SKS keyservers are permanently down due to GDPR filings + # 'hkp://pool.sks-keyservers.net:80' # 'hkp://ipv4.pool.sks-keyservers.net:80' ) - pacman-key --init &> /dev/null + pacman -S --noconfirm archlinux-keyring + pacman-key --init &>"${IO_NULL}" + pacman-key --populate archlinux { # Try default keyserver first @@ -448,7 +491,7 @@ init_keyring() { for ks in "${keyservers[@]}"; do pacman-key --refresh-keys --keyserver "${ks}" && return 0 done - } &>/dev/null + } &>"${IO_NULL}" return 1 } @@ -521,7 +564,7 @@ fi # Check if any VirtIO devices exist if lsblk | grep -E 'vd.*disk'; then - [ -d /dev/disk/by-path/ ] && show_path=true + [ -d /dev/disk/by-path/ ] && show_path=$true fi # No frills GPT partitioning @@ -585,20 +628,36 @@ while dialog --clear --title "New zpool?" --yesno "${msg}" $HEIGHT $WIDTH; do partinfo="$(get_parts)" if [ "$zpconf" == "s" ]; then + errcho "zpconf=s" msg="Select a partition.\n\nIf you used alez to create your partitions,\nyou likely want the one ending with -part2" # shellcheck disable=SC2086 zps=$(dialog --stdout --clear --title "Choose partition" \ --menu "${msg}" $HEIGHT $WIDTH "$(( 2 + ptcount))" ${partinfo}) if [[ "${install_type}" =~ ^(b|B)$ ]]; then # shellcheck disable=SC2046 + errcho "zpconf=s" + errcho "features=$(print_features)" + errcho "zroot=${zroot}" + errcho "zps=$zps" + errcho "partids=${partids[$zps]}" + errcho "install_type=${install_type}" + errcho "zpool create -f -d -m none -o ashift=12 $(print_features) ${zroot} ${partids[$zps]}" zpool create -f -d -m none -o ashift=12 $(print_features) "${zroot}" "${partids[$zps]}" else + errcho "zpconf=s" + errcho "features=$(print_features)" + errcho "zroot=${zroot}" + errcho "zps=$zps" + errcho "partids=${partids[$zps]}" + errcho "install_type=${install_type}" + errcho "zpool create -f -d -m none -o ashift=12 ${zroot} ${partids[$zps]}" zpool create -f -d -m none -o ashift=12 "${zroot}" "${partids[$zps]}" fi dialog --title "Success" --msgbox "Created a single disk zpool with ${partids[$zps]}...." ${HEIGHT} ${WIDTH} break elif [ "$zpconf" == "m" ]; then # shellcheck disable=SC2086 + errcho "zpconf=m" zp1=$(dialog --stdout --clear --title "First zpool partition" \ --menu "Select the number of the first partition" $HEIGHT $WIDTH "$(( 2 + ptcount ))" ${partinfo}) # shellcheck disable=SC2086 @@ -608,10 +667,26 @@ while dialog --clear --title "New zpool?" --yesno "${msg}" $HEIGHT $WIDTH; do echo "Creating a mirrored zpool..." if [[ "${install_type}" =~ ^(b|B)$ ]]; then # shellcheck disable=SC2046 + errcho "zpconf=m" + errcho "features=$(print_features)" + errcho "zroot=${zroot}" + errcho "zp1=$zp1" + errcho "zp2=$zp2" + errcho "partids=${partids[$zp1]},${partids[$zp2]}" + errcho "install_type=${install_type}" + errcho "zpool create ${zroot} mirror -f -d -m none -o ashift=12 $(print_features) ${partids[$zp1]} ${partids[$zp2]}" zpool create "${zroot}" mirror -f -d -m none \ -o ashift=12 \ $(print_features) "${partids[$zp1]}" "${partids[$zp2]}" else + errcho "zpconf=m" + errcho "features=$(print_features)" + errcho "zroot=${zroot}" + errcho "zp1=$zp1" + errcho "zp2=$zp2" + errcho "partids=${partids[$zp1]},${partids[$zp2]}" + errcho "install_type=${install_type}" + errcho "zpool create ${zroot} mirror -f -d -m none -o ashift=12 ${partids[$zp1]} ${partids[$zp2]}" zpool create "${zroot}" mirror -f -d -m none -o ashift=12 "${partids[$zp1]}" "${partids[$zp2]}" fi dialog --title "Success" --msgbox "Created a mirrored zpool with ${partids[$zp1]} ${partids[$zp2]}...." ${HEIGHT} ${WIDTH} @@ -625,7 +700,7 @@ done zfs create -o mountpoint=none "${zroot}"/data zfs create -o mountpoint=legacy "${zroot}"/data/home - { zfs create -o mountpoint=/ "${zroot}"/ROOT/default || : ; } &> /dev/null + { zfs create -o mountpoint=/ "${zroot}"/ROOT/default || : ; } &>"${IO_NULL}" # GRUB only datasets if [[ "${bootloader}" =~ ^(g|G)$ ]]; then @@ -678,6 +753,7 @@ if [[ "${install_type}" =~ ^(u|U)$ ]]; then $HEIGHT $WIDTH "$(( 2 + ptcount))" ${partinfo}) efi_partition="${partids[$esp]}" + errcho "mkfs.fat -F 32 ${efi_partition}" mkfs.fat -F 32 "${efi_partition}"| dialog --progressbox 10 70 mkdir -p "${installdir}${esp_mountpoint}" "${installdir}/boot" @@ -691,26 +767,30 @@ fi dialog --title "Begin install?" --msgbox "Setup complete, begin install?" ${HEIGHT} ${WIDTH} +errcho "Initializing keyring..." if ! init_keyring; then dialog --title "Installation error" \ --msgbox "ERROR: Failed to initialize keyring" ${HEIGHT} ${WIDTH} exit 1 fi +errcho "Fetching archzfs key..." if ! fetch_archzfs_key; then dialog --title "Installation error" \ --msgbox "ERROR: Failed to fetch archzfs key" ${HEIGHT} ${WIDTH} exit 1 fi +errcho "Refreshing mirrors..." refresh_mirrors +errcho "Installing arch..." install_arch | dialog --progressbox 30 70 # Install GRUB BIOS if [[ "${install_type}" =~ ^(b|B)$ ]]; then - chrun "pacman -S --noconfirm grub os-prober" "Installing GRUB in chroot..." 2> /dev/null | dialog --progressbox 30 70 + chrun "pacman -S --noconfirm grub os-prober" "Installing GRUB in chroot..." 2>"${IO_NULL}" | dialog --progressbox 30 70 add_grub_entry @@ -748,7 +828,7 @@ else else install_grub_efi "${esp_mountpoint}" fi - } 2> /dev/null | dialog --progressbox 30 70 + } 2>"${IO_NULL}" | dialog --progressbox 30 70 fi { @@ -758,7 +838,7 @@ fi else chrun "mkinitcpio -p linux" fi -} 2> /dev/null | dialog --progressbox 30 70 +} 2>"${IO_NULL}" | dialog --progressbox 30 70 unmount_cleanup From 6c4aaa785490228ae6be4fa576d0c19ed3eb33bf Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Thu, 21 Oct 2021 19:38:38 +0000 Subject: [PATCH 4/7] Forgot to remove all sks keyserver references in the code! Thaaanks, GDPR... --- alez.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/alez.sh b/alez.sh index 1b3aceb..700cf56 100755 --- a/alez.sh +++ b/alez.sh @@ -296,7 +296,7 @@ install_arch(){ } 2>"${IO_NULL}" chrun "if ! pacman-key -r F75D9D76; then - pacman-key -r F75D9D76 --keyserver hkp://pool.sks-keyservers.net:80; + pacman-key -r F75D9D76 --keyserver hkps://keyserver.ubuntu.com; fi && pacman-key --lsign-key F75D9D76" \ "Adding Arch ZFS repo key in chroot..." 2>"${IO_NULL}" @@ -452,8 +452,11 @@ define() { fetch_archzfs_key() { declare -a keyservers=( - 'hkp://pool.sks-keyservers.net:80' - # 'hkp://pgp.mit.edu:80' # Replace with working keyservers + 'hkps://keyserver.ubuntu.com' + 'hkps://pgp.mit.edu' + 'hkp://pgp.mit.edu:80' + # SKS keyservers are permanently down due to GDPR filings + # 'hkp://pool.sks-keyservers.net:80' # 'hkp://ipv4.pool.sks-keyservers.net:80' ) From d4bd32011f61a3c5de0c240f801beb6a1f861a59 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Sat, 23 Oct 2021 16:21:40 +0000 Subject: [PATCH 5/7] Added ability to mount a virtual disk to qemu ISO runner VM. Added bash strict mode switches. Fixed bugs. alez still fails on partition selection when testing this way, though... possibly idiosyncratic disk naming in qemu that the script doesn't recognize, still investigating... --- Dockerfile | 8 +++++--- alez.sh | 11 +++++++---- run_archiso | 11 +++++++++-- test | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index 927cfbe..9e1d6c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,11 +3,11 @@ FROM archlinux:base ARG ALEZ_BUILD_DIR='/opt/alez' ARG ARCHZFS_KEY='F75D9D76' -RUN pacman -Syu --noconfirm --needed base base-devel git archiso reflector curl wget +RUN pacman -Syu --noconfirm --needed base base-devel git archiso reflector curl wget dialog RUN mkdir ~/.gnupg && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf RUN pacman-key --init && pacman-key --populate archlinux && \ - if ! pacman-key -r "${ARCHZFS_KEY}"; then pacman-key -r "${ARCHZFS_KEY}" --keyserver hkp://pool.sks-keyservers.net:80; fi && pacman-key --lsign-key "${ARCHZFS_KEY}" + if ! pacman-key -r "${ARCHZFS_KEY}"; then pacman-key -r "${ARCHZFS_KEY}" --keyserver hkps://keyserver.ubuntu.com; fi && pacman-key --lsign-key "${ARCHZFS_KEY}" RUN mkdir -p "${ALEZ_BUILD_DIR}" && \ cp -r /usr/share/archiso/configs/releng "${ALEZ_BUILD_DIR}/iso" && \ @@ -23,7 +23,7 @@ RUN sed -i '/^\[core\]/i [archzfs]\n\ Server = http://archzfs.com/$repo/x86_64\n' \ "${ALEZ_BUILD_DIR}/iso/pacman.conf" -RUN printf 'git\narchzfs-linux\nreflector\nwget\nlinux\nlinux-firmware\ndhcpcd\nless\nmdadm' >> \ +RUN printf 'git\narchzfs-linux\nreflector\nwget\nlinux\nlinux-firmware\ndhcpcd\nless\nmdadm\ndialog' >> \ "${ALEZ_BUILD_DIR}/iso/packages.x86_64" COPY motd "${ALEZ_BUILD_DIR}/iso/airootfs/etc/" @@ -31,6 +31,8 @@ COPY motd "${ALEZ_BUILD_DIR}/iso/airootfs/etc/" # Copy in current directory to allow git tag checking COPY . "${ALEZ_BUILD_DIR}/iso/airootfs/usr/local/share/ALEZ" +COPY alez.sh "/usr/local/share/ALEZ/alez.sh" +RUN chmod u+x "/usr/local/share/ALEZ/alez.sh" RUN ln -s "/usr/local/share/ALEZ/alez.sh" "${ALEZ_BUILD_DIR}/iso/airootfs/usr/local/bin/alez" VOLUME "${ALEZ_BUILD_DIR}/iso/out" diff --git a/alez.sh b/alez.sh index 700cf56..d974612 100755 --- a/alez.sh +++ b/alez.sh @@ -5,7 +5,10 @@ # by Dan MacDonald with contributions from John Ramsden # Exit on error -set -o errexit -o errtrace +set -o errexit -o errtrace -o pipefail -o nounset +# Restrict internal field separator +IFS=$'\n\t' + # Set a default locale during install to avoid mandb error when indexing man pages export LANG=C @@ -72,14 +75,14 @@ IO_ERR=$STDERR IO_NULL=$DEVNULL # redirect all IO output to stdout in debug or test env mode PROD=$true -if [ $DEBUG ] || [ $TEST ]; then +if [ ${DEBUG:-} ] || [ ${TEST:-} ]; then PROD=$false fi if [ ! $PROD ]; then IO_OUT=$STDOUT - IO_ERR=$STDOUT - IO_NULL=$STDOUT + IO_ERR=$STDERR + IO_NULL=$STDERR fi # Echo to standard error if not in prod, otherwise do nothing (useful for debugging) diff --git a/run_archiso b/run_archiso index 6ddce15..662d42a 100755 --- a/run_archiso +++ b/run_archiso @@ -12,7 +12,8 @@ # - edk2-ovmf (when UEFI booting) -set -eu +# set -eu +set -o errexit -o errtrace -o pipefail -o nounset print_help() { local usagetext @@ -30,6 +31,7 @@ Options: -u set boot type to 'UEFI' -v use VNC display (instead of default SDL) -c [image] attach an additional optical disc image (e.g. for cloud-init) + -x [diskpath] attach an extra virtual hard disk Example: Run an image using UEFI: @@ -117,6 +119,7 @@ run_image() { image='' oddimage='' +extradisk='' accessibility='' boot_type='bios' mediatype='cdrom' @@ -127,7 +130,7 @@ working_dir="$(mktemp -dt run_archiso.XXXXXXXXXX)" trap cleanup_working_dir EXIT if (( ${#@} > 0 )); then - while getopts 'abc:dhi:suv' flag; do + while getopts 'abc:dhi:suvx:' flag; do case "$flag" in a) accessibility='on' @@ -158,6 +161,10 @@ if (( ${#@} > 0 )); then display='none' qemu_options+=(-vnc 'vnc=0.0.0.0:0,vnc=[::]:0') ;; + x) + extradisk="$OPTARG" + qemu_options+=('-drive' "file=$extradisk,if=virtio,aio=native,media=disk,cache.direct=on") + ;; *) printf '%s\n' "Error: Wrong option. Try 'run_archiso -h'." exit 1 diff --git a/test b/test index bb290c5..3962891 100755 --- a/test +++ b/test @@ -1,8 +1,50 @@ +#!/usr/bin/env bash + +# this script follows unofficial bash strict mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/ # prereqs: qemu edk2-ovmf docker # also make sure you have VT-x emulation on if you're running this inside a VM -docker build -t aleztest . +# Exit on error +set -o errexit -o errtrace -o pipefail -o nounset +# Restrict internal field separator +IFS=$'\n\t' + +testimagepath="./testdata/test.img" + +# remove all built iso files unless PRESERVE is set +[ ${PRESERVE:-} ] || { + echo "Removing existing ISO's..." + [ -f ./iso_out/*.iso ] && rm -v ./iso_out/*.iso +} + +[ ${SKIP_REBUILD:-} ] || { + echo "Building ALEZ docker image..." + docker build -t aleztest . +} + +# rebuild a new iso unless PRESERVE is set +[ ${PRESERVE:-} ] || { + echo "Building ISO..." + docker run -it --rm --privileged -v $(realpath ./iso_out):/opt/alez/iso/out aleztest +} + +# create a virtual drive to attach to qemu to test/dev if it doesn't already exist +[ -d "./testdata" ] || mkdir -p ./testdata +[ -f "$testimagepath" ] || { + echo "Creating virtual drive image for qemu VM at ${testimagepath}..." + qemu-img create -f qcow2 $testimagepath 10G +} + +echo "Firing up qemu VM using ISO as boot..." +./run_archiso -u -x $testimagepath -i ./iso_out/archlinux-*.iso -docker run -it --rm --privileged -v $(realpath ./iso_out):/opt/alez/iso/out aleztest +# cleanup; remove the virtual drive +# only runs if the command above was successful +[ $? ] && { + echo "VM exited successfully; removing virtual drive at ${testimagepath}..." + rm $testimagepath +} -./run_archiso -u -i ./iso_out/archlinux-*.iso +# if there wasn't a failure before now, then exit 0, or else the exit code of the above command would be the exit code +# (see the end of the unofficial bash strict mode article for explanation) +exit 0 From 7ed4f210e09f811b1eb9d3f54ad6ee89c0c6cac0 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Tue, 26 Oct 2021 21:43:52 +0000 Subject: [PATCH 6/7] Added tiny test library (with its own tests of itself) after being dissatisfied with the over-engineered options already available --- tinytestlib | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100755 tinytestlib diff --git a/tinytestlib b/tinytestlib new file mode 100755 index 0000000..e628c3a --- /dev/null +++ b/tinytestlib @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +# Tinytestlib: +# Because all the other solutions I found to do basic bash shell script testing were too large. + +# Exit on error +set -o errexit -o errtrace -o pipefail -o nounset + +# Captures stdout, stderr and return code of any command into the named variables. +capture () { + local _out_var _out _err_var _err _ret_var _ret debugflag + debugflag= + if [ "$1" = "--debug" ]; then + debugflag=1 + shift + fi + if [ "$#" -lt 4 ]; then + echo "Usage: capture [--debug] command [arg ...]" + return 1 + fi + _out_var="$1"; shift + _err_var="$1"; shift + _ret_var="$1"; shift + # just a modification of some nutso magic I found online that captures stdout into $_out, stderr into $_err and return/status code into $_ret + # WITHOUT opening files, because touching the filesystem unnecessarily is sad (slows down tests, etc) + . <({ _err=$({ _out=$("$@"); _ret=$?; } 2>&1; declare -p _out _ret >&2); declare -p _err; } 2>&1) + if [ $debugflag ]; then + echo "cmd: $@" + echo "out: $_out" + echo "err: $_err" + echo "ret: $_ret" + fi + read $_out_var <<<$(printf "%b" "$_out") + read $_err_var <<<$(printf "%b" "$_err") + read $_ret_var <<<$(printf "%b" "$_ret") +} + +red_text () { + echo -en "\e[31m${1}\e[0m" +} + +green_text () { + echo -en "\e[32m${1}\e[0m" +} + +hex_encode () { + printf "%b" "$*" | hexdump -ve '/1 "%02x"' +} + +hex_decode () { + printf "%b" "$*" | xxd -r -p +} + +_assert_equality_or_lack_thereof () { + local comp_op arg1_enc arg2_enc + comp_op="$1" + shift + # to tapdance around escaping issues, I just encode to hex and compare those. + # This may or may not be necessary anymore, but it fixed issues in the past + # Wish it didn't have to fire up a subshell though; future tweak? + arg1_enc=$(hex_encode $1) + arg2_enc=$(hex_encode $2) + if [ "$arg1_enc" $comp_op "$arg2_enc" ]; then + green_text "." + return 0 + else + red_text "F" + red_text "assert_equal failure: values <$1> and <$2> do not match" >&2 + echo "" >&2 + return 1 + fi +} +assert_equal () { + _assert_equality_or_lack_thereof "==" "$1" "$2" +} +assert_not_equal () { + _assert_equality_or_lack_thereof "!=" "$1" "$2" +} + +# Who tests the testers?? Obviously, the testers themselves test the testers!! +# Run this by passing env TEST=true +_test () { + local stdout stderr ret + # test basic assert_equal success + assert_equal "test with spaces" "test with spaces" + # test basic assert_not_equal success + assert_not_equal "a a" "b b" + # test basic assert_equal success by capturing the stdout, stderr and returncode + capture stdout stderr ret assert_equal "test with spaces" "test with spaces" + assert_equal "$stdout" "\e[32m.\e[0m" # ansi green . + assert_equal "$stderr" "" + assert_equal $ret 0 + # test basic assert_equal failure by capturing the stdout, stderr and returncode + capture stdout stderr ret assert_equal "good" "bad" + assert_equal "$stdout" "\e[31mF\e[0m" # ansi red F + assert_equal "$stderr" "\e[31massert_equal failure: values and do not match\e[0m" + assert_equal $ret 1 + # test the hex encoder/decoder + local testencode testdecode teststring + teststring="hello" + testencode=$(hex_encode $teststring) + testdecode=$(hex_decode $testencode) + assert_equal "$teststring" "$testdecode" +} + +testsuite () { + _test + echo + green_text "Success\n" +} + +if [ $TEST ]; then + testsuite +fi From ed8cd99648ee5a6ca0c2ede54ed566fcb33f3989 Mon Sep 17 00:00:00 2001 From: Peter Marreck Date: Wed, 27 Oct 2021 01:35:33 +0000 Subject: [PATCH 7/7] tinytestlib tweaks, added assert_success and assert_failure with tests --- tinytestlib | 52 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/tinytestlib b/tinytestlib index e628c3a..68d07c1 100755 --- a/tinytestlib +++ b/tinytestlib @@ -72,9 +72,37 @@ _assert_equality_or_lack_thereof () { } assert_equal () { _assert_equality_or_lack_thereof "==" "$1" "$2" + return $? } assert_not_equal () { _assert_equality_or_lack_thereof "!=" "$1" "$2" + return $? +} +assert_success () { + local stdout stderr ret + capture stdout stderr ret "$*" + if [ $ret = 0 ]; then + green_text "." + return 0 + else + red_text "F" + red_text "assert_success failure: <$*>" >&2 + echo "" >&2 + return 1 + fi +} +assert_failure () { + local stdout stderr ret + capture stdout stderr ret "$*" + if [ $ret != 0 ]; then + green_text "." + return 0 + else + red_text "F" + red_text "asserted failure, but it actually succeeded: <$*>" >&2 + echo "" >&2 + return 1 + fi } # Who tests the testers?? Obviously, the testers themselves test the testers!! @@ -83,6 +111,7 @@ _test () { local stdout stderr ret # test basic assert_equal success assert_equal "test with spaces" "test with spaces" + assert_equal "\"quoted 'stuff'\"" "\"quoted 'stuff'\"" # test basic assert_not_equal success assert_not_equal "a a" "b b" # test basic assert_equal success by capturing the stdout, stderr and returncode @@ -101,12 +130,29 @@ _test () { testencode=$(hex_encode $teststring) testdecode=$(hex_decode $testencode) assert_equal "$teststring" "$testdecode" + # test success and failure assertions + assert_success ls + assert_failure ls thisfilebetternotexist } testsuite () { - _test - echo - green_text "Success\n" + local stdout stderr ret + capture stdout stderr ret _test + echo -e $stdout + echo -en $stderr + # for some reason I couldn't propagate the return code without adding "|| return 1" to every assert line in _test, which is lame. + # I thought -o errexit did this? Anyway, I just look at the stderr of the test suite itself to determine, for now. + if [[ $stderr == "" ]]; then + echo + green_text "Success" + echo + return 0 + else + echo + red_text "Failure" + echo + return 1 + fi } if [ $TEST ]; then