dotfiles/install/lib/common.sh

119 lines
3.3 KiB
Bash

#!/bin/bash
# Shared helpers for the Pop!_OS / Ubuntu installer.
DOTFILES_DIR="${DOTFILES_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
KEYRINGS_DIR="/etc/apt/keyrings"
APT_LISTS_DIR="/etc/apt/sources.list.d"
info() { printf '\033[0;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; }
error() { printf '\033[0;31mERROR:\033[0m %s\n' "$*" >&2; }
die() {
error "$*"
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}
download_verified() {
local url="$1" sha256="$2" destination="$3"
if ! curl -fsSL --retry 3 "$url" -o "$destination"; then
rm -f "$destination"
die "Failed to download $url"
fi
if ! printf '%s %s\n' "$sha256" "$destination" | sha256sum --check --status -; then
rm -f "$destination"
die "SHA-256 verification failed for $url"
fi
}
key_fingerprints_match() {
local key="$1" expected_fingerprints="$2"
local actual_fingerprints normalized_expected
actual_fingerprints="$(
gpg --show-keys --with-colons "$key" 2>/dev/null |
awk -F: '$1 == "fpr" { print $10 }' |
sort -u |
tr '\n' ' '
)"
normalized_expected="$(
for fingerprint in $expected_fingerprints; do
printf '%s\n' "$fingerprint"
done |
sort -u |
tr '\n' ' '
)"
[[ -n $actual_fingerprints && $actual_fingerprints == "$normalized_expected" ]]
}
apt_install() {
sudo apt-get install -y --no-install-recommends "$@"
}
apt_update_once() {
if [[ -z ${_APT_UPDATED:-} ]]; then
info "Refreshing apt indexes..."
sudo apt-get update -y
_APT_UPDATED=1
fi
}
# Symlink $1 -> $2, backing up any pre-existing real target first. Idempotent.
backup_and_link() {
local src="$1" dest="$2"
if [[ ! -e $src ]]; then
warn "Source missing, skipping link: $src"
return 0
fi
mkdir -p "$(dirname "$dest")"
if [[ -L $dest ]]; then
[[ "$(readlink "$dest")" == "$src" ]] && return 0
rm -f "$dest"
elif [[ -e $dest ]]; then
mv "$dest" "$dest.pre-install.$(date +%s)"
info "Backed up existing $dest"
fi
ln -sfn "$src" "$dest"
info "Linked $dest -> $src"
}
# Add a signed apt repo. Args: name keyring_url repo_line expected_fingerprints
add_apt_repo() {
local name="$1" keyring_url="$2" repo_line="$3" expected_fingerprints="$4"
local keyring="$KEYRINGS_DIR/$name.gpg"
local list="$APT_LISTS_DIR/$name.list"
if [[ -f $list && -s $keyring ]] &&
key_fingerprints_match "$keyring" "$expected_fingerprints"; then
return 0
fi
info "Adding apt repo: $name"
local tmp_key tmp_gpg
tmp_key="$(mktemp)"
tmp_gpg="$(mktemp)"
trap 'rm -f "$tmp_key" "$tmp_gpg"' RETURN
if ! curl -fsSL "$keyring_url" -o "$tmp_key"; then
die "Failed to download key for $name from $keyring_url"
fi
[[ -s $tmp_key ]] || die "Downloaded key for $name is empty"
key_fingerprints_match "$tmp_key" "$expected_fingerprints" ||
die "Signing-key fingerprint verification failed for $name"
if ! gpg --yes --dearmor -o "$tmp_gpg" <"$tmp_key" 2>/dev/null; then
# Already binary (.gpg) keys can't be dearmored; use the raw bytes instead.
cp "$tmp_key" "$tmp_gpg"
fi
sudo install -d -m 0755 "$KEYRINGS_DIR"
sudo install -m 0644 "$tmp_gpg" "$keyring"
echo "$repo_line" | sudo tee "$list" >/dev/null
unset _APT_UPDATED # force re-update so the new repo is seen
}