From 94d6acadb3f445627d12aa73de743de4d2bc0039 Mon Sep 17 00:00:00 2001 From: bcherb2 Date: Fri, 21 Aug 2026 22:34:06 -0400 Subject: [PATCH] feat: bootstrap installs git, and prints a next step that works run_before_00-require-git.sh: the documented first command died on the most ordinary kind of fresh machine there is. chezmoi is a static download and installs fine, but all six git-repo externals need git, and Ubuntu cloud images, the Ubuntu docker image and WSL Ubuntu all ship curl and no git. Installs it across apt/dnf/yum/pacman/zypper/apk/brew, and explains itself where it cannot (no sudo, or macOS where `git` is a GUI-installer stub). run_after_99-next-step.sh: `dotup` is command-not-found in the very session that installed it, because Ubuntu's ~/.profile adds ~/.local/bin to PATH only if the directory existed at login. No child process can fix its parent's PATH, so print the absolute path instead of letting the user guess. Silent once PATH is sorted out. --- .chezmoiscripts/run_after_99-next-step.sh | 27 +++++++++ .chezmoiscripts/run_before_00-require-git.sh | 64 ++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 .chezmoiscripts/run_after_99-next-step.sh create mode 100755 .chezmoiscripts/run_before_00-require-git.sh diff --git a/.chezmoiscripts/run_after_99-next-step.sh b/.chezmoiscripts/run_after_99-next-step.sh new file mode 100755 index 0000000..bab0e29 --- /dev/null +++ b/.chezmoiscripts/run_after_99-next-step.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Say what to run next, using a path that actually works right now. +# +# `dotup` lands in ~/.local/bin, and Ubuntu's stock ~/.profile does add that to +# PATH -- but behind `[ -d "$HOME/.local/bin" ]`, evaluated once, at login. On +# a fresh box the directory does not exist when you log in, so it is not on +# PATH; this apply then creates it; and the shell you are sitting in never +# re-reads .profile. The result is that `dotup` is "command not found" in +# exactly the session that just installed it, and works fine in the next one. +# +# That cannot be fixed from inside a child process -- nothing here can alter +# the PATH of the shell that invoked it. What it can do is stop the user +# guessing: print the absolute path, which always works, and say why the bare +# name does not yet. +# +# Silent once PATH is sorted out, so a routine `chezmoi update` says nothing. +set -eu + +command -v dotup >/dev/null 2>&1 && exit 0 +[ -x "$HOME/.local/bin/dotup" ] || exit 0 + +printf '\n' >&2 +printf 'Next: ~/.local/bin/dotup\n' >&2 +printf '\n' >&2 +printf 'Type the path in full. `dotup` on its own will not be found until you\n' >&2 +printf 'open a new shell -- ~/.profile only adds ~/.local/bin to PATH if the\n' >&2 +printf 'directory already existed when you logged in, and it did not.\n' >&2 diff --git a/.chezmoiscripts/run_before_00-require-git.sh b/.chezmoiscripts/run_before_00-require-git.sh new file mode 100755 index 0000000..a4f44ea --- /dev/null +++ b/.chezmoiscripts/run_before_00-require-git.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# git has to exist before chezmoi fetches the six git-repo externals. +# +# The documented first command is +# +# sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply +# +# and on a stock box it used to fail here: +# +# chezmoi: .config/tmux/plugins/tpm: exec: "git": executable file not found +# +# chezmoi itself is a static download and installs fine. git is a different +# matter: Ubuntu Server cloud images, the Ubuntu docker image and WSL Ubuntu all +# ship curl and none of them ship git. So the front door of the whole system +# did not open on the most ordinary kind of fresh machine there is. +# +# `run_before_` is the fix rather than a README note, because chezmoi runs these +# before every other target -- externals included. Verified, not assumed: a +# probe script printed "git ABSENT here" and the external failed immediately +# after, in that order. +# +# This exits in microseconds on every machine that already has git, which after +# the first apply is all of them. +set -eu + +command -v git >/dev/null 2>&1 && exit 0 + +printf 'chezmoi: git is required for the shell externals and is not installed.\n' >&2 + +# Root needs no sudo; a non-root user without sudo cannot do this at all, and +# should be told rather than shown a package manager error. +if [ "$(id -u)" -eq 0 ]; then + SUDO= +elif command -v sudo >/dev/null 2>&1; then + SUDO=sudo +else + printf 'chezmoi: cannot install it -- not root and no sudo. Install git and re-run:\n' >&2 + printf 'chezmoi: chezmoi init --apply \n' >&2 + exit 1 +fi + +printf 'chezmoi: installing git...\n' >&2 +if command -v apt-get >/dev/null 2>&1; then + ${SUDO} env DEBIAN_FRONTEND=noninteractive apt-get update -qq || : + ${SUDO} env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git +elif command -v dnf >/dev/null 2>&1; then ${SUDO} dnf install -y git +elif command -v yum >/dev/null 2>&1; then ${SUDO} yum install -y git +elif command -v pacman >/dev/null 2>&1; then ${SUDO} pacman -Sy --noconfirm git +elif command -v zypper >/dev/null 2>&1; then ${SUDO} zypper --non-interactive install git +elif command -v apk >/dev/null 2>&1; then ${SUDO} apk add --no-cache git +elif command -v brew >/dev/null 2>&1; then brew install git +elif [ "$(uname -s)" = Darwin ]; then + # On macOS `git` is a stub that triggers the Command Line Tools installer, + # which is a GUI dialog -- there is nothing useful to do unattended. + printf 'chezmoi: run `xcode-select --install`, then re-run chezmoi init --apply.\n' >&2 + exit 1 +else + printf 'chezmoi: no package manager I recognise. Install git and re-run.\n' >&2 + exit 1 +fi + +command -v git >/dev/null 2>&1 \ + || { printf 'chezmoi: git still not on PATH after installing it.\n' >&2; exit 1; } +printf 'chezmoi: git %s installed.\n' "$(git --version | awk '{print $3}')" >&2