Files
platform/scripts/setup/machine-setup/lib/files.sh
T
pastilhasandClaude Opus 5 d6a78d7b5a put the shell configuration in one place, after everything it configures
The Shell section moves to 26, after Neovim, the runtimes and the agent CLIs.
Everything that writes to .zshrc now happens there and only there: the starship
init, the PATH for the agent CLIs (moved out of that section), the aliases, and
the editor.

That ordering is what the editor choice needs — it offers whichever of nvim, vim
and nano are actually present, so it has to run after Neovim is installed rather
than naming an editor that is not there. Which was the original's mistake in the
other direction: it set core.editor to nvim four sections before installing it.

The default editor is the setting git's core.editor was deliberately left out in
favour of. EDITOR, VISUAL and SUDO_EDITOR go in the account's shell, and the
Debian `editor` alternative is set too — an account's shell config cannot reach
root or sudoedit, and those are exactly the cases where the wrong editor is most
annoying.

Recorded a limitation of append_once while cleaning up after it: renaming a
marker orphans the block that used the old name, and changing a block's content
does nothing because the marker is still found. Both need the old block removed
by hand. This run left exactly that — a `local-bin` block superseded by
`agent-clis` — in the dev box's .zshrc, now removed.

UFW is deliberately still unported and will be last, for the reason the original
gave: it is the one step that can cut the connection the run is happening over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 21:22:15 +00:00

128 lines
4.4 KiB
Bash

#!/bin/bash
# =============================================================================
# machine-setup — writing files into somebody's home
# =============================================================================
#
# Definitions only, like the other lib/ files.
#
# ── The rule ──
#
# A setup script may create a config file. It may not silently replace one the
# user wrote. The original did the second: `cp .tmux.conf $USER_HOME/` on every
# run, over whatever was there, and five separate `cat >>` into .zshrc with no
# guard — so a second pass duplicated the starship init, the nvim PATH, bun, deno
# and the aliases.
#
# Both of those are the same mistake in different shapes: writing without looking
# first. The two helpers here are the two safe shapes.
[[ -n "${MACHINE_SETUP_FILES_LOADED:-}" ]] && return 0
MACHINE_SETUP_FILES_LOADED=1
# Put a config file in place, asking before it replaces one the user has.
#
# Three outcomes, and the caller can tell them apart by the return code:
#
# 0 installed — there was nothing there, or the user chose to replace
# 1 identical — already exactly this, nothing done
# 2 kept — the user chose to keep theirs
#
# When the file exists and differs, this ASKS rather than deciding. Silently
# keeping theirs is safe but unhelpful — they never learn that a newer version
# exists — and silently replacing it is how a setup script eats somebody's
#configuration. So: keep, replace, or show the difference first, and a replaced file is
# always kept beside the new one.
#
# NOTE for callers: 1 and 2 are outcomes, not failures — but they are still
# non-zero, so calling this as a plain command under `set -e` ends the script
# before the result can be read. Always capture it:
#
# install_config "$src" "$dest" "$user" && rc=0 || rc=$?
install_config() {
local src="$1" dest="$2" owner="$3" answer
if [[ ! -f "$dest" ]]; then
install -D -m 0644 -o "$owner" -g "$owner" "$src" "$dest"
return 0
fi
cmp -s "$src" "$dest" && return 1
echo ""
warn "${dest} already exists here, and differs from the one this script ships."
# Never replace a file the user has without being told to. An unattended run
# answers "keep", because the alternative is destroying configuration nobody
# was present to defend.
if [[ "${ASSUME_YES:-}" == "1" ]] || [[ ! -t 0 ]]; then
echo " keeping yours (nothing was asked, so nothing is replaced)"
return 2
fi
while true; do
echo " [1] keep yours — nothing changes"
echo " [2] use ours — yours is kept as ${dest}.before-machine-setup"
echo " [3] show me the difference first"
echo ""
if ! read -rp " Which one? (1/2/3) [1]: " answer; then
echo ""
echo " keeping yours"
return 2
fi
case "${answer:-1}" in
1)
echo " keeping yours"
return 2
;;
2)
cp -a "$dest" "${dest}.before-machine-setup"
install -D -m 0644 -o "$owner" -g "$owner" "$src" "$dest"
ok "replaced — yours is at ${dest}.before-machine-setup"
return 0
;;
3)
echo ""
# yours on the left, ours on the right: - is what you would lose,
# + is what you would gain.
diff -u --label "yours: ${dest}" --label "ours: ${src}" "$dest" "$src" | page
echo ""
;;
*) warn "Pick 1, 2 or 3." ;;
esac
done
}
# Append a block to a file exactly once.
#
# The block is wrapped in markers naming what it is, so a second run recognises
# its own work instead of adding it again — and so a human reading the file can
# see which lines came from here and delete them as a unit.
#
# append_once ~/.zshrc bun <<'EOF'
# export PATH="$HOME/.bun/bin:$PATH"
# EOF
#
# Returns 0 if it wrote, 1 if the block was already there.
#
# One limitation, and it bites the author rather than the user: RENAMING a marker
# orphans the block that used the old name. append_once only recognises the name
# it is given, so the previous block stays in the file doing whatever it did.
# Changing a block's CONTENT has the same shape — the marker is found, so the new
# content is never written. Both need the old block removed by hand.
append_once() {
local file="$1" name="$2"
local begin="# >>> machine-setup: ${name} >>>"
local end="# <<< machine-setup: ${name} <<<"
if [[ -f "$file" ]] && grep -qF "$begin" "$file"; then
return 1
fi
{
echo ""
echo "$begin"
cat
echo "$end"
} >>"$file"
}