diff --git a/packs/tmux/README.md b/packs/tmux/README.md index 7081e2e..953d50a 100644 --- a/packs/tmux/README.md +++ b/packs/tmux/README.md @@ -7,10 +7,11 @@ What this pack installs: -- `install-010-core.sh` -- tmux via apt; enforces version floor >= 3.2 -- `install-020-config.sh` -- symlinks the team `tmux.conf` to `~/.tmux.conf` -- `install-030-tpm.sh` -- clones TPM (plugin manager); then `prefix + I` - inside tmux installs the team plugin set +- `install-010-core.sh` -- tmux via apt; enforces version floor >= 3.2 +- `install-020-config.sh` -- symlinks the team `tmux.conf` to `~/.tmux.conf` +- `install-030-tpm.sh` -- clones TPM (the plugin manager itself) +- `install-040-plugins.sh` -- installs the team plugin set via TPM's + `bin/install_plugins` (scripted `prefix + I`) Key decisions (debate welcome, change via PR): @@ -23,6 +24,12 @@ - Plugins: `tmux-yank` (clipboard copy), `tmux-prefix-highlight` (visual cue when the prefix is armed). Additions go through `plugins.list` + the matching `@plugin` line in `files/tmux.conf`. +- Plugins install non-interactively (`install-040-plugins.sh`) rather than + leaving `prefix + I` to the user. A fresh clone that stopped at TPM looked + installed but was not: `tmux-yank` was silently dead and the + `#{prefix_highlight}` in `status-right` rendered empty, which reads as a + broken status bar rather than a missing step. `prefix + I` still works and + is still the way to pick up plugins added mid-session. Daily driver commands: diff --git a/packs/tmux/install-040-plugins.sh b/packs/tmux/install-040-plugins.sh new file mode 100644 index 0000000..43220b2 --- /dev/null +++ b/packs/tmux/install-040-plugins.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# packs/tmux/install-040-plugins.sh -- install the team TPM plugin set. +# +# The scriptable equivalent of pressing prefix + I inside tmux, so a fresh +# clone converges from bootstrap.sh alone instead of leaving TPM installed +# but the plugin set absent (yank silently dead, prefix_highlight empty). +# +# Mutates: clones every @plugin declared in files/tmux.conf into +# ~/.tmux/plugins/, via TPM's own bin/install_plugins. Plugins already +# present are left untouched. Side effect: TPM resolves its plugin path by +# asking tmux ("tmux start-server \; show-environment"), so this starts a +# tmux server if none is running; running sessions are not modified. +# +# Usage: bash install-040-plugins.sh [TOOLKIT_DRY_RUN=1 to preview] +# Exit codes: 0 PROCEED, 1 HOLD (a plugin failed to download), +# 2 precondition missing (tmux, git, TPM, or config absent). +# ASCII + LF. + +set -euo pipefail +shopt -s inherit_errexit 2>/dev/null || true +IFS=$'\n\t' +PACK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +. "$PACK_DIR/../../lib/lib-install.sh" + +require_cmd git "apt-get install git" +require_cmd tmux "run install-010-core.sh first" + +INSTALLER="$HOME/.tmux/plugins/tpm/bin/install_plugins" +if [ ! -x "$INSTALLER" ]; then + fail "TPM not installed: $INSTALLER missing (run install-030-tpm.sh first)" + exit 2 +fi + +# The @plugin lines in this pack's tmux.conf are authoritative (plugins.list +# mirrors them for humans). TPM itself reads the LIVE config at ~/.tmux.conf, +# so if that is not our symlink the set it installs would differ from ours. +CONF="$PACK_DIR/files/tmux.conf" +LIVE_CONF="$HOME/.tmux.conf" + +if [ ! -e "$LIVE_CONF" ]; then + fail "$LIVE_CONF missing (run install-020-config.sh first)" + exit 2 +fi +if [ "$(readlink -f "$LIVE_CONF" || true)" != "$(readlink -f "$CONF" || true)" ]; then + warn "$LIVE_CONF is not this pack's tmux.conf; TPM installs the set declared there, not ours" +fi + +PLUGINS=() +while IFS= read -r p; do + [ -n "$p" ] && PLUGINS+=("$p") +done < <(sed -n "s/^[[:space:]]*set[^#]*@plugin[[:space:]]*['\"]\([^'\"]*\)['\"].*/\1/p" "$CONF") + +if [ "${#PLUGINS[@]}" -eq 0 ]; then + fail "no @plugin lines found in $CONF" + exit 2 +fi + +# plugin_dir NAME -- the directory TPM clones a plugin into. +plugin_dir() { + local name=${1##*/} + printf '%s/.tmux/plugins/%s' "$HOME" "${name%.git}" +} + +MISSING=0 +for p in "${PLUGINS[@]}"; do + if [ -d "$(plugin_dir "$p")" ]; then + ok "already installed: $p" + else + info "not installed: $p" + MISSING=$((MISSING + 1)) + fi +done + +if [ "$MISSING" -eq 0 ]; then + ok "plugin set converged (${#PLUGINS[@]} plugins); nothing to do" + exit 0 +fi + +# TPM prints its own per-plugin progress and exits non-zero if any failed. +if ! run_mut "$INSTALLER"; then + fail "TPM reported at least one plugin download failure" + exit 1 +fi + +if [ "${TOOLKIT_DRY_RUN:-0}" = "1" ]; then + exit 0 +fi + +STILL_MISSING=() +for p in "${PLUGINS[@]}"; do + [ -d "$(plugin_dir "$p")" ] || STILL_MISSING+=("$p") +done +if [ "${#STILL_MISSING[@]}" -gt 0 ]; then + fail "plugins still missing after install:$(printf ' %s' "${STILL_MISSING[@]}")" + exit 1 +fi + +ok "plugin set installed (${#PLUGINS[@]} plugins)" +info "already-running sessions pick them up with: prefix + r"