Compare commits

..

12 Commits

Author SHA1 Message Date
05a422376f clean up 2026-06-08 22:00:31 +10:00
bc3ab33c0a fix socket path 2026-06-08 21:47:48 +10:00
c0c25c44b8 fix env population 2026-06-08 21:46:59 +10:00
07d9d6364e Remove idle-battery-suspend.sh from stage 06 2026-06-08 21:10:40 +10:00
99c18198f4 Drop swayidle and mempi-sync service references 2026-06-08 21:09:41 +10:00
59dd65950d Fix SSH agent and systemd service race conditions
- bw-ssh-keys.service: use ssh-agent.service, add SSH_AUTH_SOCK env
- Stage 08: enable ssh-agent.socket, mask gcr-ssh-agent before services
- Stage 11: remove SSH agent section (moved to stage 08 for ordering)
- Stage 09: remove stale bw-load-ssh autostart config
- .zshrc: export SSH_AUTH_SOCK to match OpenSSH agent socket
- Remove config/autostart/ (no longer needed)
- porridge daemon: no longer exits on missing API key;
  add SIGHUP handler for live config reload
2026-06-08 21:08:16 +10:00
1cc2a52a17 feat: add system package upgrade to stage 00
- Add PKG_UPGRADE variable to lib/distro.sh:
  - Debian: sudo apt upgrade -y
  - Fedora: sudo dnf upgrade -y
- Export PKG_UPGRADE so stage scripts can use it
- Add upgrade step to 00-envcheck.sh after the existing cache update
  - Errors are non-fatal (|| warn) to handle fresh installs gracefully
2026-06-07 17:06:19 +10:00
56d84b5ffd minor edits 2026-06-07 16:22:08 +10:00
e5b0bc9e0d move quick start script to tmp folder 2026-06-07 16:21:56 +10:00
beba5dd350 README: fix repo name linux-provision -> linux-provisioning 2026-06-07 15:39:16 +10:00
2ee054bd77 README: switch to self-hosted Gitea archive URL 2026-06-07 15:38:05 +10:00
a79d2b3fa2 README: use curl+tar zip download instead of git clone
Works on a fresh machine with no credentials or SSH keys configured.
GitHub serves public archives without authentication.
2026-06-07 15:37:01 +10:00
11 changed files with 54 additions and 74 deletions

View File

@@ -18,7 +18,7 @@ Detection is in `lib/distro.sh`. It sets variables like `$PKG_INSTALL`,
## Quick Start ## Quick Start
```bash ```bash
git clone https://git.julianprester.com/julianprester/linux-provision.git ~/linux-provision && bash ~/linux-provision/provision.sh --all curl -fsSL https://git.julianprester.com/julianprester/linux-provisioning/archive/main.tar.gz | tar -xz -C /tmp/ && bash /tmp/linux-provisioning/provision.sh --all
``` ```
Or run individual stages: Or run individual stages:
@@ -44,7 +44,7 @@ linux-provision/
│ ├── 05-git.sh # Git config, SSH key setup │ ├── 05-git.sh # Git config, SSH key setup
│ ├── 06-scripts.sh # ~/.local/bin (bw, zoom, env, etc.) │ ├── 06-scripts.sh # ~/.local/bin (bw, zoom, env, etc.)
│ ├── 07-uv-projects.sh # Clone + install Julian's uv tools from ~/Development │ ├── 07-uv-projects.sh # Clone + install Julian's uv tools from ~/Development
│ ├── 08-systemd.sh # User systemd services (porridge, swayidle, etc.) │ ├── 08-systemd.sh # User systemd services (porridge, BW SSH, etc.)
│ ├── 09-desktop.sh # Keybindings, hotkeys, ghostty, fonts │ ├── 09-desktop.sh # Keybindings, hotkeys, ghostty, fonts
│ ├── 10-docker.sh # Docker CE setup │ ├── 10-docker.sh # Docker CE setup
│ ├── 11-tweaks.sh # sysctl, kernel params, TLP/powertop, modprobe │ ├── 11-tweaks.sh # sysctl, kernel params, TLP/powertop, modprobe
@@ -53,10 +53,7 @@ linux-provision/
│ ├── git/gitconfig │ ├── git/gitconfig
│ ├── shell/{zshrc,p10k.zsh} │ ├── shell/{zshrc,p10k.zsh}
│ ├── scripts/{bw-load-ssh.sh,idle-battery-suspend.sh,zoom.sh,env.sh} │ ├── scripts/{bw-load-ssh.sh,idle-battery-suspend.sh,zoom.sh,env.sh}
── systemd/{porridge.service,...} ── systemd/{porridge.service,...}
│ ├── sysctl/99-custom.conf
│ └── modprobe/{system76-power.conf,pop-default-settings-dirty-frag.conf}
├── etc/ # System-level configs (copied to /etc)
└── TODO.md # Post-provisioning manual steps └── TODO.md # Post-provisioning manual steps
``` ```

