Kept as it worked — upstream tarball, symlink, a config repo cloned into the account's ~/.config/nvim — with the defects fixed rather than the design changed. The one that mattered: the asset name. Neovim publishes nvim-linux-x86_64.tar.gz and nvim-linux-arm64.tar.gz. The original mapped aarch64 to "aarch64", which is not a name Neovim has ever published, so on an arm machine it downloaded a 404 and handed the HTML error page to tar. Verified against the release API — same class of bug as lazygit's hardcoded x86_64, and the second one this port has found in an arch mapping. The tarball is now checked with `tar -tzf` before anything is removed, so a bad download says what is wrong instead of failing inside tar. The rest: The tarball went to the working directory, via `curl -LO`, and stayed there if tar failed. It goes to /tmp and is cleaned up. The old /opt install was removed before the new one was known to be good. The download and its sanity check now come first, so a failed fetch leaves the working copy alone. The custom-repo option defaulted to git@gogs:andrepadez/nvim-config.git — a private repository nobody else can clone, and the same mistake as defaulting the login server to a personal headscale. No default now. git clone runs from /, for the reason git config does: the script's working directory is usually under the invoking user's home at 0750, which the target account cannot stat. The ~/.config/nvim/.git removal is now conditional on it being the starter. That is a template and dropping its history is right; a config of the user's own is something they will want to keep pulling. An existing config is left alone and said so, rather than moved to a .bak that silently overwrote the previous .bak. The PATH line the original appended to .zshrc is gone. /usr/local/bin/nvim is symlinked and already on PATH, so it was doing nothing except growing the file on every run. Verified on this host (already current, existing config left alone) and against a fresh account (LazyVim starter cloned, owned correctly, .git dropped). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
173 lines
7.0 KiB
Bash
173 lines
7.0 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"
|
|
}
|
|
|
|
# -----------------------------------------------------------------------------
|
|
# Neovim
|
|
# -----------------------------------------------------------------------------
|
|
#
|
|
# From the upstream tarball rather than the distribution, which ships Neovim
|
|
# years behind — Ubuntu 24.04 has 0.9 where upstream is on 0.12, and LazyVim
|
|
# requires 0.9+ with most plugins wanting newer.
|
|
#
|
|
# The asset names are x86_64 and arm64. The original mapped aarch64 to
|
|
# "aarch64", which is not a name Neovim publishes: on an arm machine it
|
|
# downloaded a 404 and tar failed on the HTML error page.
|
|
nvim_asset() {
|
|
case "$ARCH" in
|
|
amd64) echo x86_64 ;;
|
|
arm64) echo arm64 ;;
|
|
esac
|
|
}
|
|
|
|
nvim_installed_version() { nvim --version 2>/dev/null | awk 'NR == 1 { print $2 }'; }
|
|
|
|
nvim_latest_version() {
|
|
curl -fsSL https://api.github.com/repos/neovim/neovim/releases/latest 2>/dev/null |
|
|
jq -r '.tag_name // empty'
|
|
}
|
|
|
|
# Downloaded to /tmp, not to whatever directory the script was launched from —
|
|
# the original used `curl -LO`, which drops the tarball beside the script and
|
|
# leaves it there if tar fails.
|
|
#
|
|
# The old install is removed only after the download has succeeded, so a failed
|
|
# fetch leaves the working copy alone.
|
|
nvim_install() {
|
|
local asset tarball dest
|
|
asset="$(nvim_asset)"
|
|
tarball="/tmp/nvim-linux-${asset}.tar.gz"
|
|
dest="/opt/nvim-linux-${asset}"
|
|
|
|
curl -fsSL -o "$tarball" \
|
|
"https://github.com/neovim/neovim/releases/latest/download/nvim-linux-${asset}.tar.gz" || return 1
|
|
|
|
# A 404 comes back as an HTML page, and tar's failure on it is unhelpful.
|
|
# Checking here names the real problem.
|
|
tar -tzf "$tarball" >/dev/null 2>&1 || {
|
|
rm -f "$tarball"
|
|
warn "the download is not a tarball — the release asset may have been renamed"
|
|
return 1
|
|
}
|
|
|
|
rm -rf "$dest"
|
|
tar -C /opt -xzf "$tarball"
|
|
rm -f "$tarball"
|
|
ln -sf "${dest}/bin/nvim" /usr/local/bin/nvim
|
|
}
|
|
|
|
# Clone a Neovim config into the account's ~/.config/nvim.
|
|
#
|
|
# From `cd /` for the same reason git config does: the script's working directory
|
|
# is usually under the invoking user's home at 0750, which the target account
|
|
# cannot stat, and git fails there before it does anything useful.
|
|
nvim_clone_config() {
|
|
local repo="$1" dest="${USER_HOME}/.config/nvim"
|
|
|
|
install -d -m 0755 -o "$USERNAME" -g "$USERNAME" "${USER_HOME}/.config"
|
|
(cd / && sudo -H -u "$USERNAME" git clone --depth 1 "$repo" "$dest" >/dev/null 2>&1) || return 1
|
|
|
|
# The starter is a template, not something to track. Left in place for a
|
|
# config of the user's own, which they will want to keep pulling.
|
|
[[ "$repo" == *LazyVim/starter* ]] && sudo -u "$USERNAME" rm -rf "${dest}/.git"
|
|
return 0
|
|
}
|