port the git section, and stop it overwriting an identity that already exists
Detects first, then asks. If there is a name and email configured it shows them and offers to change them, defaulting to no — the common reason to re-run this script is everything except this. If there is nothing configured it just asks. Default branch is master rather than main, unless the answer says otherwise. Three problems in the original, beyond running unconditionally: prompt_value accepts an empty answer, so pressing Enter wrote `user.name = ""`. An empty name is worse than none: unset makes git refuse to commit and say why, empty makes it commit with a blank author and never mention it. ask_required re-asks instead. core.editor was set to nvim four sections before Neovim is installed, so anything invoking the editor in between failed. Dropped for now rather than moved — it is a preference, and worth deciding separately. Nothing checked whether the writes worked. That last one was not theoretical. Testing against a throwaway account, all three writes failed and the section still printed "OK: written". `git config --global` needs no repository, but git stats the working directory on the way, looking for one — and the script runs from under the invoking user's home, which is 0750, so the target account cannot stat it: fatal: failed to stat '<cwd>': Permission denied The wrappers now run in a subshell from /, which every account can stat, and the caller checks the exit status and reads the value back before claiming success. Also recorded where it is written: docs/agent-git-identity.md says every agent Officer runs commits as the owner, because it runs as the owner. This is not only the human's identity, it is what git log attributes agent commits to — which is worth knowing while choosing it. Verified both paths: this host's existing identity is shown and left alone by default, and a fresh account gets a correct .gitconfig owned by that account with defaultBranch master. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#!/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"
|
||||
}
|
||||
@@ -32,6 +32,8 @@ source "$SCRIPT_DIR/lib/files.sh"
|
||||
source "$SCRIPT_DIR/lib/ssh.sh"
|
||||
# shellcheck source=lib/network.sh
|
||||
source "$SCRIPT_DIR/lib/network.sh"
|
||||
# shellcheck source=lib/dev.sh
|
||||
source "$SCRIPT_DIR/lib/dev.sh"
|
||||
|
||||
# Trap errors with context. Installed here rather than in lib/base.sh, because
|
||||
# that file is definitions only and a trap is a side effect on whoever sources it.
|
||||
@@ -1267,13 +1269,80 @@ EOF
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# 20. Git
|
||||
# =============================================================================
|
||||
|
||||
step "Git"
|
||||
if ! skip; then
|
||||
echo ""
|
||||
info "Git — the identity commits from this machine are made under"
|
||||
|
||||
GIT_NAME_NOW="$(git_get user.name)"
|
||||
GIT_EMAIL_NOW="$(git_get user.email)"
|
||||
GIT_BRANCH_NOW="$(git_get init.defaultBranch)"
|
||||
|
||||
GIT_DO=true
|
||||
if git_has_identity; then
|
||||
echo " name: ${GIT_NAME_NOW:-not set}"
|
||||
echo " email: ${GIT_EMAIL_NOW:-not set}"
|
||||
echo " default branch: ${GIT_BRANCH_NOW:-not set (git uses master)}"
|
||||
echo ""
|
||||
# Default no. There is already a working identity, and the common case for
|
||||
# re-running this script is everything except this.
|
||||
confirm "Change it?" n || GIT_DO=false
|
||||
else
|
||||
echo " nothing configured yet"
|
||||
echo ""
|
||||
echo " Worth knowing: agents Officer runs commit as this account, so this"
|
||||
echo " is what git log will attribute their commits to as well."
|
||||
fi
|
||||
|
||||
if [[ "$GIT_DO" == false ]]; then
|
||||
echo " left as it is"
|
||||
SUMMARY+=("Git: unchanged (${GIT_NAME_NOW})")
|
||||
else
|
||||
echo ""
|
||||
ask_required GIT_NAME "Name" "$GIT_NAME_NOW"
|
||||
ask_required GIT_EMAIL "Email" "$GIT_EMAIL_NOW"
|
||||
ask_required GIT_BRANCH "Default branch for new repositories" "${GIT_BRANCH_NOW:-master}"
|
||||
|
||||
echo ""
|
||||
echo " name: ${GIT_NAME}"
|
||||
echo " email: ${GIT_EMAIL}"
|
||||
echo " default branch: ${GIT_BRANCH}"
|
||||
if confirm "Proceed?"; then
|
||||
# Checked, not assumed. These run as another account and can fail for
|
||||
# reasons that have nothing to do with the values — which is exactly what
|
||||
# happened when they were run from a directory that account could not stat.
|
||||
GIT_OK=true
|
||||
git_set user.name "$GIT_NAME" || GIT_OK=false
|
||||
git_set user.email "$GIT_EMAIL" || GIT_OK=false
|
||||
git_set init.defaultBranch "$GIT_BRANCH" || GIT_OK=false
|
||||
|
||||
if $GIT_OK && [[ "$(git_get user.name)" == "$GIT_NAME" ]]; then
|
||||
ok "written to ${USER_HOME}/.gitconfig"
|
||||
SUMMARY+=("Git: ${GIT_NAME} <${GIT_EMAIL}>, default branch ${GIT_BRANCH}")
|
||||
else
|
||||
warn "could not write the git config for ${USERNAME}"
|
||||
ERRORS+=("Git: writing ${USER_HOME}/.gitconfig failed")
|
||||
SUMMARY+=("Git: FAILED to write")
|
||||
fi
|
||||
else
|
||||
warn "skipped by request"
|
||||
SUMMARY+=("Git: SKIPPED by request")
|
||||
fi
|
||||
fi
|
||||
step_ok
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# NOT PORTED YET
|
||||
# =============================================================================
|
||||
#
|
||||
# Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order:
|
||||
#
|
||||
# git config · docker · zsh + prompt (incl. .tmux.conf) · tailscale · neovim · js runtimes ·
|
||||
# docker · zsh + prompt (incl. .tmux.conf) · tailscale · neovim · js runtimes ·
|
||||
# dev tools · ufw · zshrc
|
||||
#
|
||||
# Each arrives as its own commit. Delete this block when the list is empty.
|
||||
|
||||
Reference in New Issue
Block a user