install split

This commit is contained in:
Jonas H
2026-03-19 08:02:02 +01:00
parent 508eddef62
commit 0b11a0d315
3 changed files with 354 additions and 212 deletions

205
install-deps.sh Executable file
View File

@@ -0,0 +1,205 @@
#!/usr/bin/env bash
# install-deps.sh: Install all system and tooling dependencies.
# Run this after cloning on a new machine (before install-stow.sh).
set -euo pipefail
DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
ok() { echo "$*"; }
info() { echo "$*"; }
warn() { echo "$*"; }
header() { echo ""; echo "==> $*"; }
have() { command -v "$1" &>/dev/null; }
# ---------------------------------------------------------------------------
# Detect Linux distribution
# ---------------------------------------------------------------------------
detect_distro() {
if [[ -f /etc/os-release ]]; then
# shellcheck source=/dev/null
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
DISTRO=$(detect_distro)
# ---------------------------------------------------------------------------
# System packages
# ---------------------------------------------------------------------------
header "System packages"
COMMON_PACKAGES=(
stow
swayidle
mako
libnotify
fuzzel
wireplumber
brightnessctl
alsa-utils
wf-recorder
slurp
zenity
jq
firefox
zoxide
)
case "$DISTRO" in
endeavouros|arch)
DISTRO_PACKAGES=(
pipewire-pulse
ffmpeg
)
PACKAGES=("${COMMON_PACKAGES[@]}" "${DISTRO_PACKAGES[@]}")
# Warn about pending upgrades (partial upgrades are unsupported on Arch)
sudo pacman -Sy --noconfirm &>/dev/null
updates=$(pacman -Qu 2>/dev/null | wc -l)
if [[ "$updates" -gt 0 ]]; then
warn "$updates system package(s) are out of date."
warn "If pacman fails with dependency conflicts, run 'sudo pacman -Syu'"
warn "locally (not over SSH) then re-run this script."
echo ""
fi
# Only install packages that aren't already present
MISSING=()
for pkg in "${PACKAGES[@]}"; do
if ! pacman -Q "$pkg" &>/dev/null; then
MISSING+=("$pkg")
else
ok "$pkg"
fi
done
if [[ ${#MISSING[@]} -gt 0 ]]; then
info "Installing: ${MISSING[*]}"
sudo pacman -S --noconfirm "${MISSING[@]}"
fi
;;
fedora|fedora-asahi-remix)
DISTRO_PACKAGES=(
pipewire-utils
ffmpeg
)
PACKAGES=("${COMMON_PACKAGES[@]}" "${DISTRO_PACKAGES[@]}")
MISSING=()
for pkg in "${PACKAGES[@]}"; do
if rpm -q "$pkg" &>/dev/null; then
ok "$pkg"
else
MISSING+=("$pkg")
fi
done
if [[ ${#MISSING[@]} -gt 0 ]]; then
info "Installing: ${MISSING[*]}"
sudo dnf install -y "${MISSING[@]}"
fi
;;
*)
warn "Unsupported distribution: $DISTRO — skipping system packages."
;;
esac
# ---------------------------------------------------------------------------
# Tools not in distro repos — warn if missing
# ---------------------------------------------------------------------------
header "Optional tools (manual install)"
warn_missing() {
local cmd="$1" msg="$2"
if have "$cmd"; then
ok "$cmd"
else
warn "'$cmd' not found — $msg"
fi
}
warn_missing autotiling "install via pip: pip install --user autotiling"
warn_missing eww "install from https://github.com/elkowar/eww/releases"
warn_missing wezterm "install from https://wezfurlong.org/wezterm/install/linux.html"
# ---------------------------------------------------------------------------
# Starship
# ---------------------------------------------------------------------------
header "Starship"
if have starship; then
ok "starship already installed"
else
info "Installing Starship..."
curl -sS https://starship.rs/install.sh | sh
fi
# ---------------------------------------------------------------------------
# npm global packages
# ---------------------------------------------------------------------------
header "npm global packages"
if npm list -g --depth=0 @mariozechner/pi-coding-agent &>/dev/null; then
ok "@mariozechner/pi-coding-agent already installed"
else
info "Installing @mariozechner/pi-coding-agent..."
npm install -g @mariozechner/pi-coding-agent
fi
# ---------------------------------------------------------------------------
# pi extension dependencies
# ---------------------------------------------------------------------------
header "pi extension node_modules"
for ext_dir in "$DOTFILES_DIR"/pi/.pi/agent/extensions/*/; do
[[ -f "$ext_dir/package.json" ]] || continue
ext_name="$(basename "$ext_dir")"
if [[ -d "$ext_dir/node_modules" ]]; then
ok "$ext_name (node_modules already present)"
else
info "Running npm install for $ext_name..."
npm install --prefix "$ext_dir" --ignore-scripts
fi
done
# ---------------------------------------------------------------------------
# Fonts
# ---------------------------------------------------------------------------
header "Fonts"
FONT_DIR="$HOME/.local/share/fonts/JetBrainsMono"
if [[ -d "$FONT_DIR" ]]; then
ok "JetBrains Mono Nerd Font already installed"
else
info "Installing JetBrains Mono Nerd Font..."
TMP="$(mktemp -d)"
curl -fLo "$TMP/JetBrainsMono.zip" \
"https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip"
mkdir -p "$FONT_DIR"
unzip -q "$TMP/JetBrainsMono.zip" -d "$FONT_DIR"
rm -rf "$TMP"
fc-cache -f "$FONT_DIR"
ok "Font installed"
fi
# ---------------------------------------------------------------------------
echo ""
echo "All dependencies done. Run ./install-stow.sh to link configs."

149
install-stow.sh Executable file
View File

@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# install-stow.sh: Stow dotfile packages into $HOME with per-package prompts.
# Packages that are already fully stowed are skipped automatically.
# Run after install-deps.sh.
set -euo pipefail
DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
ok() { echo "$*"; }
info() { echo "$*"; }
warn() { echo "$*"; }
header() { echo ""; echo "==> $*"; }
# Ask yes/no. Returns 0 for yes, 1 for no.
ask_yn() {
local prompt="$1"
while true; do
read -r -p "$prompt [y/N] " answer
case "${answer,,}" in
y|yes) return 0 ;;
n|no|"") return 1 ;;
*) echo " Please answer y or n." ;;
esac
done
}
# ---------------------------------------------------------------------------
# Check if a package is already fully stowed
# (every file in the package has a correct symlink in $HOME)
# ---------------------------------------------------------------------------
is_stowed() {
local pkg="$1"
local pkg_dir="$DOTFILES_DIR/$pkg"
local all_ok=true
while IFS= read -r -d '' src; do
local rel="${src#$pkg_dir/}"
local target="$HOME/$rel"
# Must exist as a symlink pointing at this exact source
if [[ ! -L "$target" ]] || [[ "$(readlink -f "$target")" != "$(readlink -f "$src")" ]]; then
all_ok=false
break
fi
done < <(find "$pkg_dir" -type f -print0 2>/dev/null)
$all_ok
}
# List any real files (non-symlinks) that would be overwritten by stowing
conflicts_for() {
local pkg="$1"
local pkg_dir="$DOTFILES_DIR/$pkg"
local conflicts=()
while IFS= read -r -d '' src; do
local rel="${src#$pkg_dir/}"
local target="$HOME/$rel"
if [[ -e "$target" ]] && [[ ! -L "$target" ]]; then
conflicts+=("$target")
fi
done < <(find "$pkg_dir" -type f -print0 2>/dev/null)
printf '%s\n' "${conflicts[@]}"
}
# ---------------------------------------------------------------------------
# Collect packages (top-level dirs, skip hidden)
# ---------------------------------------------------------------------------
header "Stowing dotfile packages"
PACKAGES=()
for d in "$DOTFILES_DIR"/*/; do
pkg="$(basename "$d")"
[[ "$pkg" == .* ]] && continue
PACKAGES+=("$pkg")
done
STOWED=()
SKIPPED=()
for pkg in "${PACKAGES[@]}"; do
echo ""
echo "Package: $pkg"
if is_stowed "$pkg"; then
ok "already stowed — skipping"
STOWED+=("$pkg (already)")
continue
fi
# Show conflicts, if any
mapfile -t conflicts < <(conflicts_for "$pkg")
if [[ ${#conflicts[@]} -gt 0 ]]; then
warn "Stowing would overwrite these real files:"
for f in "${conflicts[@]}"; do
echo " $f"
done
echo " Back them up or delete them first, then re-run."
fi
if ask_yn " Stow '$pkg'?"; then
stow --dir="$DOTFILES_DIR" --target="$HOME" --restow "$pkg"
ok "stowed"
STOWED+=("$pkg")
else
info "skipped"
SKIPPED+=("$pkg")
fi
done
# ---------------------------------------------------------------------------
# Configure shell (after stowing zshrc)
# ---------------------------------------------------------------------------
header "Shell configuration"
if [[ ! -f "$HOME/.zshrc" ]]; then
touch "$HOME/.zshrc"
fi
if ! grep -q 'eval "$(starship init zsh)"' "$HOME/.zshrc"; then
echo 'eval "$(starship init zsh)"' >> "$HOME/.zshrc"
ok "Added starship init to ~/.zshrc"
else
ok "Starship already configured in ~/.zshrc"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
header "Summary"
if [[ ${#STOWED[@]} -gt 0 ]]; then
echo " Stowed: ${STOWED[*]}"
fi
if [[ ${#SKIPPED[@]} -gt 0 ]]; then
echo " Skipped: ${SKIPPED[*]}"
fi
echo ""
echo "Done."

View File

@@ -1,212 +0,0 @@
#!/usr/bin/env bash
# install.sh: Install dependencies and stow all packages.
# Run this after cloning on a new machine.
set -euo pipefail
DOTFILES_DIR="$(cd "$(dirname "$0")" && pwd)"
# ---------------------------------------------------------------------------
# Detect Linux distribution
# ---------------------------------------------------------------------------
detect_distro() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
echo "$ID"
else
echo "unknown"
fi
}
DISTRO=$(detect_distro)
# ---------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------
COMMON_PACKAGES=(
stow
swayidle
mako
libnotify
fuzzel
wireplumber
brightnessctl
alsa-utils
wf-recorder
slurp
zenity
jq
firefox
zoxide
)
# On Arch-based systems, partial upgrades are unsupported — installing a
# package that pulls in a newer library breaks packages built against the old
# one. The right fix is 'sudo pacman -Syu', but doing that over SSH risks
# a dropped connection mid-kernel-upgrade. So we just warn and let the user
# decide.
check_arch_updates() {
sudo pacman -Sy --noconfirm # refresh databases only, no upgrade
local updates
updates=$(pacman -Qu 2>/dev/null | wc -l)
if [[ "$updates" -gt 0 ]]; then
echo ""
echo "WARNING: $updates system package(s) are out of date."
echo " If pacman fails with dependency conflicts, run 'sudo pacman -Syu'"
echo " locally (not over SSH, to avoid kernel upgrade risks), then re-run"
echo " this script."
echo ""
fi
}
case "$DISTRO" in
endeavouros|arch)
# Keep distro-specific packages clearly listed
DISTRO_PACKAGES=(
pipewire-pulse # Arch-specific
ffmpeg # Available in official repos
)
PACKAGES=("${COMMON_PACKAGES[@]}" "${DISTRO_PACKAGES[@]}")
echo "Installing packages for Arch/EndeavourOS..."
sudo pacman -S --noconfirm "${PACKAGES[@]}"
;;
fedora|fedora-asahi-remix)
DISTRO_PACKAGES=(
pipewire-utils # Fedora's package name
ffmpeg # Requires RPM Fusion
)
PACKAGES=("${COMMON_PACKAGES[@]}" "${DISTRO_PACKAGES[@]}")
echo "Installing packages for Fedora..."
sudo dnf install -y "${PACKAGES[@]}"
;;
*)
echo "ERROR: Unsupported distribution: $DISTRO"
exit 1
;;
esac
# ---------------------------------------------------------------------------
# Packages not in Fedora repos - install manually if missing
# ---------------------------------------------------------------------------
warn_missing() {
local cmd="$1" msg="$2"
if ! command -v "$cmd" &>/dev/null; then
echo "WARNING: '$cmd' not found - $msg"
fi
}
warn_missing autotiling "install via pip: pip install --user autotiling"
warn_missing eww "install from https://github.com/elkowar/eww/releases"
warn_missing wezterm "install from https://wezfurlong.org/wezterm/install/linux.html"
# ---------------------------------------------------------------------------
# Starship
# ---------------------------------------------------------------------------
if ! command -v starship &>/dev/null; then
echo ""
echo "Installing Starship..."
curl -sS https://starship.rs/install.sh | sh
else
echo ""
echo "Starship already installed, skipping."
fi
# ---------------------------------------------------------------------------
# NPM packages
# ---------------------------------------------------------------------------
echo ""
echo "Installing npm packages..."
npm install -g @mariozechner/pi-coding-agent
# ---------------------------------------------------------------------------
# Fonts
# ---------------------------------------------------------------------------
FONT_DIR="$HOME/.local/share/fonts/JetBrainsMono"
if [[ ! -d "$FONT_DIR" ]]; then
echo ""
echo "Installing JetBrains Mono Nerd Font..."
TMP="$(mktemp -d)"
curl -fLo "$TMP/JetBrainsMono.zip" \
"https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip"
mkdir -p "$FONT_DIR"
unzip -q "$TMP/JetBrainsMono.zip" -d "$FONT_DIR"
rm -rf "$TMP"
fc-cache -f "$FONT_DIR"
echo "Font installed."
else
echo "JetBrains Mono Nerd Font already installed, skipping."
fi
# ---------------------------------------------------------------------------
# Stow all packages
# ---------------------------------------------------------------------------
echo ""
echo "Stowing dotfiles..."
# Check for conflicts before stowing
CONFLICTS=()
for PACKAGE in "$DOTFILES_DIR"/*/; do
PACKAGE="$(basename "$PACKAGE")"
# Skip hidden directories and .git
[[ "$PACKAGE" == .* ]] && continue
# Check if package has files that would conflict
while IFS= read -r -d '' dotfile; do
# Get the target path this would create
TARGET_PATH="${HOME}/${dotfile#$DOTFILES_DIR/$PACKAGE/}"
# If target exists and is NOT a symlink to the correct location, it's a conflict
if [[ -e "$TARGET_PATH" ]] && [[ ! -L "$TARGET_PATH" ]]; then
CONFLICTS+=("$PACKAGE (file: $TARGET_PATH)")
fi
done < <(find "$DOTFILES_DIR/$PACKAGE" -type f -print0 2>/dev/null || true)
done
if [[ ${#CONFLICTS[@]} -gt 0 ]]; then
echo "WARNING: Found conflicts with existing configs:"
for conflict in "${CONFLICTS[@]}"; do
echo "$conflict"
done
echo ""
fi
# Stow all packages
for PACKAGE in "$DOTFILES_DIR"/*/; do
PACKAGE="$(basename "$PACKAGE")"
# Skip hidden directories
[[ "$PACKAGE" == .* ]] && continue
echo " $PACKAGE"
stow --dir="$DOTFILES_DIR" --target="$HOME" --restow "$PACKAGE"
done
# ---------------------------------------------------------------------------
# Configure shell (after stowing)
# ---------------------------------------------------------------------------
echo ""
echo "Configuring zsh..."
# Ensure .zshrc exists (stow should have created it via symlink)
if [[ ! -f "$HOME/.zshrc" ]]; then
touch "$HOME/.zshrc"
fi
# Ensure starship is initialized in .zshrc
if ! grep -q 'eval "$(starship init zsh)"' "$HOME/.zshrc"; then
echo 'eval "$(starship init zsh)"' >> "$HOME/.zshrc"
echo " Added starship initialization to ~/.zshrc"
else
echo " Starship already configured in ~/.zshrc"
fi
echo "Done."