os-user-shell.ts:33 calls scripts/setup/starship.toml "the prompt config the owner's own install uses — one file, both audiences". It was not: the original machine script wrote a DIFFERENT config inline, so the owner got a prompt that only disabled language modules while every member got the repo file with its custom format. Two prompts, one comment claiming otherwise. This deploys the same file the platform does, which makes the comment true. Verified with cmp against a fresh account: byte-identical to what a member gets. Nothing overwrites any more: .config/starship.toml and .tmux.conf go through install_config, so they are written when absent, skipped when identical, and KEPT when they differ — with the cp printed, so taking ours stays the reader's decision. On this host that is what happens: the existing config differs and is left alone. The starship line in .zshrc is marker-wrapped by append_once. Verified over three consecutive runs: one block, not three. The original appended it unguarded every time. The login shell is now its own question. Having zsh on the machine and being handed it at every login are different decisions, and `chsh` made the second one silently. It also adds the shell to /etc/shells first, which chsh requires. .tmux.conf lives here now, with the rest of the dotfiles, rather than in user creation where the original put it only because that is where $USER_HOME first exists. One bug found by running it: install_config returns 2 for "kept yours", which is an outcome rather than a failure — but still non-zero, so calling it as a plain command under `set -e` ended the run before `case $?` could read it. Captured with && / || at both call sites, and the contract is documented where the function is defined so the next caller does not repeat it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
102 lines
4.4 KiB
Bash
102 lines
4.4 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# machine-setup — the development environment
|
|
# =============================================================================
|
|
#
|
|
# Definitions only, like the other lib/ files.
|
|
|
|
[[ -n "${MACHINE_SETUP_DEV_LOADED:-}" ]] && return 0
|
|
MACHINE_SETUP_DEV_LOADED=1
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# git
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# Read and written as the account, not as root. `git config --global` writes to
|
|
# $HOME/.gitconfig, so running it under sudo without -H would write root's.
|
|
#
|
|
# ── Why this asks before touching an existing identity ──
|
|
#
|
|
# The original set all four values unconditionally on every run. Re-running it on
|
|
# a machine somebody already uses replaces the name and email they had with
|
|
# whatever is typed — and prompt_value accepts an empty answer, so pressing
|
|
# Enter twice wrote `user.name = ""`. An empty name is worse than none at all:
|
|
# unset makes git refuse to commit and say why, empty makes it commit with a
|
|
# blank author and never mention it.
|
|
#
|
|
# ── And why it is worth being careful about here in particular ──
|
|
#
|
|
# docs/agent-git-identity.md: every agent Officer runs commits AS THE OWNER,
|
|
# because it runs as the owner. So this is not only the human's identity — it is
|
|
# what `git log` will attribute every agent commit on this machine to.
|
|
|
|
# Run from / rather than wherever the script was launched.
|
|
#
|
|
# `git config --global` reads and writes $HOME/.gitconfig and needs no repository
|
|
# — but git still stats the working directory on the way, looking for one. The
|
|
# script is typically launched from somewhere under the invoking user's home,
|
|
# which is 0750, so the target account cannot stat it and every call dies with
|
|
#
|
|
# fatal: failed to stat '<cwd>': Permission denied
|
|
#
|
|
# Found because the writes failed silently: the section reported "written" while
|
|
# nothing had been. Both wrappers now run in a subshell from /, which every
|
|
# account can stat, and their exit status is checked by the caller.
|
|
git_get() { (cd / && sudo -H -u "$USERNAME" git config --global --get "$1" 2>/dev/null); }
|
|
git_set() { (cd / && sudo -H -u "$USERNAME" git config --global "$1" "$2"); }
|
|
|
|
# Is there anything configured at all?
|
|
git_has_identity() { [[ -n "$(git_get user.name)" || -n "$(git_get user.email)" ]]; }
|
|
|
|
# Ask for a value that must not be empty. The original's prompt accepted empty
|
|
# and wrote it; this re-asks.
|
|
ask_required() {
|
|
local __var="$1" message="$2" default="$3" answer=""
|
|
while [[ -z "$answer" ]]; do
|
|
if ! read -rp " ${message}${default:+ [$default]}: " answer; then
|
|
echo ""
|
|
fail "No answer."
|
|
fi
|
|
answer="${answer:-$default}"
|
|
[[ -z "$answer" ]] && warn "This one cannot be left blank."
|
|
done
|
|
printf -v "$__var" '%s' "$answer"
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Shell
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# ── One starship config, not two ──
|
|
#
|
|
# The platform deploys scripts/setup/starship.toml into every member's home
|
|
# (os-user-shell.ts), and the comment there calls it "the prompt config the
|
|
# owner's own install uses — one file, both audiences". That was not true: the
|
|
# original machine script wrote a DIFFERENT config inline, so the owner got one
|
|
# prompt and every member got another. This deploys the same file the platform
|
|
# does, which makes the comment true rather than aspirational.
|
|
#
|
|
# It lives one directory up because it is shared with the platform, not owned by
|
|
# this script.
|
|
STARSHIP_SRC="${STARSHIP_SRC:-$SCRIPT_DIR/../starship.toml}"
|
|
|
|
user_login_shell() { getent passwd "$USERNAME" | cut -d: -f7; }
|
|
|
|
oh_my_zsh_installed() { [[ -d "${USER_HOME}/.oh-my-zsh" ]]; }
|
|
|
|
install_oh_my_zsh() {
|
|
# The installer refuses to run unattended over an existing install, so this is
|
|
# only ever called when there is none.
|
|
sudo -H -u "$USERNAME" sh -c \
|
|
"$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended >/dev/null 2>&1
|
|
}
|
|
|
|
# `chsh` is what actually changes the login shell. Asked separately from
|
|
# installing zsh, because having a shell available and being handed it at every
|
|
# login are different decisions.
|
|
set_login_shell() {
|
|
local shell="$1"
|
|
grep -qxF "$shell" /etc/shells || echo "$shell" >>/etc/shells
|
|
chsh -s "$shell" "$USERNAME"
|
|
}
|