Julian Prester 5f8640c057 Simplify and clean up provisioning scripts
- lib/distro.sh: add DISTRO_CODENAME from VERSION_CODENAME; remove
  unused REPO_ADD_RPM variable; export DISTRO_CODENAME

- stages/01-repos.sh: replace repeated '. /etc/os-release' subshell
  calls with $DISTRO_CODENAME in Docker and Tailscale repo lines

- stages/00-envcheck.sh: combine four mkdir -p calls into one

- stages/02-packages.sh: remove identical if/else branches in fd
  symlink block; both branches were the same command

- stages/03-toolchains.sh: set NVM_DIR once before the if/else instead
  of twice; remove dead commented-out duplicate curl line

- stages/04-shell.sh: capture $(date +%Y%m%d) into _bak_date once per
  backup and reuse in cp and warn to avoid redundant forks

- stages/06-scripts.sh: split 'export VAR=$(cmd)' into assignment +
  export to correctly propagate errors under set -euo pipefail

- stages/07-uv-projects.sh: remove unused has_package_json field (always
  false, never read); fix stage completion message 06 -> 07

- stages/08-systemd.sh: remove mempi-sync.timer (deploy, enable, header
  comment); deploy mempi-sync.service only

- stages/09-desktop.sh: remove duplicate mkdir -p in COSMIC section;
  remove unused repo_url parameter from install_nerd_font()

- stages/12-other-apps.sh: replace manual distro branch for Nextcloud
  with pkg_install_mapped

- config/scripts/bw-load-ssh.sh: split 'export BW_SESSION=$(cat ...)'
  into assignment + export

- config/systemd/mempi-sync.timer: delete file
2026-06-07 15:18:12 +10:00

Linux Machine Provisioning

Purpose: Rapidly set up a new Fedora Linux machine with Julian's toolchain, config, and customisations. Generated from audit of Pop!_OS "thinkpad" machine.

Supported Distributions

Auto-detected — no manual config needed:

Family Distros Package Manager
Debian Ubuntu, Pop!_OS, Debian, Linux Mint apt
Fedora Fedora Workstation, RHEL, CentOS dnf

Detection is in lib/distro.sh. It sets variables like $PKG_INSTALL, $PKG_UPDATE, $GRUB_UPDATE etc. that all stage scripts use.

Quick Start

# Clone this repo on the new machine
git clone git@github.com:julianprester/linux-provision.git ~/linux-provision
cd ~/linux-provision

# Review and edit config/shell/zshrc.local with your API keys
cp config/shell/zshrc.local.example ~/.zshrc.local
# Edit ~/.zshrc.local with real API keys

# Run the full provisioning (will prompt for sudo)
bash provision.sh --all

# Or run individual stages
bash provision.sh --stage 03-toolchains
bash provision.sh --stage 06-uv-projects

# Or source it for interactive use
source provision.sh --interactive

Structure

linux-provision/
├── README.md              # This file
├── provision.sh           # Master orchestrator — run with --all or --stage N
├── stages/                # Modular stage scripts, sourced by provision.sh
│   ├── 00-envcheck.sh     # OS/sudo/environment checks
│   ├── 01-repos.sh        # DNF repos (RPM Fusion, COPR, Microsoft, etc.)
│   ├── 02-packages.sh     # System packages via DNF
│   ├── 03-toolchains.sh   # nvm, Node, uv, Python
│   ├── 04-shell.sh        # zsh, oh-my-zsh, powerlevel10k, configs
│   ├── 05-git.sh          # Git config, SSH key setup
│   ├── 06-scripts.sh      # ~/.local/bin (bw, zoom, env, etc.)
│   ├── 07-uv-projects.sh  # Clone + install Julian's uv tools from ~/Development
│   ├── 08-systemd.sh      # User systemd services (porridge, swayidle, etc.)
│   ├── 09-desktop.sh      # Keybindings, hotkeys, ghostty, fonts
│   ├── 10-docker.sh       # Docker CE setup
│   ├── 11-tweaks.sh       # sysctl, kernel params, TLP/powertop, modprobe
│   └── 12-other-apps.sh   # Chrome, Signal, Zotero
├── config/                # Dotfiles and config files (installed by stages)
│   ├── git/gitconfig
│   ├── shell/{zshrc,zshrc.local.example,p10k.zsh}
│   ├── scripts/{bw-load-ssh.sh,idle-battery-suspend.sh,zoom.sh,env.sh}
│   ├── 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

Stages Overview

# Stage What it does
00 envcheck Verify Fedora, sudo access, directory setup
01 repos RPM Fusion free/nonfree, COPRs, Microsoft, Docker, Google, Signal, Tailscale
02 packages Install all system packages (distro-mapped names)
03 toolchains Install nvm + Node LTS, uv, Python
04 shell Install zsh, oh-my-zsh, p10k, deploy .zshrc, .p10k.zsh
05 git Deploy .gitconfig, generate SSH key
06 scripts Deploy ~/.local/bin scripts (bw, bw-load-ssh, Zoom wrapper, etc.)
07 uv-projects Clone all Julian's Python tool repos from GitHub, uv install (needs SSH keys)
08 systemd Deploy and enable user systemd services
09 desktop Configure keybindings, hotkeys, ghostty, fonts
10 docker Install Docker CE, add user to docker group
11 tweaks sysctl, kernel cmdline, TLP/powertop, modprobe blacklists
12 other-apps Google Chrome, Signal, Zotero

Post-Install Manual Steps

See TODO.md for things that can't be automated: restoring SSH keys from Bitwarden, configuring Tailscale, importing GPG keys, etc.

Design Notes

  • Distribution-agnostic — detects Debian/Ubuntu/Pop vs Fedora via lib/distro.sh. Package manager commands, repo config, and package names adapt automatically.
  • Idempotent — safe to run multiple times. Stages check for existing installations before repeating work.
  • Secrets out of repo — API keys live in ~/.zshrc.local (gitignored). The repo ships zshrc.local.example with placeholder values.
  • One stage per concern — comment out stages you don't need in provision.sh or pass --stage individually.
  • Hardware-specific quirks commented out — AMD GPU kernel params, WiFi workarounds, etc. are documented but disabled by default.
Description
No description provided
Readme 297 KiB
Languages
Shell 100%