diff --git a/scripts/setup/machine-setup/lib/dev.sh b/scripts/setup/machine-setup/lib/dev.sh index 22f5f61c..bb375f2d 100644 --- a/scripts/setup/machine-setup/lib/dev.sh +++ b/scripts/setup/machine-setup/lib/dev.sh @@ -99,3 +99,74 @@ set_login_shell() { 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 +} diff --git a/scripts/setup/machine-setup/machine-setup.sh b/scripts/setup/machine-setup/machine-setup.sh index 6f07fb36..673ebd23 100755 --- a/scripts/setup/machine-setup/machine-setup.sh +++ b/scripts/setup/machine-setup/machine-setup.sh @@ -1853,13 +1853,109 @@ EOF step_ok fi +# ============================================================================= +# 24. Neovim +# ============================================================================= + +step "Neovim" +if ! skip; then + NVIM_NOW="$(nvim_installed_version)" + NVIM_LATEST="$(nvim_latest_version)" + + echo "" + info "Neovim — from upstream, not from the distribution" + echo " installed: ${NVIM_NOW:-not installed}" + echo " latest: ${NVIM_LATEST:-could not reach github}" + echo " Ubuntu ships a Neovim years behind upstream, and LazyVim wants a" + echo " recent one, so this takes the release tarball." + + if [[ -z "$NVIM_LATEST" ]]; then + warn "cannot reach the release API — leaving Neovim alone" + SUMMARY+=("Neovim: skipped, could not reach github") + elif [[ "$NVIM_NOW" == "$NVIM_LATEST" ]]; then + echo " already on the latest release, nothing to do" + SUMMARY+=("Neovim: already ${NVIM_NOW}") + else + echo "" + if confirm "${NVIM_NOW:+Upgrade}${NVIM_NOW:-Install} Neovim ${NVIM_LATEST}?"; then + if nvim_install; then + ok "neovim $(nvim_installed_version) at /usr/local/bin/nvim" + SUMMARY+=("Neovim: $(nvim_installed_version)") + else + warn "the Neovim install did not complete" + ERRORS+=("Neovim: install failed") + SUMMARY+=("Neovim: install FAILED") + fi + else + warn "skipped by request" + SUMMARY+=("Neovim: SKIPPED by request") + fi + fi + + # ── configuration ── + if command -v nvim &>/dev/null && id "$USERNAME" &>/dev/null; then + NVIM_CONFIG="${USER_HOME}/.config/nvim" + + if [[ -d "$NVIM_CONFIG" ]]; then + echo "" + echo " ${USERNAME} already has a Neovim config at ${NVIM_CONFIG}" + SUMMARY+=("Neovim: existing config left alone") + else + echo "" + info "Neovim configuration for ${USERNAME}" + echo " [1] LazyVim starter — a maintained set of defaults, sensible to" + echo " build on and easy to remove" + echo " [2] a git repository of your own" + echo " [3] nothing — plain Neovim" + echo "" + + NVIM_REPO="" + NVIM_PICK="" + while [[ -z "$NVIM_PICK" ]]; do + if ! read -rp " Which one? (1/2/3) [1]: " NVIM_CHOICE; then + echo "" + fail "No answer." + fi + case "${NVIM_CHOICE:-1}" in + 1) + NVIM_REPO="https://github.com/LazyVim/starter" + NVIM_PICK=config + ;; + 2) + # No default. The original suggested its author's own private repo, + # which nobody else can clone. + read -rp " Repository URL: " NVIM_REPO || fail "No answer." + [[ -n "$NVIM_REPO" ]] && NVIM_PICK=config || warn "A repository URL is needed." + ;; + 3) NVIM_PICK=none ;; + *) warn "Pick 1, 2 or 3." ;; + esac + done + + if [[ "$NVIM_PICK" == config ]]; then + if nvim_clone_config "$NVIM_REPO"; then + ok "config cloned to ${NVIM_CONFIG}" + SUMMARY+=("Neovim: config from ${NVIM_REPO}") + else + warn "could not clone ${NVIM_REPO}" + ERRORS+=("Neovim: cloning ${NVIM_REPO} failed") + SUMMARY+=("Neovim: config clone FAILED") + fi + else + SUMMARY+=("Neovim: no config, plain Neovim") + fi + fi + fi + step_ok +fi + # ============================================================================= # NOT PORTED YET # ============================================================================= # # Sections still to move across from scripts/setup-old/setup-ubuntu.sh, in order: # -# neovim · js runtimes · +# js runtimes · # dev tools · ufw · zshrc # # And one that is new rather than ported, to come last of all: