- 00-system-prep.sh: bootstrap sudo when run as root; on Debian enable contrib/non-free-firmware (SKIP_NONFREE to opt out) + install needrestart - scripts/verify-debian.sh: non-destructive post-install sanity checker (exit 0/1) covering OS, tools, Debian renames, upstream tooling, .NET/Podman/Postgres, groups, flatpak, dotfile symlinks - 01-package-install.sh: auto-detect current Kubernetes minor from upstream (fallback v1.36, override K8S_MINOR) instead of stale v1.32 pin - fix shellcheck findings (SC2155, SC2207, SC2088); all scripts clean at warning severity - docs: fold decisions/status into plan; README lists new tools
319 lines
12 KiB
Bash
Executable File
319 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# 01-package-install.sh
|
|
# Installs system packages and Flatpak applications.
|
|
# Relies on DISTRO and PACKAGE_MANAGER being set by the caller.
|
|
|
|
echo "--- Starting Package Installation ---"
|
|
|
|
if [ -z "$DISTRO" ] || [ -z "$PACKAGE_MANAGER" ]; then
|
|
echo "ERROR: DISTRO and PACKAGE_MANAGER must be set in the environment."
|
|
exit 1
|
|
fi
|
|
|
|
# Define base package list
|
|
# Base packages available in BOTH Debian and Fedora repositories.
|
|
# NOTE: gh, kubectl, minikube, k9s, terraform are NOT in Debian main —
|
|
# they are handled later in setup_upstream_tooling() via vendor repos/.deb.
|
|
BASE_PACKAGE_LIST=(
|
|
ansible
|
|
bat
|
|
btop
|
|
curl
|
|
direnv
|
|
eza
|
|
fd-find
|
|
flatpak
|
|
fzf
|
|
git
|
|
httpie
|
|
iperf3
|
|
jq
|
|
mtr
|
|
nmap
|
|
python3
|
|
python3-pip
|
|
ripgrep
|
|
socat
|
|
tcpdump
|
|
tldr
|
|
tmux
|
|
traceroute
|
|
unzip
|
|
virt-manager
|
|
wget
|
|
whois
|
|
zoxide
|
|
zsh
|
|
)
|
|
|
|
# Distro-specific packages to add to the main list
|
|
DISTRO_SPECIFIC_PACKAGES=()
|
|
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
|
|
DISTRO_SPECIFIC_PACKAGES+=(
|
|
"bind-utils" # dig, nslookup, etc.
|
|
"fontconfig-devel" # for fc-cache
|
|
"nmap-ncat" # netcat utility
|
|
"openssl-devel" # for various compilations
|
|
"util-linux-user" # for chsh (if needed)
|
|
)
|
|
elif [ "$PACKAGE_MANAGER" == "apt" ]; then
|
|
DISTRO_SPECIFIC_PACKAGES+=(
|
|
"bind9-dnsutils" # dig/nslookup (replaces dnsutils on Debian 13+)
|
|
"bridge-utils" # virt-manager bridge networking
|
|
"ca-certificates" # HTTPS repo keys
|
|
"curl" # ensure present for repo bootstrapping
|
|
"gnupg" # apt keyring handling
|
|
"libfontconfig-dev"
|
|
"libssl-dev"
|
|
"libvirt-clients" # virsh
|
|
"libvirt-daemon-system" # libvirtd daemon + default network
|
|
"netcat-openbsd"
|
|
"qemu-system-x86" # KVM/QEMU backend for virt-manager
|
|
"virtinst" # virt-install
|
|
)
|
|
fi
|
|
|
|
# Combine package lists
|
|
PACKAGE_LIST=("${BASE_PACKAGE_LIST[@]}" "${DISTRO_SPECIFIC_PACKAGES[@]}")
|
|
mapfile -t PACKAGE_LIST < <(printf "%s\n" "${PACKAGE_LIST[@]}" | LC_ALL=C sort -u)
|
|
|
|
# --- apt helpers ---------------------------------------------------------
|
|
_apt_is_installed() {
|
|
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -q "ok installed"
|
|
}
|
|
|
|
_apt_install() {
|
|
if _apt_is_installed "$1"; then
|
|
echo "$1 already installed."
|
|
return 0
|
|
fi
|
|
echo "Installing $1 (apt)..."
|
|
if sudo apt install -y "$1"; then
|
|
echo "$1 has been installed."
|
|
return 0
|
|
fi
|
|
echo "WARNING: Failed to install $1."
|
|
return 1
|
|
}
|
|
|
|
# debian/ubuntu codename, e.g. "trixie" (no lsb_release dependency)
|
|
_get_codename() {
|
|
. /etc/os-release
|
|
echo "$VERSION_CODENAME"
|
|
}
|
|
|
|
# uname -m -> Go/GitHub style arch token (amd64/arm64)
|
|
_get_upstream_arch() {
|
|
case "$(uname -m)" in
|
|
x86_64) echo "amd64" ;;
|
|
aarch64|arm64) echo "arm64" ;;
|
|
*) echo "unsupported" ;;
|
|
esac
|
|
}
|
|
|
|
|
|
|
|
FLATPAK_LIST=(
|
|
com.adamcake.Bolt
|
|
com.bitwarden.desktop
|
|
com.discordapp.Discord
|
|
com.github.IsmaelMartinez.teams_for_linux
|
|
com.github.tchx84.Flatseal
|
|
com.valvesoftware.Steam
|
|
com.visualstudio.code
|
|
io.podman_desktop.PodmanDesktop
|
|
net.davidotek.pupgui2
|
|
org.mozilla.Thunderbird
|
|
org.signal.Signal
|
|
org.videolan.VLC
|
|
)
|
|
|
|
echo "Installing System Packages..."
|
|
for package_name in "${PACKAGE_LIST[@]}"; do
|
|
if [ "$PACKAGE_MANAGER" == "dnf" ]; then
|
|
if ! rpm -q "$package_name" &>/dev/null; then
|
|
echo "Installing $package_name (dnf)..."
|
|
if sudo dnf install "$package_name" -y; then
|
|
echo "$package_name has been installed."
|
|
else
|
|
echo "WARNING: Failed to install $package_name. It may not be available in the repositories."
|
|
fi
|
|
else
|
|
echo "$package_name already installed."
|
|
fi
|
|
|
|
elif [ "$PACKAGE_MANAGER" == "apt" ]; then
|
|
# For apt, check if package provides the command or is installed
|
|
# dpkg-query is generally more reliable for checking installed status
|
|
actual_package_name=$package_name
|
|
if [ "$package_name" == "fd-find" ] && ! dpkg -s fd-find &>/dev/null ; then
|
|
# On some newer Ubuntu/Debian, 'fd-find' might be the package,
|
|
# but user might want 'fd' if it's a different source or a metapackage.
|
|
# For now, we stick to fd-find.
|
|
: # Keep actual_package_name as fd-find
|
|
fi
|
|
|
|
if ! dpkg-query -W -f='${Status}' "$actual_package_name" 2>/dev/null | grep -q "ok installed"; then
|
|
echo "Installing $actual_package_name (apt)..."
|
|
sudo apt install "$actual_package_name" -y
|
|
echo "$actual_package_name has been installed."
|
|
else
|
|
echo "$actual_package_name already installed."
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# Post-install for fd-find on Debian/Ubuntu (create symlink)
|
|
if [ "$PACKAGE_MANAGER" == "apt" ] && command -v fdfind &>/dev/null && ! command -v fd &>/dev/null; then
|
|
if dpkg-query -W -f='${Status}' "fd-find" 2>/dev/null | grep -q "ok installed"; then
|
|
echo "Creating symlink for fd from fdfind..."
|
|
sudo ln -sf /usr/bin/fdfind /usr/local/bin/fd
|
|
fi
|
|
fi
|
|
|
|
# Post-install for bat on Debian/Ubuntu (package ships the binary as 'batcat')
|
|
if [ "$PACKAGE_MANAGER" == "apt" ] && command -v batcat &>/dev/null && ! command -v bat &>/dev/null; then
|
|
echo "Creating symlink for bat from batcat..."
|
|
sudo ln -sf /usr/bin/batcat /usr/local/bin/bat
|
|
fi
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Upstream-only tooling (NOT packaged in Debian main / Fedora base repos)
|
|
# ---------------------------------------------------------------------------
|
|
# gh -> GitHub CLI apt repository
|
|
# terraform -> HashiCorp apt repository
|
|
# kubectl -> Kubernetes (pkgs.k8s.io) apt repository
|
|
# k9s -> GitHub release tarball
|
|
# minikube -> official .deb from Google's release bucket
|
|
# On Fedora these are a best-effort dnf attempt (usually need COPR/manual).
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_apt_add_gh_repo() {
|
|
[ -f /etc/apt/sources.list.d/github-cli.list ] && { echo "GitHub CLI repo already configured."; return 0; }
|
|
echo "Adding GitHub CLI apt repository..."
|
|
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
| sudo tee /usr/share/keyrings/githubcli-archive-keyring.gpg >/dev/null
|
|
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
| sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
|
|
}
|
|
|
|
_apt_add_hashicorp_repo() {
|
|
[ -f /etc/apt/sources.list.d/hashicorp.list ] && { echo "HashiCorp repo already configured."; return 0; }
|
|
echo "Adding HashiCorp apt repository..."
|
|
curl -fsSL https://apt.releases.hashicorp.com/gpg \
|
|
| sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
|
sudo chmod go+r /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(_get_codename) main" \
|
|
| sudo tee /etc/apt/sources.list.d/hashicorp.list >/dev/null
|
|
}
|
|
|
|
# Kubernetes apt repo. Auto-detects the latest stable minor from the
|
|
# kubernetes/kubernetes GitHub release feed, falling back to a pinned minor
|
|
# if the API is unreachable. Override explicitly with K8S_MINOR=...
|
|
K8S_MINOR_FALLBACK="v1.36"
|
|
_k8s_latest_minor() {
|
|
local tag minor
|
|
tag="$(curl -fsSL --max-time 10 https://api.github.com/repos/kubernetes/kubernetes/releases/latest \
|
|
| grep '"tag_name"' | cut -d'"' -f4)" || return 1
|
|
minor="${tag#v}" # e.g. 1.36.0
|
|
minor="${minor%.*}" # strip patch -> 1.36
|
|
echo "v${minor}"
|
|
}
|
|
_apt_add_kubernetes_repo() {
|
|
[ -f /etc/apt/sources.list.d/kubernetes.list ] && { echo "Kubernetes repo already configured."; return 0; }
|
|
K8S_MINOR="${K8S_MINOR:-$(_k8s_latest_minor || echo "$K8S_MINOR_FALLBACK")}"
|
|
echo "Adding Kubernetes apt repository (${K8S_MINOR})..."
|
|
curl -fsSL "https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/Release.key" \
|
|
| sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
|
|
sudo chmod go+r /usr/share/keyrings/kubernetes-archive-keyring.gpg
|
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${K8S_MINOR}/deb/ /" \
|
|
| sudo tee /etc/apt/sources.list.d/kubernetes.list >/dev/null
|
|
}
|
|
|
|
_install_k9s_bin() {
|
|
command -v k9s &>/dev/null && { echo "k9s already installed."; return 0; }
|
|
local K9S_VERSION K9S_URL TMPDIR UPSTREAM_ARCH
|
|
UPSTREAM_ARCH="$(_get_upstream_arch)"
|
|
[ "$UPSTREAM_ARCH" = "unsupported" ] && { echo "WARNING: unsupported arch for k9s."; return 1; }
|
|
K9S_VERSION="$(curl -fsSL https://api.github.com/repos/derailed/k9s/releases/latest | grep '"tag_name"' | cut -d'"' -f4)"
|
|
K9S_URL="https://github.com/derailed/k9s/releases/download/${K9S_VERSION}/k9s_Linux_${UPSTREAM_ARCH}.tar.gz"
|
|
echo "Downloading k9s ${K9S_VERSION}..."
|
|
TMPDIR="$(mktemp -d)"
|
|
if curl -fsSL -L "${K9S_URL}" -o "${TMPDIR}/k9s.tar.gz"; then
|
|
tar -C "${TMPDIR}" -xzf "${TMPDIR}/k9s.tar.gz" k9s
|
|
sudo install -m755 "${TMPDIR}/k9s" /usr/local/bin/k9s
|
|
echo "k9s ${K9S_VERSION} installed to /usr/local/bin/k9s"
|
|
else
|
|
echo "WARNING: Failed to download k9s."
|
|
fi
|
|
rm -rf "${TMPDIR}"
|
|
}
|
|
|
|
_install_minikube_deb() {
|
|
command -v minikube &>/dev/null && { echo "minikube already installed."; return 0; }
|
|
local UPSTREAM_ARCH TMPDIR
|
|
UPSTREAM_ARCH="$(_get_upstream_arch)"
|
|
[ "$UPSTREAM_ARCH" = "unsupported" ] && { echo "WARNING: unsupported arch for minikube."; return 1; }
|
|
echo "Downloading minikube (.deb)..."
|
|
TMPDIR="$(mktemp -d)"
|
|
if curl -fsSL -L "https://storage.googleapis.com/minikube/releases/latest/minikube_latest_${UPSTREAM_ARCH}.deb" -o "${TMPDIR}/minikube.deb"; then
|
|
sudo dpkg -i "${TMPDIR}/minikube.deb" 2>/dev/null || sudo apt-get install -f -y
|
|
echo "minikube installed from .deb."
|
|
else
|
|
echo "WARNING: Failed to download minikube."
|
|
fi
|
|
rm -rf "${TMPDIR}"
|
|
}
|
|
|
|
setup_upstream_tooling() {
|
|
echo ""
|
|
echo "Installing upstream-only tooling (gh, terraform, kubectl, k9s, minikube)..."
|
|
if [ "$PACKAGE_MANAGER" == "apt" ]; then
|
|
_apt_add_gh_repo
|
|
_apt_add_hashicorp_repo
|
|
_apt_add_kubernetes_repo
|
|
echo "Refreshing apt after adding vendor repos..."
|
|
sudo apt update
|
|
_apt_install gh
|
|
_apt_install terraform
|
|
_apt_install kubectl
|
|
_install_k9s_bin
|
|
_install_minikube_deb
|
|
else
|
|
# dnf best-effort: these usually need COPR/manual installs on Fedora.
|
|
for upstream_pkg in gh terraform kubectl k9s minikube; do
|
|
if ! rpm -q "$upstream_pkg" &>/dev/null; then
|
|
echo "Attempting $upstream_pkg (dnf)..."
|
|
if ! sudo dnf install -y "$upstream_pkg"; then
|
|
echo "WARNING: $upstream_pkg unavailable in dnf repos — install it via COPR/manually."
|
|
fi
|
|
else
|
|
echo "$upstream_pkg already installed."
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
setup_upstream_tooling
|
|
|
|
echo "Installing Flatpak Applications..."
|
|
if command -v flatpak &> /dev/null; then
|
|
for flatpak_name in "${FLATPAK_LIST[@]}"; do
|
|
if ! flatpak list --app | grep -q "$flatpak_name"; then
|
|
echo "Installing Flatpak $flatpak_name..."
|
|
flatpak install flathub "$flatpak_name" -y
|
|
echo "$flatpak_name has been installed."
|
|
else
|
|
echo "Flatpak $flatpak_name already installed."
|
|
fi
|
|
done
|
|
else
|
|
echo "WARNING: flatpak command not found. Skipping Flatpak app installation."
|
|
fi
|
|
|
|
echo "--- Package Installation Finished ---"
|
|
|