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>
This commit is contained in:
@@ -347,3 +347,35 @@ install_opencode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_pi() { npm install -g @mariozechner/pi-coding-agent >/dev/null 2>&1; }
|
install_pi() { npm install -g @mariozechner/pi-coding-agent >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Default editor
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# One preference, two mechanisms, and both are needed:
|
||||||
|
#
|
||||||
|
# EDITOR / VISUAL what the account's own shell hands to git, crontab -e,
|
||||||
|
# systemctl edit and anything else that opens an editor
|
||||||
|
# update-alternatives the system-wide `editor` command, which is what root and
|
||||||
|
# `sudoedit` use — an account's shell config cannot reach
|
||||||
|
# those
|
||||||
|
#
|
||||||
|
# This is the setting core.editor was deliberately left out in favour of: set it
|
||||||
|
# here and git follows, along with everything else.
|
||||||
|
|
||||||
|
editor_candidates() {
|
||||||
|
local e
|
||||||
|
for e in nvim vim nano; do command -v "$e" &>/dev/null && echo "$e"; done
|
||||||
|
}
|
||||||
|
|
||||||
|
current_editor() { (cd / && sudo -H -u "$USERNAME" bash -lc 'echo "${EDITOR:-}"' 2>/dev/null); }
|
||||||
|
|
||||||
|
set_system_editor() {
|
||||||
|
local editor="$1" path
|
||||||
|
path="$(command -v "$editor")" || return 1
|
||||||
|
# Only where the alternatives system is in use. Absent on non-Debian systems,
|
||||||
|
# where there is nothing to set.
|
||||||
|
command -v update-alternatives &>/dev/null || return 0
|
||||||
|
update-alternatives --install /usr/bin/editor editor "$path" 100 >/dev/null 2>&1
|
||||||
|
update-alternatives --set editor "$path" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ install_config() {
|
|||||||
# EOF
|
# EOF
|
||||||
#
|
#
|
||||||
# Returns 0 if it wrote, 1 if the block was already there.
|
# 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() {
|
append_once() {
|
||||||
local file="$1" name="$2"
|
local file="$1" name="$2"
|
||||||
local begin="# >>> machine-setup: ${name} >>>"
|
local begin="# >>> machine-setup: ${name} >>>"
|
||||||
|
|||||||
@@ -1754,107 +1754,7 @@ if ! skip; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 23. Shell
|
# 23. Neovim
|
||||||
# =============================================================================
|
|
||||||
#
|
|
||||||
# zsh, oh-my-zsh, the starship prompt, and the dotfiles that go with them. The
|
|
||||||
# .tmux.conf lives here rather than in user creation, where the original put it
|
|
||||||
# only because that is where $USER_HOME first exists.
|
|
||||||
|
|
||||||
step "Shell"
|
|
||||||
if ! skip; then
|
|
||||||
SHELL_NOW="$(user_login_shell)"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
info "Shell — what ${USERNAME} gets at every login"
|
|
||||||
echo " login shell: ${SHELL_NOW}"
|
|
||||||
echo " zsh: $(command -v zsh &>/dev/null && echo 'installed' || echo 'not installed')"
|
|
||||||
echo " oh-my-zsh: $(oh_my_zsh_installed && echo 'installed' || echo 'not installed')"
|
|
||||||
echo " starship: $(command -v starship &>/dev/null && echo 'installed' || echo 'not installed')"
|
|
||||||
|
|
||||||
# ── zsh and oh-my-zsh ──
|
|
||||||
if ! command -v zsh &>/dev/null || ! oh_my_zsh_installed; then
|
|
||||||
echo ""
|
|
||||||
echo " oh-my-zsh is a configuration framework for zsh: completions, a"
|
|
||||||
echo " plugin system, and sensible history behaviour out of the box."
|
|
||||||
if confirm "Install zsh and oh-my-zsh?"; then
|
|
||||||
command -v zsh &>/dev/null || pkg_install_now zsh
|
|
||||||
if ! oh_my_zsh_installed; then
|
|
||||||
install_oh_my_zsh
|
|
||||||
oh_my_zsh_installed && ok "oh-my-zsh installed" || warn "oh-my-zsh did not install"
|
|
||||||
fi
|
|
||||||
SUMMARY+=("Shell: zsh and oh-my-zsh installed")
|
|
||||||
else
|
|
||||||
warn "skipped by request"
|
|
||||||
SUMMARY+=("Shell: SKIPPED by request")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── the login shell, asked separately ──
|
|
||||||
#
|
|
||||||
# Having zsh on the machine and being handed it at every login are different
|
|
||||||
# decisions, and the original made the second one silently.
|
|
||||||
if command -v zsh &>/dev/null && [[ "$SHELL_NOW" != *zsh ]]; then
|
|
||||||
echo ""
|
|
||||||
echo " ${USERNAME}'s login shell is ${SHELL_NOW}. Changing it to zsh takes"
|
|
||||||
echo " effect at the next login, and does not affect this session."
|
|
||||||
if confirm "Make zsh the login shell?"; then
|
|
||||||
set_login_shell "$(command -v zsh)"
|
|
||||||
ok "login shell is now $(user_login_shell)"
|
|
||||||
SUMMARY+=("Shell: login shell set to zsh")
|
|
||||||
else
|
|
||||||
warn "left as ${SHELL_NOW}"
|
|
||||||
SUMMARY+=("Shell: login shell left as ${SHELL_NOW}")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── dotfiles, none of which overwrite ──
|
|
||||||
if id "$USERNAME" &>/dev/null; then
|
|
||||||
echo ""
|
|
||||||
info " shell configuration"
|
|
||||||
|
|
||||||
# The prompt config the platform also deploys to every member. install_config
|
|
||||||
# keeps whatever is already there if it differs.
|
|
||||||
if [[ -r "$STARSHIP_SRC" ]]; then
|
|
||||||
# && / || rather than a bare call: 2 means "kept yours", which is an
|
|
||||||
# outcome and not a failure, but is still non-zero and would end the run.
|
|
||||||
install_config "$STARSHIP_SRC" "${USER_HOME}/.config/starship.toml" "$USERNAME" && RC=0 || RC=$?
|
|
||||||
case $RC in
|
|
||||||
0) ok "starship config installed — the same one members get" ;;
|
|
||||||
1) echo " starship config already matches" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -r "$SCRIPT_DIR/.tmux.conf" ]]; then
|
|
||||||
install_config "$SCRIPT_DIR/.tmux.conf" "${USER_HOME}/.tmux.conf" "$USERNAME" && RC=0 || RC=$?
|
|
||||||
case $RC in
|
|
||||||
0) ok "tmux config installed" ;;
|
|
||||||
1) echo " tmux config already matches" ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Marker-wrapped, so a second run recognises its own work instead of adding
|
|
||||||
# it again. The original appended this unguarded on every pass.
|
|
||||||
if command -v starship &>/dev/null; then
|
|
||||||
ZSHRC="${USER_HOME}/.zshrc"
|
|
||||||
touch "$ZSHRC"
|
|
||||||
chown "$USERNAME:$USERNAME" "$ZSHRC"
|
|
||||||
if append_once "$ZSHRC" starship <<'EOF'
|
|
||||||
eval "$(starship init zsh)"
|
|
||||||
EOF
|
|
||||||
then
|
|
||||||
ok "starship added to .zshrc"
|
|
||||||
else
|
|
||||||
echo " starship already in .zshrc"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
SUMMARY+=("Shell: prompt and dotfiles in place")
|
|
||||||
fi
|
|
||||||
step_ok
|
|
||||||
fi
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 24. Neovim
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
step "Neovim"
|
step "Neovim"
|
||||||
@@ -1950,7 +1850,7 @@ if ! skip; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 25. JavaScript runtimes
|
# 24. JavaScript runtimes
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
#
|
#
|
||||||
# Not offered as a choice. Officer does not run without these, so asking would be
|
# Not offered as a choice. Officer does not run without these, so asking would be
|
||||||
@@ -2063,7 +1963,7 @@ if ! skip; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 26. Agent CLIs
|
# 25. Agent CLIs
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
#
|
#
|
||||||
# claude and opencode are what the chat sidecars spawn, so they are installed
|
# claude and opencode are what the chat sidecars spawn, so they are installed
|
||||||
@@ -2133,26 +2033,6 @@ if ! skip; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ~/.local/bin has to be on PATH for the account's own shell. The sidecars find
|
|
||||||
# claude without it — claude-manager.ts checks that directory explicitly — but
|
|
||||||
# a user who cannot run `claude` in their own terminal reasonably concludes it
|
|
||||||
# was never installed.
|
|
||||||
if id "$USERNAME" &>/dev/null; then
|
|
||||||
ZSHRC="${USER_HOME}/.zshrc"
|
|
||||||
touch "$ZSHRC"
|
|
||||||
chown "$USERNAME:$USERNAME" "$ZSHRC"
|
|
||||||
# Both directories, because the two installers disagree about where they put
|
|
||||||
# things. The sidecars find their binaries without this — they check the
|
|
||||||
# exact paths — but a user who cannot run `claude` in their own terminal
|
|
||||||
# reasonably concludes it was never installed.
|
|
||||||
if append_once "$ZSHRC" agent-clis <<'EOF'
|
|
||||||
export PATH="$HOME/.local/bin:$HOME/.opencode/bin:$PATH"
|
|
||||||
EOF
|
|
||||||
then
|
|
||||||
ok "~/.local/bin and ~/.opencode/bin added to ${USERNAME}'s PATH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── PI, which nothing here uses ──
|
# ── PI, which nothing here uses ──
|
||||||
echo ""
|
echo ""
|
||||||
if command -v pi &>/dev/null; then
|
if command -v pi &>/dev/null; then
|
||||||
@@ -2176,6 +2056,173 @@ EOF
|
|||||||
step_ok
|
step_ok
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 26. Shell
|
||||||
|
# =============================================================================
|
||||||
|
#
|
||||||
|
# zsh, oh-my-zsh, the starship prompt, and the dotfiles that go with them. The
|
||||||
|
# .tmux.conf lives here rather than in user creation, where the original put it
|
||||||
|
# only because that is where $USER_HOME first exists.
|
||||||
|
|
||||||
|
step "Shell"
|
||||||
|
if ! skip; then
|
||||||
|
SHELL_NOW="$(user_login_shell)"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Shell — what ${USERNAME} gets at every login"
|
||||||
|
echo " login shell: ${SHELL_NOW}"
|
||||||
|
echo " zsh: $(command -v zsh &>/dev/null && echo 'installed' || echo 'not installed')"
|
||||||
|
echo " oh-my-zsh: $(oh_my_zsh_installed && echo 'installed' || echo 'not installed')"
|
||||||
|
echo " starship: $(command -v starship &>/dev/null && echo 'installed' || echo 'not installed')"
|
||||||
|
|
||||||
|
# ── zsh and oh-my-zsh ──
|
||||||
|
if ! command -v zsh &>/dev/null || ! oh_my_zsh_installed; then
|
||||||
|
echo ""
|
||||||
|
echo " oh-my-zsh is a configuration framework for zsh: completions, a"
|
||||||
|
echo " plugin system, and sensible history behaviour out of the box."
|
||||||
|
if confirm "Install zsh and oh-my-zsh?"; then
|
||||||
|
command -v zsh &>/dev/null || pkg_install_now zsh
|
||||||
|
if ! oh_my_zsh_installed; then
|
||||||
|
install_oh_my_zsh
|
||||||
|
oh_my_zsh_installed && ok "oh-my-zsh installed" || warn "oh-my-zsh did not install"
|
||||||
|
fi
|
||||||
|
SUMMARY+=("Shell: zsh and oh-my-zsh installed")
|
||||||
|
else
|
||||||
|
warn "skipped by request"
|
||||||
|
SUMMARY+=("Shell: SKIPPED by request")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── the login shell, asked separately ──
|
||||||
|
#
|
||||||
|
# Having zsh on the machine and being handed it at every login are different
|
||||||
|
# decisions, and the original made the second one silently.
|
||||||
|
if command -v zsh &>/dev/null && [[ "$SHELL_NOW" != *zsh ]]; then
|
||||||
|
echo ""
|
||||||
|
echo " ${USERNAME}'s login shell is ${SHELL_NOW}. Changing it to zsh takes"
|
||||||
|
echo " effect at the next login, and does not affect this session."
|
||||||
|
if confirm "Make zsh the login shell?"; then
|
||||||
|
set_login_shell "$(command -v zsh)"
|
||||||
|
ok "login shell is now $(user_login_shell)"
|
||||||
|
SUMMARY+=("Shell: login shell set to zsh")
|
||||||
|
else
|
||||||
|
warn "left as ${SHELL_NOW}"
|
||||||
|
SUMMARY+=("Shell: login shell left as ${SHELL_NOW}")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── dotfiles, none of which overwrite ──
|
||||||
|
if id "$USERNAME" &>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
info " shell configuration"
|
||||||
|
|
||||||
|
# The prompt config the platform also deploys to every member. install_config
|
||||||
|
# keeps whatever is already there if it differs.
|
||||||
|
if [[ -r "$STARSHIP_SRC" ]]; then
|
||||||
|
# && / || rather than a bare call: 2 means "kept yours", which is an
|
||||||
|
# outcome and not a failure, but is still non-zero and would end the run.
|
||||||
|
install_config "$STARSHIP_SRC" "${USER_HOME}/.config/starship.toml" "$USERNAME" && RC=0 || RC=$?
|
||||||
|
case $RC in
|
||||||
|
0) ok "starship config installed — the same one members get" ;;
|
||||||
|
1) echo " starship config already matches" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -r "$SCRIPT_DIR/.tmux.conf" ]]; then
|
||||||
|
install_config "$SCRIPT_DIR/.tmux.conf" "${USER_HOME}/.tmux.conf" "$USERNAME" && RC=0 || RC=$?
|
||||||
|
case $RC in
|
||||||
|
0) ok "tmux config installed" ;;
|
||||||
|
1) echo " tmux config already matches" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Everything below writes .zshrc, and every block is marker-wrapped so a
|
||||||
|
# second run recognises its own work. The original appended all of it
|
||||||
|
# unguarded, so a re-run duplicated the lot.
|
||||||
|
ZSHRC="${USER_HOME}/.zshrc"
|
||||||
|
touch "$ZSHRC"
|
||||||
|
chown "$USERNAME:$USERNAME" "$ZSHRC"
|
||||||
|
|
||||||
|
if command -v starship &>/dev/null; then
|
||||||
|
if append_once "$ZSHRC" starship <<'EOF'
|
||||||
|
eval "$(starship init zsh)"
|
||||||
|
EOF
|
||||||
|
then
|
||||||
|
ok "starship added to .zshrc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The agent CLIs install to two different directories, neither of which is on
|
||||||
|
# PATH by default. The sidecars find them regardless — they check the exact
|
||||||
|
# paths — but a user who cannot run `claude` in their own terminal reasonably
|
||||||
|
# concludes it was never installed.
|
||||||
|
if append_once "$ZSHRC" agent-clis <<'EOF'
|
||||||
|
export PATH="$HOME/.local/bin:$HOME/.opencode/bin:$PATH"
|
||||||
|
EOF
|
||||||
|
then
|
||||||
|
ok "~/.local/bin and ~/.opencode/bin added to PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if append_once "$ZSHRC" aliases <<'EOF'
|
||||||
|
alias sz="source ~/.zshrc"
|
||||||
|
EOF
|
||||||
|
then
|
||||||
|
ok "shell aliases added"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUMMARY+=("Shell: prompt and dotfiles in place")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── the default editor ──
|
||||||
|
echo ""
|
||||||
|
info "Default editor — what opens when anything needs you to type something"
|
||||||
|
echo " git commit, crontab -e, systemctl edit, sudoedit. One preference,"
|
||||||
|
echo " which is why git's own core.editor is deliberately not set: git"
|
||||||
|
echo " falls back to \$EDITOR, so setting it here covers everything."
|
||||||
|
echo ""
|
||||||
|
mapfile -t EDITORS < <(editor_candidates)
|
||||||
|
if ((${#EDITORS[@]} == 0)); then
|
||||||
|
warn "no editor found to offer — skipping"
|
||||||
|
else
|
||||||
|
EDITOR_NOW="$(current_editor)"
|
||||||
|
echo " currently: ${EDITOR_NOW:-not set}"
|
||||||
|
for i in "${!EDITORS[@]}"; do
|
||||||
|
printf ' [%d] %s\n' "$((i + 1))" "${EDITORS[$i]}"
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
EDITOR_PICK=""
|
||||||
|
while [[ -z "$EDITOR_PICK" ]]; do
|
||||||
|
if ! read -rp " Which one? (1-${#EDITORS[@]}) [1]: " EDITOR_CHOICE; then
|
||||||
|
echo ""
|
||||||
|
fail "No answer."
|
||||||
|
fi
|
||||||
|
EDITOR_CHOICE="${EDITOR_CHOICE:-1}"
|
||||||
|
if [[ "$EDITOR_CHOICE" =~ ^[0-9]+$ ]] && ((EDITOR_CHOICE >= 1 && EDITOR_CHOICE <= ${#EDITORS[@]})); then
|
||||||
|
EDITOR_PICK="${EDITORS[$((EDITOR_CHOICE - 1))]}"
|
||||||
|
else
|
||||||
|
warn "Pick a number from the list."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Written to the account's shell, and set as the system `editor` alternative
|
||||||
|
# so root and sudoedit agree with it.
|
||||||
|
if append_once "$ZSHRC" editor <<EOF
|
||||||
|
export EDITOR="${EDITOR_PICK}"
|
||||||
|
export VISUAL="${EDITOR_PICK}"
|
||||||
|
export SUDO_EDITOR="${EDITOR_PICK}"
|
||||||
|
EOF
|
||||||
|
then
|
||||||
|
ok "EDITOR, VISUAL and SUDO_EDITOR set to ${EDITOR_PICK}"
|
||||||
|
else
|
||||||
|
echo " .zshrc already has an editor block — edit it by hand to change it"
|
||||||
|
fi
|
||||||
|
set_system_editor "$EDITOR_PICK" && ok "system 'editor' alternative set to ${EDITOR_PICK}"
|
||||||
|
SUMMARY+=("Editor: ${EDITOR_PICK}")
|
||||||
|
fi
|
||||||
|
step_ok
|
||||||
|
fi
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# NOT PORTED YET
|
# NOT PORTED YET
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -2196,7 +2243,7 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 23. Summary
|
# 28. Summary
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user