diff --git a/scripts/setup/machine-setup/lib/dev.sh b/scripts/setup/machine-setup/lib/dev.sh index 23ba640b..b69278e5 100644 --- a/scripts/setup/machine-setup/lib/dev.sh +++ b/scripts/setup/machine-setup/lib/dev.sh @@ -219,7 +219,38 @@ install_node() { npm config set prefix /usr/local >/dev/null 2>&1 || true } -bun_installed() { command -v bun &>/dev/null; } +# Present anywhere: on PATH for this root shell, or in the account's own +# ~/.bun/bin, which is where the installer puts it and where root cannot see it. +bun_installed() { command -v bun &>/dev/null || [[ -x "${USER_HOME}/.bun/bin/bun" ]]; } + +# Asked of whichever copy exists. Before the symlink is made, root's PATH has no +# bun at all, so `bun --version` reports nothing on a machine that plainly has it. +bun_version() { + if command -v bun &>/dev/null; then + bun --version + elif [[ -x "${USER_HOME}/.bun/bin/bun" ]]; then + "${USER_HOME}/.bun/bin/bun" --version + fi +} + +# The system-wide link, ensured on every run rather than only after an install. +# +# pm2 started at boot by systemd has no login shell, so ~/.bun/bin is not on its +# PATH — and every one of the twenty ecosystem apps that says `script: 'bun'` +# then fails to start on reboot while working perfectly when started by hand. A +# machine that already had bun before this script ran would never get the link if +# it were only made as part of installing. +# +# Safe across upgrades: a symlink resolves by path, and `bun upgrade` replaces +# the file at $BUN_INSTALL/bin/bun rather than moving it. The link only breaks if +# the home directory goes, which breaks bun anyway. +ensure_bun_symlink() { + local bin="${USER_HOME}/.bun/bin/bun" + [[ -x "$bin" ]] || return 1 + [[ "$(readlink -f /usr/local/bin/bun 2>/dev/null)" == "$(readlink -f "$bin")" ]] && return 1 + ln -sf "$bin" /usr/local/bin/bun + return 0 +} # Installed as the account, then symlinked system-wide. pm2 started at boot by # systemd has no login shell and therefore no ~/.bun/bin on PATH — without the @@ -227,9 +258,7 @@ bun_installed() { command -v bun &>/dev/null; } # started by hand, which is a miserable thing to debug. install_bun() { (cd / && sudo -H -u "$USERNAME" bash -c 'curl -fsSL https://bun.sh/install | bash') >/dev/null 2>&1 - local bin="${USER_HOME}/.bun/bin/bun" - [[ -x "$bin" ]] || return 1 - ln -sf "$bin" /usr/local/bin/bun + [[ -x "${USER_HOME}/.bun/bin/bun" ]] } pm2_installed() { command -v pm2 &>/dev/null; } diff --git a/scripts/setup/machine-setup/machine-setup.sh b/scripts/setup/machine-setup/machine-setup.sh index d9a0708d..61db28ca 100755 --- a/scripts/setup/machine-setup/machine-setup.sh +++ b/scripts/setup/machine-setup/machine-setup.sh @@ -1967,7 +1967,7 @@ if ! skip; then echo " node ${NODE_NOW:+v$NODE_NOW }${NODE_NOW:+-> }LTS $(node_lts_label)" echo " pm2 is a Node application, and officer-pty compiles node-pty" echo " against whatever Node is installed — there is no Linux prebuild." - echo " bun $(bun_installed && bun --version 2>/dev/null || echo 'not installed')" + echo " bun $(bun_installed && bun_version || echo 'not installed')" echo " the platform itself, and nineteen of the twenty pm2 apps." echo " pm2 $(pm2_installed && echo installed || echo 'not installed')" echo " supervises all of them, and the ecosystem files are written for it." @@ -1996,18 +1996,31 @@ if ! skip; then # ── bun ── if bun_installed; then - ok "bun $(bun --version 2>/dev/null) already installed" + ok "bun $(bun_version) already installed" else info " installing bun..." if install_bun && bun_installed; then - ok "bun $(bun --version), symlinked to /usr/local/bin/bun" - SUMMARY+=("Bun: $(bun --version)") + ok "bun $(bun_version)" + SUMMARY+=("Bun: $(bun_version)") else warn "the bun install did not complete" ERRORS+=("Bun: install failed") fi fi + # Ensured every run, not only after an install. A machine that already had bun + # would otherwise never get the link, and the failure only shows up at the next + # reboot. + if ensure_bun_symlink; then + ok "bun symlinked to /usr/local/bin/bun — pm2 at boot has no ~/.bun on PATH" + SUMMARY+=("Bun: symlinked system-wide") + elif [[ -x /usr/local/bin/bun ]]; then + echo " /usr/local/bin/bun already points at it" + else + warn "bun is not at ${USER_HOME}/.bun/bin/bun — cannot link it system-wide" + ERRORS+=("Bun: no system-wide symlink; pm2 apps will fail at boot") + fi + # ── pm2 ── if pm2_installed; then ok "pm2 $(pm2 -v 2>/dev/null | tail -1) already installed"