diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66faf30..0a198ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,56 @@ jobs: HOMEBREW_NO_AUTO_UPDATE: 1 steps: - uses: actions/checkout@v7 - - name: Every Brewfile entry still exists in Homebrew + - name: Every Brewfile entry still exists in Homebrew and isn't deprecated run: | - brew info --formula $(brew bundle list --file=Brewfile --formula) > /dev/null - brew info --cask $(brew bundle list --file=Brewfile --cask) > /dev/null + brew info --json=v2 --formula $(brew bundle list --file=Brewfile --formula) > formulae.json + brew info --json=v2 --cask $(brew bundle list --file=Brewfile --cask) > casks.json + # brew info still succeeds for deprecated and disabled packages, which are about to go away. + retiring="$(jq -r '(.formulae[], .casks[]) | select(.deprecated or .disabled) + | "\(.token // .name): \(if .disabled then "disabled" else "deprecated" end) (\(.disable_reason // .deprecation_reason // "no reason given"))"' \ + formulae.json casks.json)" + if [[ -n "$retiring" ]]; then + echo "$retiring" + exit 1 + fi + + setup: + runs-on: macos-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v7 + - name: First run + run: ./setup.sh + - name: Second run changes nothing + run: | + snapshot() { + brew list --formula --versions + brew list --cask --versions + for file in ~/.zprofile ~/.zshrc ~/.gitconfig ~/.config/ghostty/config \ + ~/.config/herdr/config.toml ~/.docker/config.json; do + echo "== $file" + cat "$file" 2>/dev/null || echo "(missing)" + done + } + snapshot > first-run.txt + ./setup.sh + snapshot > second-run.txt + diff -u first-run.txt second-run.txt + - name: An interactive zsh starts without errors + run: | + # Under a pty, because zsh can't switch its line editor on without one + # and says "can't change option: zle" whatever the config. One perl pass + # drops everything a terminal adds by itself: escape sequences (the + # prompt), control characters, and the caret notation the pty echoes for + # the EOF that ends the session. Whatever is left is a real message. + output="$(script -q /dev/null zsh -i -c exit 2>&1 | perl -ne ' + s/\e\][^\a\e]*(?:\a|\e\\)//g; # window title + s/\e\[[0-9;?]*[a-zA-Z]//g; # colors, cursor moves + s/\e[=>]//g; # keypad mode + s/[\x00-\x08\x0b-\x1f\x7f]//g; # other control characters + next if /^(?:\^[\x3f\x40-\x5f])*\s*$/; # ^D and friends, blank lines + print')" + if [[ -n "$output" ]]; then + echo "$output" + exit 1 + fi diff --git a/Brewfile b/Brewfile index 31f9db2..c9f5666 100644 --- a/Brewfile +++ b/Brewfile @@ -1,6 +1,10 @@ # Everything setup.sh installs. Run on its own with: brew bundle --file=Brewfile # Comment out anything you don't want before running. +# Take over apps you already installed by hand (like Chrome) instead of failing +# with "It seems there is already an App at ...". +cask_args adopt: true + # --- Shell & terminal --- cask "ghostty" # fast, native, GPU-accelerated terminal brew "starship" # fast, minimal prompt (replaces oh-my-zsh themes) diff --git a/README.md b/README.md index b825c33..b021ed9 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Want a different selection? Edit the [`Brewfile`](Brewfile) before running `./se ### What `setup.sh` does - Installs [Homebrew](https://brew.sh/) if it's missing and adds it to your `PATH` in `~/.zprofile` -- Installs everything in the [`Brewfile`](Brewfile) with `brew bundle` +- Installs everything in the [`Brewfile`](Brewfile) with `brew bundle`. Apps you already installed yourself, like Chrome, are handed over to Homebrew rather than installed again - Adds one `source` line to the end of `~/.zshrc` that loads [`dotfiles/zshrc`](dotfiles/zshrc) - Adds a `config-file` line to `~/.config/ghostty/config` that loads [`dotfiles/ghostty`](dotfiles/ghostty), which makes **Cmd+K** clear the screen inside herdr and tmux too, and makes the left Option key work as Alt - Creates `~/.config/herdr/config.toml` from [`dotfiles/herdr.toml`](dotfiles/herdr.toml) if you don't have one, or adds the tab shortcut to the one you have @@ -41,7 +41,7 @@ Want a different selection? Edit the [`Brewfile`](Brewfile) before running `./se - Turns on commit signing with your SSH key, if you have one and haven't set up signing already - Lets the Homebrew `docker` CLI find the `compose` and `buildx` plugins -It never overwrites your existing files, and it's safe to run again: that's also how you install anything you add to the `Brewfile` later. Your own settings in `~/.zshrc` and `~/.gitconfig` still apply, and those in `~/.gitconfig` take precedence. The config is loaded from the cloned repo, so keep it where you cloned it. +If something in the `Brewfile` fails to install, the other steps still run and the failures are listed at the end. It never overwrites your existing files, and it's safe to run again: that's also how you install anything you add to the `Brewfile` later. Your own settings in `~/.zshrc` and `~/.gitconfig` still apply, and those in `~/.gitconfig` take precedence. The config is loaded from the cloned repo, so keep it where you cloned it. ## What gets installed diff --git a/setup.sh b/setup.sh index 2570a1e..724bcea 100755 --- a/setup.sh +++ b/setup.sh @@ -55,7 +55,10 @@ if ! grep -q 'brew shellenv' "$HOME/.zprofile"; then fi step "Apps and tools from the Brewfile" -brew bundle --file="$REPO_DIR/Brewfile" +# A failed install doesn't stop the setup: the steps below don't need it. +# The failures are listed at the end. +BUNDLE_FAILED="" +brew bundle --file="$REPO_DIR/Brewfile" || BUNDLE_FAILED=1 step "Shell config" ZSHRC_LINE="source \"$REPO_DIR/dotfiles/zshrc\"" @@ -151,3 +154,10 @@ if [[ -z "$(git config --global user.email || true)" ]]; then note "- Set your git identity: git config --global user.name \"Your Name\"" note " git config --global user.email \"you@example.com\"" fi + +if [[ -n "$BUNDLE_FAILED" ]]; then + step "Some Brewfile entries didn't install" + brew bundle check --file="$REPO_DIR/Brewfile" --no-upgrade --verbose 2>&1 | sed 's/^/ /' || true + note "Look for \"Error:\" in the brew bundle output above, fix it, then re-run ./setup.sh" + exit 1 +fi