quickemu/action

553 lines
18 KiB
Bash
Executable File

#!/usr/bin/env bash
#shellcheck disable=SC1090
#
function write_output() {
. "actions/${OS}"
# Load the list of RELEASES
RELEASES=$(cut -d: -f1 < "public/tmp_${OS}" | cut -d' ' -f2 | sort -ur | paste -sd ' ')
# Check if 'edition' function exists and load it if present
if grep -q 'GUEST' "actions/${OS}"; then
GUEST=$(printf 'GUEST="%s"\n' "$(grep 'GUEST' < "actions/${OS}" | cut -d'"' -f2)")
else
GUEST=$(echo -e "\n")
fi
if grep -q 'IMAGE_TYPE' "actions/${OS}"; then
IMAGE_TYPE=$(printf 'IMAGE_TYPE="%s"\n' "$(grep 'IMAGE_TYPE' < "actions/${OS}" | cut -d'"' -f2)")
else
IMAGE_TYPE=$(echo -e "\n")
fi
if grep -q 'function edition' "actions/${OS}"; then
. "actions/${OS}"
EDITIONS=$(printf 'EDITIONS="%s"\n' "$(cut -d: -f1 < "public/tmp_${OS}" | cut -d' ' -f3 | sort -ur | paste -sd ' ')")
else
EDITIONS=$(echo -e "\n")
fi
echo
REST=$(sed -n '/^function releases_()/,$p' "actions/${OS}")
# Create and save the template with all collected information
printf '# Template file for '\''%s'\''
OSNAME="%s"
PRETTY="%s"
BASEDOF="%s"
DESCRIPTION="%s"
HOMEPAGE="%s"
CREDENTIALS="%s"
%s
%s
RELEASES="%s"
%s
%s
' "$OS" "$OSNAME" "$PRETTY" "$BASEDOF" "$DESCRIPTION" "$HOMEPAGE" "$CREDENTIALS" "$GUEST" "$IMAGE_TYPE" "$RELEASES" "$EDITIONS" "$REST" | tee -a "public/${OS}"
# Append content from tmp_${OS} to TODO/all and remove the temporary file
cat "public/tmp_${OS}" >> TODO/all
#rm "public/tmp_${OS}"
echo
}
function test_result() {
local OS="${1}"
local RELEASE="${2}"
local EDITION="${3:-}"
local URL="${4:-}"
local RESULT="${5:-}"
if [ -n "${EDITION}" ]; then
OS="${OS} ${RELEASE} ${EDITION}"
else
OS="${OS} ${RELEASE}"
fi
if [ -n "${RESULT}" ]; then
# Pad the OS string for consistent output
OS=$(printf "%-35s" "${OS}")
echo -e "${OS} ${URL}"
else
OS=$(printf "%-36s" "${OS}:")
echo -e "${OS} ${URL}"
fi
}
function test_all() {
OS="${1}"
mkdir -p public
rm -f "public/${OS}"
touch "public/${OS}"
rm -f "public/tmp_${OS}"
touch "public/tmp_${OS}"
. "actions/${OS}"
if [[ "${OS}" == *ubuntu* && "${OS}" != "ubuntu-server" ]]; then
FUNC="ubuntu"
fi
echo "${OS}
"
for RELEASE in $("releases_"); do
. "actions/${OS}"
if [[ $(type -t "editions_") == function ]]; then
for EDITION in $(editions_""); do
. "actions/${OS}"
URL=$(get_ | cut -d' ' -f1 | head -n 1)
if [ "${OPERATION}" == "show" ]; then
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" | tee -a "public/tmp_${OS}"
elif [ "${OPERATION}" == "test" ]; then
CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL")
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}" | tee -a "public/tmp_${OS}"
fi
done
elif [[ "${OS}" == "windows"* ]]; then
languages_
for I18N in "${I18N[@]}"; do
if [ "${OPERATION}" == "show" ]; then
test_result "${OS}" "${RELEASE}" "${I18N}" "" | tee -a "public/tmp_${OS}"
elif [ "${OPERATION}" == "test" ]; then
test_result "${OS}" "${RELEASE}" "${I18N}" "${URL}" "SKIP" | tee -a "public/tmp_${OS}"
fi
done
elif [[ "${OS}" == "macos" ]]; then
(get_)
elif [ "${OS}" == "ubuntu-server" ]; then
(get_)
elif [[ "${OS}" == *ubuntu* ]]; then
(get_)
else
URL=$(get_ | cut -d' ' -f1 | head -n 1)
if [ "${OPERATION}" == "show" ]; then
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" | tee -a "public/tmp_${OS}"
elif [ "${OPERATION}" == "test" ]; then
CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL")
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}" | tee -a "public/tmp_${OS}"
fi
fi
done
}
function get_releases() {
OS="${1}"
mkdir -p public
rm -f "public/${OS}"
#touch "public/${OS}"
rm -f "public/tmp_${OS}"
#touch "public/tmp_${OS}"
. "actions/${OS}"
if [[ "${OS}" == *ubuntu* && "${OS}" != "ubuntu-server" ]]; then
FUNC="ubuntu"
fi
echo "${OS}
"
for RELEASE in $("releases_"); do
. "actions/${OS}"
if [[ $(type -t "editions_") == function ]]; then
for EDITION in $(editions_""); do
. "actions/${OS}"
URL=$(get_ | cut -d' ' -f1 | head -n 1)
echo "${OS} ${RELEASE} ${EDITION}" >> "public/tmp_${OS}"
done
elif [[ "${OS}" == "windows"* ]]; then
languages_
for I18N in "${I18N[@]}"; do
echo "${OS} ${RELEASE} ${I18N}" >> "public/tmp_${OS}"
done
elif [[ "${OS}" == "macos" ]]; then
(get_)
elif [ "${OS}" == "ubuntu-server" ]; then
(get_)
elif [[ "${OS}" == *ubuntu* ]]; then
(get_ubuntu)
else
#validate_release releases_"${OS}"
URL=$(get_"${OS}" | cut -d' ' -f1 | head -n 1)
echo "${OS} ${RELEASE} ${EDITION}" >> "public/tmp_${OS}"
fi
done
}
function check_hash() {
local iso=""
local hash=""
local hash_algo=""
if [ "${OPERATION}" == "download" ]; then
iso="${1}"
else
iso="${VM_PATH}/${1}"
fi
hash="${2}"
# Guess the hash algorithm by the hash length
case ${#hash} in
32) hash_algo=md5sum;;
40) hash_algo=sha1sum;;
64) hash_algo=sha256sum;;
128) hash_algo=sha512sum;;
*) echo "WARNING! Can't guess hash algorithm, not checking ${iso} hash."
return;;
esac
echo -n "Checking ${iso} with ${hash_algo}... "
if ! echo "${hash} ${iso}" | ${hash_algo} --check --status; then
echo "ERROR!"
echo "${iso} doesn't match ${hash}. Try running 'quickget' again."
exit 1
else
echo "Good!"
fi
}
# Download a file from the web and pipe it to stdout
function web_pipe() {
curl --disable --silent --location "${1}"
}
# Download a JSON file from the web and pipe it to stdout
function web_pipe_json() {
curl --disable --silent --location --header "Accept: application/json" "${1}"
}
# checks if a URL is reachable
function web_check() {
local HEADERS=()
local URL="${1}"
# Process any headers
while (( "$#" )); do
if [ "${1}" == "--header" ]; then
HEADERS+=("${1}" "${2}")
shift 2
else
shift
fi
done
curl --silent --location --head --output /dev/null --fail --connect-timeout 30 --max-time 30 --retry 3 "${HEADERS[@]}" "${URL}"
}
# checks if a URL needs to be redirected and returns the final URL
function web_redirect() {
local REDIRECT_URL=""
local URL="${1}"
# Check for URL redirections
# Output to nonexistent directory so the download fails fast
REDIRECT_URL=$(curl --silent --location --fail --write-out '%{url_effective}' --output /var/cache/${RANDOM}/${RANDOM} "${URL}" )
if [ "${REDIRECT_URL}" != "${URL}" ]; then
echo "${REDIRECT_URL}"
else
echo "${URL}"
fi
}
# Download a file from the web
function web_get() {
local CHECK=""
local HEADERS=()
local URL="${1}"
local DIR="${2}"
local FILE=""
local USER_AGENT="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
if [ -n "${3}" ]; then
FILE="${3}"
else
FILE="${URL##*/}"
fi
# Process any URL redirections after the file name has been extracted
URL=$(web_redirect "${URL}")
# Process any headers
while (( "$#" )); do
if [ "${1}" == "--header" ]; then
HEADERS+=("${1}" "${2}")
shift 2
else
shift
fi
done
# Test mode for ISO
if [ "${OPERATION}" == "show" ]; then
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}"
exit 0
elif [ "${OPERATION}" == "test" ]; then
CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL")
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}"
exit 0
elif [ "${OPERATION}" == "download" ]; then
DIR="$(pwd)"
fi
if [ "${DIR}" != "$(pwd)" ] && ! mkdir -p "${DIR}" 2>/dev/null; then
echo "ERROR! Unable to create directory ${DIR}"
exit 1
fi
if [[ ${OS} != windows && ${OS} != macos && ${OS} != windows-server ]]; then
echo "Downloading ${PRETTY} ${RELEASE} ${EDITION}"
echo "- URL: ${URL}"
fi
if ! curl --progress-bar --location --output "${DIR}/${FILE}" --continue-at - --user-agent "${USER_AGENT}" "${HEADERS[@]}" -- "${URL}"; then
echo "ERROR! Failed to download ${URL} with curl."
rm -f "${DIR}/${FILE}"
fi
}
function zsync_get() {
local CHECK=""
local DIR="${2}"
local FILE="${1##*/}"
local OUT=""
local URL="${1}"
# Test mode for ISO
if [ "${OPERATION}" == "show" ]; then
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}"
exit 0
elif [ "${OPERATION}" == "test" ]; then
CHECK=$(web_check "${URL}" && echo "PASS" || echo "FAIL")
test_result "${OS}" "${RELEASE}" "${EDITION}" "${URL}" "${CHECK}"
exit 0
elif command -v zsync &>/dev/null; then
if [ -n "${3}" ]; then
OUT="${3}"
else
OUT="${FILE}"
fi
if ! mkdir -p "${DIR}" 2>/dev/null; then
echo "ERROR! Unable to create directory ${DIR}"
exit 1
fi
echo "Downloading ${PRETTY} ${RELEASE} ${EDITION} from ${URL}"
# Only force http for zsync - not earlier because we might fall through here
if ! zsync "${URL/https/http}.zsync" -i "${DIR}/${OUT}" -o "${DIR}/${OUT}" 2>/dev/null; then
echo "ERROR! Failed to download ${URL/https/http}.zsync"
exit 1
fi
if [ -e "${DIR}/${OUT}.zs-old" ]; then
rm "${DIR}/${OUT}.zs-old"
fi
else
echo "INFO: zsync not found, falling back to curl"
if [ -n "${3}" ]; then
web_get "${1}" "${2}" "${3}"
else
web_get "${1}" "${2}"
fi
fi
}
function make_vm_config() {
local CONF_FILE=""
local IMAGE_FILE=""
local ISO_FILE=""
local IMAGE_TYPE=""
local GUEST=""
if [ "${OPERATION}" == "download" ]; then
exit 0
fi
IMAGE_FILE="${1}"
ISO_FILE="${2}"
case "${OS}" in
batocera)
GUEST="batocera"
IMAGE_TYPE="img";;
custom)
GUEST="${CUSTOM_OS}"
IMAGE_TYPE="${CUSTOM_IMAGE_TYPE}";;
dragonflybsd)
GUEST="dragonflybsd"
IMAGE_TYPE="iso";;
easyos)
GUEST="linux"
IMAGE_TYPE="img";;
freebsd|ghostbsd)
GUEST="freebsd"
IMAGE_TYPE="iso";;
haiku)
GUEST="haiku"
IMAGE_TYPE="iso";;
freedos)
GUEST="freedos"
IMAGE_TYPE="iso";;
kolibrios)
GUEST="kolibrios"
IMAGE_TYPE="iso";;
macos)
GUEST="macos"
IMAGE_TYPE="img";;
netbsd)
GUEST="netbsd"
IMAGE_TYPE="iso";;
openbsd)
GUEST="openbsd"
IMAGE_TYPE="iso";;
openindiana)
GUEST="solaris"
IMAGE_TYPE="iso";;
reactos)
GUEST="reactos"
IMAGE_TYPE="iso";;
truenas*)
GUEST="truenas"
IMAGE_TYPE="iso";;
ubuntu*)
GUEST="linux"
IMAGE_TYPE="iso"
# If there is a point in the release, check if it is less than 16.04
if [[ "${RELEASE}" != *"daily"* ]]; then
if [ "${RELEASE//./}" -lt 1604 ]; then
GUEST="linux_old"
fi
fi
;;
windows)
GUEST="windows"
IMAGE_TYPE="iso";;
windows-server)
GUEST="windows-server"
IMAGE_TYPE="iso";;
*)
GUEST="linux"
IMAGE_TYPE="iso";;
esac
CONF_FILE="${VM_PATH}.conf"
if [ ! -e "${CONF_FILE}" ]; then
echo "Making ${CONF_FILE}"
cat << EOF > "${CONF_FILE}"
#!${QUICKEMU} --vm
guest_os="${GUEST}"
disk_img="${VM_PATH}/disk.qcow2"
${IMAGE_TYPE}="${VM_PATH}/${IMAGE_FILE}"
EOF
echo " - Setting ${CONF_FILE} executable"
chmod u+x "${CONF_FILE}"
if [ -n "${ISO_FILE}" ]; then
echo "fixed_iso=\"${VM_PATH}/${ISO_FILE}\"" >> "${CONF_FILE}"
fi
# OS specific tweaks
case ${OS} in
alma|athenaos|centos-stream|endless|garuda|gentoo|kali|nixos|oraclelinux|popos|rockylinux)
echo "disk_size=\"32G\"" >> "${CONF_FILE}";;
openindiana)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
echo "disk_size=\"32G\"" >> "${CONF_FILE}";;
batocera)
echo "disk_size=\"8G\"" >> "${CONF_FILE}";;
bazzite)
echo "disk_size=\"64G\"" >> "${CONF_FILE}";;
dragonflybsd|haiku|openbsd|netbsd|slackware|slax|tails|tinycore)
echo "boot=\"legacy\"" >> "${CONF_FILE}";;
deepin)
echo "disk_size=\"64G\"" >> "${CONF_FILE}"
echo "ram=\"4G\"" >> "${CONF_FILE}"
;;
freedos)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
echo "disk_size=\"4G\"" >> "${CONF_FILE}"
echo "ram=\"256M\"" >> "${CONF_FILE}"
;;
kolibrios)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
echo "disk_size=\"2G\"" >> "${CONF_FILE}"
echo "ram=\"128M\"" >> "${CONF_FILE}"
;;
slint)
echo "disk_size=\"50G\"" >> "${CONF_FILE}"
;;
slitaz)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
echo "disk_size=\"4G\"" >> "${CONF_FILE}"
echo "ram=\"512M\"" >> "${CONF_FILE}"
;;
truenas-scale|truenas-core)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
# the rest is non-functional
# echo "bootdrive_size=\"5G\"" >> "${CONF_FILE}" # boot drive
# echo "1stdrive_size=\"20G\"" >> "${CONF_FILE}" # for testing
# echo "2nddrive_size=\"20G\"" >> "${CONF_FILE}" # again, for testing
;;
ubuntu-server)
# 22.04+ fails on LVM build if disk size is < 10G
# 22.04.1 fails on auto-install if TPM is disabled
echo "disk_size=\"10G\"" >> "${CONF_FILE}"
echo "ram=\"4G\"" >> "${CONF_FILE}"
if [[ "${RELEASE}" == *"22.04"* ]]; then
echo "tpm=\"on\"" >> "${CONF_FILE}"
fi
;;
vanillaos)
## Minimum is 50G for abroot, but a 64GB is allocated to give some headroom
echo "disk_size=\"64G\"" >> "${CONF_FILE}"
;;
zorin)
case ${EDITION} in
education64|edulite64) echo "disk_size=\"32G\"" >> "${CONF_FILE}";;
esac;;
reactos)
echo "boot=\"legacy\"" >> "${CONF_FILE}"
echo "disk_size=\"12G\"" >> "${CONF_FILE}"
echo "ram=\"2048M\"" >> "${CONF_FILE}"
;;
macos)
echo "disk_size=\"128G\"" >> "${CONF_FILE}"
echo "macos_release=\"${RELEASE}\"" >> "${CONF_FILE}"
# https://github.com/quickemu-project/quickemu/issues/438
if [ "${RELEASE}" == "monterey" ]; then
echo "cpu_cores=2" >> "${CONF_FILE}"
fi
;;
proxmox-ve)
echo "disk_size=\"20G\"" >> "${CONF_FILE}"
echo "ram=\"4G\"" >> "${CONF_FILE}"
;;
esac
if [ "${OS}" == "ubuntu" ] && [[ ${RELEASE} == *"daily"* ]]; then
# Minimum to install lobster testing is 18GB but 32GB are allocated for headroom
echo "disk_size=\"32G\"" >> "${CONF_FILE}"
fi
if [[ "${OS}" == "windows"* ]]; then
echo "disk_size=\"64G\"" >> "${CONF_FILE}"
fi
# Enable TPM for Windows 11 and Windows Server 2022
if [[ "${OS}" == "windows" && "${RELEASE}" == "11" || "${OS}" == "windows-server" && "${RELEASE}" == "2022" ]]; then
echo "tpm=\"on\"" >> "${CONF_FILE}"
echo "secureboot=\"off\"" >> "${CONF_FILE}"
fi
if [ "${OPERATION}" == ui ]; then
echo
gum confirm "Run new ${OS} VM?" && quickemu --vm "${CONF_FILE}"
fi
fi
echo -e "\nTo start your ${PRETTY} virtual machine run:"
if [ "${OS}" == "slint" ]; then
echo -e " quickemu --vm ${CONF_FILE}\nTo start Slint with braille support run:\n quickemu --vm --braille --display sdl ${CONF_FILE}"
else
echo " quickemu --vm ${CONF_FILE}"
fi
echo
exit 0
}
if [ "${1}" == -t ] || [ "${1}" == --test ]; then
OPERATION='test'
shift
else
OPERATION='show'
fi
if [ -z "${1}" ]; then
rm TODO/all ; touch TODO/all
for file in actions/*; do
OS="${file##*/}"
get_releases "${OS}"
#test_all "${OS}"
write_output "${OS}"
done &
elif [ -n "${1}" ]; then
test_all "${1}"
write_output "${1}"
fi