diff --git a/.tests/test.sh b/.tests/test.sh index e7db0be..a3399c0 100755 --- a/.tests/test.sh +++ b/.tests/test.sh @@ -1095,6 +1095,19 @@ else printf ' \033[33mskip\033[0m needs python3 to open a pty\n' fi +printf '\n\033[1mbootstrap.sh — the first command, wrapped\033[0m\n' +B=$ROOT/bootstrap.sh +if sh -n "$B" 2>/dev/null; then ok "bootstrap.sh parses"; else no "bootstrap.sh has a syntax error"; fi +# The same property the phase 3 negative test asserts for the tier as a whole: +# this file ships in the public repo, so it names no real host, ever. +if grep -qEi 'djdadi|15\.204|xeta' "$B"; then no "bootstrap.sh names a real host"; else ok "bootstrap.sh names no real host"; fi +# No URL from argv, env, or a tty -> usage and exit 2, before curl runs. +rc=0; out=$(DOTFILES_URL= sh "$B" &1) || rc=$? +is "no URL anywhere is refused, not guessed" 2 "$rc" +has "…and says how to call it" "usage:" "$out" +rc=0; out=$(sh "$B" "" &1) || rc=$? +is "an empty argument is an error too, not a prompt hang" 2 "$rc" + printf '\n\033[1minteractive loop (needs a pty)\033[0m\n' if command -v fzf >/dev/null && command -v curl >/dev/null && command -v script >/dev/null; then out=$(sh ./listen-test.sh 2>/dev/null | tr -d '\r' || true) diff --git a/README.md b/README.md index 0c293c8..befe23d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,13 @@ sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply ~/.local/bin/dotup ``` +The first line is also wrapped as `bootstrap.sh` in this repo, for anyone who +can fetch it raw but cannot remember chezmoi's incantation: + +```sh +curl -fsSL /bootstrap.sh | sh -s -- +``` + Two commands. The first lays down the files and clones the six externals; the second picks and installs the software. There is no third step, and nothing here prompts for anything. diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..729bf5a --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# bootstrap.sh -- the whole "first command" of a fresh machine, so nobody has +# to remember chezmoi's incantation. Everything after this is dotup's job. +# +# sh bootstrap.sh # or run it bare and answer the prompt +# +# The repo URL is an argument rather than a constant for the same reason the +# tests prompt for it: this tier is public and names no real host. Your own +# paste-ready line lives in the private repo's runbook. +set -u + +URL=${1:-${DOTFILES_URL:-}} +# Prompt only when someone is there to answer: `curl | sh` and the test +# harness both arrive with stdin not a terminal, and must fail fast instead +# of hanging on a prompt nobody sees. +if [ -z "$URL" ] && [ -t 0 ]; then + printf 'Public repo clone URL: ' >&2 + IFS= read -r URL || : +fi +[ -n "$URL" ] || { echo "usage: bootstrap.sh " >&2; exit 2; } + +command -v curl >/dev/null 2>&1 \ + || { echo "bootstrap.sh: curl is required and not installed" >&2; exit 1; } + +# Not fatal -- the public tier applies fine as root -- but brew-only packages +# (lazygit, omp, herdr) will refuse later, so say it now rather than then. +[ "$(id -u)" -ne 0 ] || echo "bootstrap.sh: running as root — Homebrew will refuse later; a normal user with sudo is the happy path" >&2 + +sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply "$URL" || exit 1 + +echo +echo "next: ~/.local/bin/dotup (full path — this shell's PATH predates the install)"