joeylib2/installer/lib/distro.sh

113 lines
3.6 KiB
Bash

# shellcheck shell=bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# Host package handling: maps the logical names in distroPackages.txt to
# apt or dnf packages, reports which are missing, and installs them.
# Sourced after lib/common.sh.
JOEY_PACKAGE_MAP="$JOEY_INSTALLER/distroPackages.txt"
# distroFamily -- print apt, dnf or unknown.
distroFamily() {
local id=""
local like=""
if [ -r /etc/os-release ]; then
id="$(. /etc/os-release && printf '%s' "${ID:-}")"
like="$(. /etc/os-release && printf '%s' "${ID_LIKE:-}")"
fi
case " $id $like " in
*" debian "*|*" ubuntu "*) printf 'apt\n' ;;
*" fedora "*|*" rhel "*|*" centos "*|*" rocky "*) printf 'dnf\n' ;;
*) printf 'unknown\n' ;;
esac
}
# distroPackageNames LOGICAL... -- print the real package names, one per line.
distroPackageNames() {
local family
local column
local name
local row
family="$(distroFamily)"
case "$family" in
apt) column=2 ;;
dnf) column=3 ;;
*) die "unsupported Linux distribution (need apt or dnf); install host packages by hand: $*" ;;
esac
for name in "$@"; do
row="$(awk -v n="$name" -v c="$column" '$1 == n { print $c }' "$JOEY_PACKAGE_MAP")"
[ -n "$row" ] || die "no entry for host dependency '$name' in distroPackages.txt"
[ "$row" != "-" ] || die "host dependency '$name' has no $family package"
printf '%s\n' "${row//,/$'\n'}"
done
}
# distroMissing PACKAGE... -- print the packages that are not installed.
distroMissing() {
local family
local pkg
local native=""
family="$(distroFamily)"
[ "$family" = "apt" ] && native="$(dpkg --print-architecture)"
for pkg in "$@"; do
case "$family" in
apt)
aptInstalled "$pkg" "$native" || printf '%s\n' "$pkg"
;;
dnf)
rpm -q "$pkg" >/dev/null 2>&1 || printf '%s\n' "$pkg"
;;
esac
done
}
# aptInstalled PACKAGE NATIVE-ARCH -- PACKAGE is installed for this machine.
# dpkg-query prints one line per architecture present (libc6 on a system
# with i386 multiarch has two), so look for a native or arch-all line; a
# PACKAGE that names its architecture (libc6:i386) needs that one.
aptInstalled() {
local arch
local status
local want="$2"
if [[ "$1" == *:* ]]; then
want="${1##*:}"
fi
while read -r arch status; do
if [ "$status" = "installed" ] && { [ "$arch" = "$want" ] || [ "$arch" = "all" ]; }; then
return 0
fi
done <<< "$(dpkg-query -W -f='${Architecture} ${db:Status-Status}\n' "$1" 2>/dev/null || true)"
return 1
}
# distroInstall PACKAGE... -- install with one sudo call (none when already
# root, as in containers and CI, which often have no sudo at all).
distroInstall() {
local family
local yes=()
local asRoot=()
[ $# -gt 0 ] || return 0
family="$(distroFamily)"
[ "$JOEY_ASSUME_YES" = "1" ] && yes=(-y)
[ "$(id -u)" = "0" ] || asRoot=(sudo)
info "installing host packages: $*"
case "$family" in
apt)
"${asRoot[@]}" apt-get update
"${asRoot[@]}" env DEBIAN_FRONTEND=noninteractive apt-get install "${yes[@]}" --no-install-recommends "$@"
;;
dnf)
"${asRoot[@]}" dnf install "${yes[@]}" "$@"
;;
*)
die "unsupported Linux distribution; install by hand: $*"
;;
esac
}