2022-03-26 12:42:40 -07:00
|
|
|
#!/bin/bash
|
|
|
|
|
2024-03-03 14:39:27 -08:00
|
|
|
# Don't fail on error. We use the exit status as a conditional.
|
|
|
|
#
|
|
|
|
# This is the default behavior but can be overriden by the caller in the
|
|
|
|
# SHELLOPTS env var.
|
|
|
|
set +e
|
|
|
|
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
|
|
|
# Execute the Debian install script.
|
|
|
|
# Arguments:
|
|
|
|
# Root directory to search from.
|
|
|
|
# File path to cached package archive.
|
|
|
|
# Installation script extension (preinst, postinst).
|
|
|
|
# Parameter to pass to the installation script.
|
|
|
|
# Returns:
|
|
|
|
# Filepath of the install script, otherwise an empty string.
|
|
|
|
###############################################################################
|
|
|
|
function execute_install_script {
|
2023-03-23 22:19:43 -07:00
|
|
|
local package_name=$(basename ${2} | awk -F\= '{print $1}')
|
2022-11-23 22:24:00 -08:00
|
|
|
local install_script_filepath=$(\
|
2023-03-23 20:20:24 -07:00
|
|
|
get_install_script_filepath "${1}" "${package_name}" "${3}")
|
2022-11-23 22:24:00 -08:00
|
|
|
if test ! -z "${install_script_filepath}"; then
|
|
|
|
log "- Executing ${install_script_filepath}..."
|
|
|
|
# Don't abort on errors; dpkg-trigger will error normally since it is
|
|
|
|
# outside its run environment.
|
|
|
|
sudo sh -x ${install_script_filepath} ${4} || true
|
|
|
|
log " done"
|
|
|
|
fi
|
2022-03-26 12:42:40 -07:00
|
|
|
}
|
|
|
|
|
2023-03-23 20:20:24 -07:00
|
|
|
###############################################################################
|
|
|
|
# Gets the Debian install script filepath.
|
|
|
|
# Arguments:
|
|
|
|
# Root directory to search from.
|
|
|
|
# Name of the unqualified package to search for.
|
|
|
|
# Extension of the installation script (preinst, postinst)
|
|
|
|
# Returns:
|
|
|
|
# Filepath of the script file, otherwise an empty string.
|
|
|
|
###############################################################################
|
|
|
|
function get_install_script_filepath {
|
|
|
|
# Filename includes arch (e.g. amd64).
|
|
|
|
local filepath="$(\
|
|
|
|
ls -1 ${1}var/lib/dpkg/info/${2}*.${3} 2> /dev/null \
|
|
|
|
| grep -E ${2}'(:.*)?.'${3} | head -1 || true)"
|
|
|
|
test "${filepath}" && echo "${filepath}"
|
|
|
|
}
|
|
|
|
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
|
|
|
# Gets a list of installed packages from a Debian package installation log.
|
|
|
|
# Arguments:
|
|
|
|
# The filepath of the Debian install log.
|
|
|
|
# Returns:
|
2023-03-23 20:20:24 -07:00
|
|
|
# The list of colon delimited action syntax pairs with each pair equals
|
|
|
|
# delimited. <name>:<version> <name>:<version>...
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
2022-08-02 21:14:51 -07:00
|
|
|
function get_installed_packages {
|
2022-11-23 22:24:00 -08:00
|
|
|
local install_log_filepath="${1}"
|
2022-08-02 21:14:51 -07:00
|
|
|
local regex="^Unpacking ([^ :]+)([^ ]+)? (\[[^ ]+\]\s)?\(([^ )]+)"
|
2022-11-23 22:24:00 -08:00
|
|
|
local dep_packages=""
|
2022-08-02 21:14:51 -07:00
|
|
|
while read -r line; do
|
2023-03-23 20:20:24 -07:00
|
|
|
# ${regex} should be unquoted since it isn't a literal.
|
2022-08-02 21:14:51 -07:00
|
|
|
if [[ "${line}" =~ ${regex} ]]; then
|
2023-03-23 22:19:43 -07:00
|
|
|
dep_packages="${dep_packages}${BASH_REMATCH[1]}=${BASH_REMATCH[4]} "
|
2022-08-02 21:14:51 -07:00
|
|
|
else
|
|
|
|
log_err "Unable to parse package name and version from \"${line}\""
|
|
|
|
exit 2
|
|
|
|
fi
|
|
|
|
done < <(grep "^Unpacking " ${install_log_filepath})
|
|
|
|
if test -n "${dep_packages}"; then
|
|
|
|
echo "${dep_packages:0:-1}" # Removing trailing space.
|
|
|
|
else
|
|
|
|
echo ""
|
|
|
|
fi
|
2022-07-19 20:02:22 -07:00
|
|
|
}
|
|
|
|
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
2023-03-23 20:20:24 -07:00
|
|
|
# Splits a fully action syntax APT package into the name and version.
|
2022-11-23 22:24:00 -08:00
|
|
|
# Arguments:
|
2023-10-11 08:07:11 -07:00
|
|
|
# The action syntax equals delimited package pair or just the package name.
|
2022-11-23 22:24:00 -08:00
|
|
|
# Returns:
|
|
|
|
# The package name and version pair.
|
|
|
|
###############################################################################
|
2022-03-26 12:42:40 -07:00
|
|
|
function get_package_name_ver {
|
2023-03-23 20:20:24 -07:00
|
|
|
local ORIG_IFS="${IFS}"
|
2023-03-23 22:19:43 -07:00
|
|
|
IFS=\= read name ver <<< "${1}"
|
|
|
|
IFS="${ORIG_IFS}"
|
2022-03-26 12:42:40 -07:00
|
|
|
# If version not found in the fully qualified package value.
|
|
|
|
if test -z "${ver}"; then
|
2023-10-11 08:07:11 -07:00
|
|
|
# This is a fallback and should not be used any more as its slow.
|
|
|
|
log_err "Unexpected version resolution for package '${name}'"
|
|
|
|
ver="$(apt-cache show ${name} | grep '^Version:' | awk '{print $2}')"
|
2022-03-26 12:42:40 -07:00
|
|
|
fi
|
2023-03-23 22:19:43 -07:00
|
|
|
echo "${name}" "${ver}"
|
2022-03-26 12:42:40 -07:00
|
|
|
}
|
2022-06-30 07:13:58 -07:00
|
|
|
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
2023-03-23 20:20:24 -07:00
|
|
|
# Sorts given packages by name and split on commas and/or spaces.
|
2022-11-23 22:24:00 -08:00
|
|
|
# Arguments:
|
2023-03-23 20:20:24 -07:00
|
|
|
# The comma and/or space delimited list of packages.
|
2022-11-23 22:24:00 -08:00
|
|
|
# Returns:
|
2023-10-11 08:07:11 -07:00
|
|
|
# Sorted list of space delimited package name=version pairs.
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
2023-03-23 20:20:24 -07:00
|
|
|
function get_normalized_package_list {
|
2023-10-11 08:07:11 -07:00
|
|
|
# Remove commas, and block scalar folded backslashes,
|
|
|
|
# extraneous spaces at the middle, beginning and end
|
|
|
|
# then sort.
|
2023-12-22 10:28:03 -08:00
|
|
|
local packages=$(echo "${1}" \
|
2023-10-11 08:07:11 -07:00
|
|
|
| sed 's/[,\]/ /g; s/\s\+/ /g; s/^\s\+//g; s/\s\+$//g' \
|
|
|
|
| sort -t' ')
|
2023-12-22 10:28:03 -08:00
|
|
|
local script_dir="$(dirname -- "$(realpath -- "${0}")")"
|
2024-03-01 12:59:14 -08:00
|
|
|
|
|
|
|
local architecture=$(dpkg --print-architecture)
|
|
|
|
if [ "${architecture}" == "arm64" ]; then
|
|
|
|
${script_dir}/apt_query-arm64 normalized-list ${packages}
|
|
|
|
else
|
|
|
|
${script_dir}/apt_query normalized-list ${packages}
|
|
|
|
fi
|
2022-11-23 22:24:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# Gets the relative filepath acceptable by Tar. Just removes the leading slash
|
|
|
|
# that Tar disallows.
|
|
|
|
# Arguments:
|
|
|
|
# Absolute filepath to archive.
|
|
|
|
# Returns:
|
|
|
|
# The relative filepath to archive.
|
|
|
|
###############################################################################
|
|
|
|
function get_tar_relpath {
|
|
|
|
local filepath=${1}
|
|
|
|
if test ${filepath:0:1} = "/"; then
|
|
|
|
echo "${filepath:1}"
|
|
|
|
else
|
|
|
|
echo "${filepath}"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2023-10-11 08:07:11 -07:00
|
|
|
function log { echo "$(date +%T.%3N)" "${@}"; }
|
|
|
|
function log_err { >&2 echo "$(date +%T.%3N)" "${@}"; }
|
2022-07-14 21:23:42 -07:00
|
|
|
|
2022-07-23 17:06:17 -07:00
|
|
|
function log_empty_line { echo ""; }
|
|
|
|
|
2022-11-23 22:24:00 -08:00
|
|
|
###############################################################################
|
|
|
|
# Validates an argument to be of a boolean value.
|
|
|
|
# Arguments:
|
|
|
|
# Argument to validate.
|
|
|
|
# Variable name of the argument.
|
|
|
|
# Exit code if validation fails.
|
|
|
|
# Returns:
|
|
|
|
# Sorted list of space delimited packages.
|
|
|
|
###############################################################################
|
|
|
|
function validate_bool {
|
|
|
|
if test "${1}" != "true" -a "${1}" != "false"; then
|
|
|
|
log "aborted"
|
|
|
|
log "${2} value '${1}' must be either true or false (case sensitive)."
|
|
|
|
exit ${3}
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# Writes the manifest to a specified file.
|
|
|
|
# Arguments:
|
|
|
|
# Type of manifest being written.
|
|
|
|
# List of packages being written to the file.
|
|
|
|
# File path of the manifest being written.
|
|
|
|
# Returns:
|
|
|
|
# Log lines from write.
|
|
|
|
###############################################################################
|
|
|
|
function write_manifest {
|
|
|
|
if [ ${#2} -eq 0 ]; then
|
|
|
|
log "Skipped ${1} manifest write. No packages to install."
|
|
|
|
else
|
|
|
|
log "Writing ${1} packages manifest to ${3}..."
|
|
|
|
# 0:-1 to remove trailing comma, delimit by newline and sort.
|
|
|
|
echo "${2:0:-1}" | tr ',' '\n' | sort > ${3}
|
|
|
|
log "done"
|
|
|
|
fi
|
|
|
|
}
|