View File

@@ -22,6 +22,10 @@ fi
# ---- PATH setup ---- # ---- PATH setup ----
export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH
# ---- SSH agent socket ----
# Match the socket used by ssh-agent.socket (OpenSSH), not GCR/gnome-keyring.
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
# ---- Oh My Zsh ---- # ---- Oh My Zsh ----
export ZSH="$HOME/.oh-my-zsh" export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="powerlevel10k/powerlevel10k" ZSH_THEME="powerlevel10k/powerlevel10k"
@@ -69,7 +73,11 @@ if [ -z "${BW_SESSION:-}" ] && [ -f "$HOME/.config/Bitwarden CLI/.session" ]; th
fi fi
if [ -n "${BW_SESSION:-}" ]; then if [ -n "${BW_SESSION:-}" ]; then
eval "$(bw get item Environment 2>/dev/null | jq -r ' eval "$(bw get item Environment 2>/dev/null | jq -r '
.fields[] | select(.type != 0) | .fields[] | select(.type == 0) |
"export " + (.name | gsub(" "; "_")) + "=" + (.value | @sh) "export " + (.name | gsub(" "; "_")) + "=" + (.value | @sh)
' 2>/dev/null)" 2>/dev/null ' 2>/dev/null)" 2>/dev/null
fi fi
# Signal porridge daemon to reload config (picks up newly loaded env vars)
_pidfile="$HOME/.local/state/porridge/daemon.pid"
[[ -f "$_pidfile" ]] && kill -HUP "$(cat "$_pidfile")" 2>/dev/null || true

View File

@@ -1,11 +1,14 @@
[Unit] [Unit]
Description=Load Bitwarden SSH keys into ssh-agent Description=Load Bitwarden SSH keys into ssh-agent
After=graphical-session.target # Use ssh-agent.service (OpenSSH) instead of GCR/gnome-keyring SSH agent
Wants=graphical-session.target # to avoid conflicts — keys are loaded into the socket the terminal sees.
After=ssh-agent.service
Wants=ssh-agent.service
[Service] [Service]
Type=oneshot Type=oneshot
Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin Environment=PATH=%h/.local/bin:/usr/local/bin:/usr/bin:/bin
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=%h/.local/bin/bw-load-ssh.sh ExecStart=%h/.local/bin/bw-load-ssh.sh
RemainAfterExit=yes RemainAfterExit=yes
Restart=on-failure Restart=on-failure

View File

@@ -1,6 +0,0 @@
[Unit]
Description=Sync mempi database to Nextcloud
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'sqlite3 %h/.local/share/pi/pi.db "PRAGMA wal_checkpoint(TRUNCATE)" && cp %h/.local/share/pi/pi.db "%h/Nextcloud/2_resources/90-99 Misc/98 ocpa/mempi-pi.db"'

View File

