Initial commit: linux-provision repo

Distribution-agnostic provisioning script that sets up a new Linux machine
(Detected via lib/distro.sh - supports Debian/Ubuntu/Pop and Fedora families).

13 stages covering:
- System packages, external repos, toolchains (nvm, uv, Python)
- Shell config (zsh, oh-my-zsh, p10k), git, SSH
- Custom uv tools from ~40 git repos
- Desktop config (keybindings, hotkeys, ghostty, fonts)
- Docker, system tweaks, browser/app installs
- Custom systemd user services (porridge, swayidle, mempi-sync, etc.)
- API keys loaded from Bitwarden at shell startup
This commit is contained in:
2026-06-05 21:21:46 +10:00
commit 180c5838ea
36 changed files with 4176 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
#!/usr/bin/env bash
# ===========================================================================
# bw-load-ssh.sh — Load SSH keys from Bitwarden into ssh-agent
# Reads SSH key items from Bitwarden vault and loads private keys into
# the running ssh-agent.
#
# Dependencies: bw (Bitwarden CLI), jq, ssh-agent running
# Usage:
# 1. First, authenticate: bw login
# 2. Unlock and save session: bw unlock --raw > ~/.config/Bitwarden\ CLI/.session
# 3. Run: ./bw-load-ssh.sh
# Or run automatically via bw-ssh-keys.service (systemd user service).
# ===========================================================================
set -euo pipefail
CONFIG_DIR="${HOME}/.config/Bitwarden CLI"
SESSION_FILE="${CONFIG_DIR}/.session"
# Check session file exists
if [ ! -f "$SESSION_FILE" ]; then
echo "ERROR: Session file not found at $SESSION_FILE"
echo "Run: bw unlock --raw > '$SESSION_FILE' && chmod 600 '$SESSION_FILE'"
exit 1
fi
# Check ssh-agent is running
if ! ssh-add -l >/dev/null 2>&1; then
echo "ERROR: ssh-agent is not running."
echo "Start it with: eval \$(ssh-agent)"
exit 1
fi
# Read session key
export BW_SESSION=$(cat "$SESSION_FILE")
# Verify session is still valid
if ! bw status 2>/dev/null | jq -e '.status == "unlocked"' >/dev/null 2>&1; then
echo "ERROR: Session is no longer valid (vault is locked or logged out)."
echo "Regenerate with: bw unlock --raw > '$SESSION_FILE' && chmod 600 '$SESSION_FILE'"
exit 1
fi
# Find all SSH key items
echo "Fetching SSH keys from vault..."
ITEMS=$(bw list items 2>/dev/null | jq -c '.[] | select(.type == 5)')
if [ -z "$ITEMS" ]; then
echo "No SSH keys found in vault."
exit 0
fi
LOADED=0
SKIPPED=0
echo "$ITEMS" | while IFS= read -r item; do
NAME=$(echo "$item" | jq -r '.name')
PUBLIC_KEY=$(echo "$item" | jq -r '.sshKey.publicKey // ""')
PRIVATE_KEY=$(echo "$item" | jq -r '.sshKey.privateKey // ""')
if [ -z "$PRIVATE_KEY" ]; then
echo " SKIP '$NAME' — no private key found"
continue
fi
# Extract comment from public key for checking if already loaded
COMMENT=$(echo "$PUBLIC_KEY" | awk '{print $3}' | tr -d '\n')
# Check if already loaded in ssh-agent
if [ -n "$COMMENT" ] && ssh-add -l 2>/dev/null | grep -q "$COMMENT"; then
echo " OK '$NAME' — already loaded"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Load into ssh-agent
if echo "$PRIVATE_KEY" | ssh-add - 2>/dev/null; then
echo " LOAD '$NAME'"
LOADED=$((LOADED + 1))
else
echo " FAIL '$NAME'"
fi
done
echo "Done. Loaded: $LOADED, Skipped (already loaded): $SKIPPED"