@@ -59,6 +59,7 @@ fi
if [ "$DISTRO_FAMILY" = "debian" ]; then if [ "$DISTRO_FAMILY" = "debian" ]; then
PKG_MGR="apt" PKG_MGR="apt"
PKG_UPDATE="sudo apt update" PKG_UPDATE="sudo apt update"
PKG_UPGRADE="sudo apt upgrade -y"
PKG_INSTALL="sudo apt install -y" PKG_INSTALL="sudo apt install -y"
PKG_INSTALL_NO_REC="sudo apt install -y --no-install-recommends" PKG_INSTALL_NO_REC="sudo apt install -y --no-install-recommends"
PKG_REMOVE="sudo apt remove -y" PKG_REMOVE="sudo apt remove -y"
@@ -88,6 +89,7 @@ if [ "$DISTRO_FAMILY" = "debian" ]; then
elif [ "$DISTRO_FAMILY" = "fedora" ]; then elif [ "$DISTRO_FAMILY" = "fedora" ]; then
PKG_MGR="dnf" PKG_MGR="dnf"
PKG_UPDATE="sudo dnf makecache" PKG_UPDATE="sudo dnf makecache"
PKG_UPGRADE="sudo dnf upgrade -y"
PKG_INSTALL="sudo dnf install -y" PKG_INSTALL="sudo dnf install -y"
PKG_INSTALL_NO_REC="sudo dnf install -y" # dnf has no --no-install-recommends equivalent PKG_INSTALL_NO_REC="sudo dnf install -y" # dnf has no --no-install-recommends equivalent
PKG_REMOVE="sudo dnf remove -y" PKG_REMOVE="sudo dnf remove -y"
@@ -185,7 +187,7 @@ pkg_install_mapped() {
# Export all variables so sourced stages can use them # Export all variables so sourced stages can use them
export DISTRO_FAMILY DISTRO_ID DISTRO_VERSION DISTRO_CODENAME DISTRO_LIKE export DISTRO_FAMILY DISTRO_ID DISTRO_VERSION DISTRO_CODENAME DISTRO_LIKE
export PKG_MGR PKG_UPDATE PKG_INSTALL PKG_INSTALL_NO_REC export PKG_MGR PKG_UPDATE PKG_UPGRADE PKG_INSTALL PKG_INSTALL_NO_REC
export PKG_REMOVE PKG_PURGE PKG_AUTOREMOVE export PKG_REMOVE PKG_PURGE PKG_AUTOREMOVE
export PKG_SEARCH PKG_LIST_INSTALLED export PKG_SEARCH PKG_LIST_INSTALLED
export SERVICE_ENABLE GRUB_UPDATE GRUB_EFI_UPDATE GRUB_FILE export SERVICE_ENABLE GRUB_UPDATE GRUB_EFI_UPDATE GRUB_FILE

View File

@@ -36,4 +36,8 @@ fi
info "Updating package cache (first run)..." info "Updating package cache (first run)..."
$PKG_UPDATE 2>/dev/null || warn "Package cache update had issues." $PKG_UPDATE 2>/dev/null || warn "Package cache update had issues."
# ---- System upgrade ----
info "Upgrading all system packages..."
$PKG_UPGRADE 2>/dev/null || warn "System upgrade had issues (this is okay on a fresh install)."
ok "Stage 00 complete." ok "Stage 00 complete."

View File

@@ -100,12 +100,6 @@ EOF
ok " Signal repo added." ok " Signal repo added."
fi fi
# ---- Papirus icon theme (PPA) ----
info " Adding Papirus PPA..."
if [ ! -f /etc/apt/sources.list.d/papirus-ubuntu-papirus-*.sources ]; then
$REPO_ADD_PPA papirus/papirus 2>/dev/null && ok " Papirus PPA added." || warn " Papirus PPA failed."
fi
# ---- Solaar (Logitech) PPA ---- # ---- Solaar (Logitech) PPA ----
info " Adding Solaar PPA..." info " Adding Solaar PPA..."
if [ ! -f /etc/apt/sources.list.d/solaar-unifying-ubuntu-stable-*.sources ]; then if [ ! -f /etc/apt/sources.list.d/solaar-unifying-ubuntu-stable-*.sources ]; then
@@ -126,10 +120,6 @@ EOF
ok " Zotero repo added." ok " Zotero repo added."
fi fi
# ---- Yubico PPA ----
info " Adding Yubico PPA..."
$REPO_ADD_PPA yubico/stable 2>/dev/null && ok " Yubico PPA added." || warn " Yubico PPA failed."
# ================================================================== # ==================================================================
# FEDORA / RHEL — DNF-based # FEDORA / RHEL — DNF-based
# ================================================================== # ==================================================================
@@ -184,14 +174,11 @@ EOF
info " Note: Signal will be installed via Flatpak in stage 12." info " Note: Signal will be installed via Flatpak in stage 12."
# ---- COPRs for extra packages ---- # ---- COPRs for extra packages ----
# Papirus icon theme is in RPM Fusion nonfree.
# Solaar is in RPM Fusion. # Solaar is in RPM Fusion.
# Yubico tools: use COPR
info " Adding COPR repos..." info " Adding COPR repos..."
# Ghostty terminal emulator # Ghostty terminal emulator
$REPO_ADD_COPR scottames/ghostty 2>/dev/null && ok " Ghostty COPR added." || warn " Ghostty COPR failed." $REPO_ADD_COPR scottames/ghostty 2>/dev/null && ok " Ghostty COPR added." || warn " Ghostty COPR failed."
# $REPO_ADD_COPR atim/papirus-icon-theme 2>/dev/null || true $REPO_ADD_COPR sergiomb/Solaar 2>/dev/null || true
# $REPO_ADD_COPR sergiomb/Solaar 2>/dev/null || true
# ---- Zotero — no DNF repo, use Flatpak (handled in stage 13) ---- # ---- Zotero — no DNF repo, use Flatpak (handled in stage 13) ----
info " Note: Zotero will be installed via Flatpak or tarball in stage 13." info " Note: Zotero will be installed via Flatpak or tarball in stage 13."

View File

@@ -2,7 +2,7 @@
# =========================================================================== # ===========================================================================
# Stage 06: Custom Scripts (~/.local/bin/) # Stage 06: Custom Scripts (~/.local/bin/)
# Deploys Julian's custom scripts: Bitwarden SSH loader, Zoom wrapper, # Deploys Julian's custom scripts: Bitwarden SSH loader, Zoom wrapper,
# idle battery suspend, env PATH helper, and more. # env PATH helper, and more.
# =========================================================================== # ===========================================================================
# These are the "glue" scripts that make the desktop work the way Julian # These are the "glue" scripts that make the desktop work the way Julian
# expects. They were found in ~/.local/bin/ on the Pop machine. # expects. They were found in ~/.local/bin/ on the Pop machine.
@@ -56,16 +56,7 @@ SCRIPT
ok "zoom wrapper created (default)." ok "zoom wrapper created (default)."
fi fi
# ---- 4. idle-battery-suspend.sh — Suspend on battery after idle ---- # ---- 4. Bitwarden CLI (bw) ----
# Checks if AC is disconnected before suspending. Prevents suspend on desktop.
# Used by swayidle.service (stage 08).
if [ -f "$SCRIPTS_DIR/idle-battery-suspend.sh" ]; then
cp "$SCRIPTS_DIR/idle-battery-suspend.sh" "$TARGET_DIR/idle-battery-suspend.sh"
chmod +x "$TARGET_DIR/idle-battery-suspend.sh"
ok "idle-battery-suspend.sh deployed."
fi
# ---- 5. Bitwarden CLI (bw) ----
# On Pop: ~/.local/bin/bw (138 MB standalone binary) # On Pop: ~/.local/bin/bw (138 MB standalone binary)
if ! command -v bw &>/dev/null; then if ! command -v bw &>/dev/null; then
info "Installing Bitwarden CLI..." info "Installing Bitwarden CLI..."

View File

@@ -3,12 +3,11 @@
# Stage 08: User Systemd Services # Stage 08: User Systemd Services
# Deploys and enables Julian's custom user systemd services. # Deploys and enables Julian's custom user systemd services.
# =========================================================================== # ===========================================================================
# On the Pop machine, Julian runs several custom services: # Custom user services:
# - porridge.service : Zoom meeting transcriber daemon # - porridge.service : Zoom meeting transcriber daemon
# - porridge-dictate.service : Push-to-talk transcription # - porridge-dictate.service : Push-to-talk transcription
# - pi-overview.service : Session dashboard on port 3000 # - pi-overview.service : Session dashboard on port 3000
# - bw-ssh-keys.service : Load Bitwarden SSH keys at boot # - bw-ssh-keys.service : Load Bitwarden SSH keys into ssh-agent
# - mempi-sync.service : Sync memory DB to Nextcloud
# - empty_downloads.service : Clear Downloads folder at login # - empty_downloads.service : Clear Downloads folder at login
# =========================================================================== # ===========================================================================
@@ -18,6 +17,27 @@ UNIT_DIR="$HOME/.config/systemd/user"
mkdir -p "$UNIT_DIR" mkdir -p "$UNIT_DIR"
# ===========================================================================
# 0. SSH Agent setup — ensure OpenSSH ssh-agent is the active agent
# ===========================================================================
# Enable ssh-agent.socket and disable/mask the GCR SSH agent so that
# bw-ssh-keys.service (below) loads keys into the same agent the user's
# terminal sees. Must run BEFORE enabling bw-ssh-keys.service.
if [ "$DISTRO_FAMILY" = "fedora" ]; then
info "Setting up OpenSSH ssh-agent..."
systemctl --user enable --now ssh-agent.socket 2>/dev/null && \
ok "ssh-agent.socket enabled." || \
warn "ssh-agent.socket not available."
if systemctl --user list-unit-files gcr-ssh-agent.service &>/dev/null 2>&1; then
systemctl --user disable --now gcr-ssh-agent.socket gcr-ssh-agent.service 2>/dev/null || true
systemctl --user mask --now gcr-ssh-agent.socket gcr-ssh-agent.service 2>/dev/null && \
ok "gcr-ssh-agent disabled (masked)." || \
warn "Could not mask gcr-ssh-agent."
fi
fi
info "Deploying user systemd services..." info "Deploying user systemd services..."
# ---- Helper: install service file ---- # ---- Helper: install service file ----
@@ -41,12 +61,9 @@ install_service_file "$SERVICES_DIR/porridge-dictate.service" "porridge-dictate.
# ---- 3. pi-overview.service — Session dashboard ---- # ---- 3. pi-overview.service — Session dashboard ----
install_service_file "$SERVICES_DIR/pi-overview.service" "pi-overview.service" install_service_file "$SERVICES_DIR/pi-overview.service" "pi-overview.service"
# ---- 4. bw-ssh-keys.service — Load Bitwarden SSH keys at boot ---- # ---- 4. bw-ssh-keys.service — Load Bitwarden SSH keys into ssh-agent ----
install_service_file "$SERVICES_DIR/bw-ssh-keys.service" "bw-ssh-keys.service" install_service_file "$SERVICES_DIR/bw-ssh-keys.service" "bw-ssh-keys.service"
# ---- 5. mempi-sync.service + timer — Sync memory DB to Nextcloud ----
install_service_file "$SERVICES_DIR/mempi-sync.service" "mempi-sync.service"
# ---- 6. empty_downloads.service — Clear Downloads at login ---- # ---- 6. empty_downloads.service — Clear Downloads at login ----
install_service_file "$SERVICES_DIR/empty_downloads.service" "empty_downloads.service" install_service_file "$SERVICES_DIR/empty_downloads.service" "empty_downloads.service"
@@ -56,9 +73,6 @@ info "Enabling and starting services..."
# Services that should start automatically (enabled) # Services that should start automatically (enabled)
systemctl --user daemon-reload systemctl --user daemon-reload
# Check which scripts from stages 06 and 07 are available before enabling services.
# This avoids failures when running stages out of order.
if [ -x "$HOME/.local/bin/porridge" ]; then if [ -x "$HOME/.local/bin/porridge" ]; then
systemctl --user enable --now porridge.service 2>/dev/null && ok "porridge.service enabled" systemctl --user enable --now porridge.service 2>/dev/null && ok "porridge.service enabled"
else else
@@ -80,13 +94,13 @@ fi
if [ -f "$HOME/.local/bin/bw-load-ssh.sh" ]; then if [ -f "$HOME/.local/bin/bw-load-ssh.sh" ]; then
systemctl --user enable bw-ssh-keys.service 2>/dev/null && ok "bw-ssh-keys.service enabled" systemctl --user enable bw-ssh-keys.service 2>/dev/null && ok "bw-ssh-keys.service enabled"
else else
warn "bw-ssh-keys.service skipped (script not found — run stage 06 first)." warn "bw-ssh-keys.service skipped (bw-load-ssh.sh not found — run stage 06 first)."
fi fi
systemctl --user enable --now empty_downloads.service 2>/dev/null && ok "empty_downloads.service enabled" || warn "empty_downloads.service not started." systemctl --user enable --now empty_downloads.service 2>/dev/null && ok "empty_downloads.service enabled" || warn "empty_downloads.service not started."
info "===== Service Status =====" info "===== Service Status ====="
systemctl --user list-units --type=service --state=running 2>/dev/null | grep -E "(porridge|swayidle|pi-overview|mempi|bw-ssh|empty)" || true systemctl --user list-units --type=service --state=running 2>/dev/null | grep -E "(porridge|pi-overview|bw-ssh|empty)" || true
ok "Stage 08 complete: user systemd services deployed." ok "Stage 08 complete: user systemd services deployed."

View File

@@ -168,7 +168,7 @@ autostart_app() {
fi fi
} }
# Apps to autostart at login # Apps to autostart at login (from system .desktop files)
autostart_app "firefox" "org.mozilla.firefox.desktop" autostart_app "firefox" "org.mozilla.firefox.desktop"
autostart_app "ghostty" "com.mitchellh.ghostty.desktop" autostart_app "ghostty" "com.mitchellh.ghostty.desktop"
autostart_app "nextcloud" "com.nextcloud.desktopclient.nextcloud.desktop" autostart_app "nextcloud" "com.nextcloud.desktopclient.nextcloud.desktop"
@@ -217,22 +217,3 @@ install_nerd_font "FiraCode"
# Rebuild font cache # Rebuild font cache
fc-cache -f "$FONT_DIR" 2>/dev/null || true fc-cache -f "$FONT_DIR" 2>/dev/null || true
ok "Font cache rebuilt." ok "Font cache rebuilt."
# ===========================================================================
# 7. GTK Theme (dark mode + Papirus icons)
# ===========================================================================
info "Setting GTK theme and icons..."
gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark' 2>/dev/null || true
gsettings set org.gnome.desktop.interface gtk-theme 'adw-gtk3-dark' 2>/dev/null || true
gsettings set org.gnome.desktop.interface icon-theme 'Papirus-Dark' 2>/dev/null || true
ok "GTK dark theme + Papirus icons set."
# ===========================================================================
# 8. Solaar (Logitech peripherals) config
# ===========================================================================
# The Pop machine had config for MX Keys Mini + MX Master 3.
# Solaar config is auto-generated when you pair devices. The config
# template (if supplied) can be placed at ~/.config/solaar/config.yaml
info "Solaar config will be generated automatically when you pair devices."
ok "Stage 09 complete: desktop configured."

View File

@@ -41,5 +41,4 @@ EOF
fi fi
$SERVICE_ENABLE powertop 2>/dev/null && ok "PowerTOP auto-tune enabled." || true $SERVICE_ENABLE powertop 2>/dev/null && ok "PowerTOP auto-tune enabled." || true
fi fi
ok "Stage 11 complete: system tweaks applied." ok "Stage 11 complete: system tweaks applied."