-
Notifications
You must be signed in to change notification settings - Fork 1
Refactor bootstrap, optimize package installation, and fix macOS compatibility issues #320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f1cea04
a1b313c
ccd328a
0cab537
584055f
7ea0943
57c2dab
f9e9665
fffaf0a
a29ec8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,16 @@ cd "$(dirname "${BASH_SOURCE}")"; | |
| GATEWAY_HOST="mac-mini.tailac7b3c.ts.net" | ||
| INSTALL_AI="" | ||
|
|
||
| # Set up Homebrew environment variables | ||
| if [[ "$(uname)" == "Darwin" ]]; then | ||
| if [[ -f "/opt/homebrew/bin/brew" ]]; then | ||
| eval "$(/opt/homebrew/bin/brew shellenv)" | ||
| elif [[ -f "/usr/local/bin/brew" ]]; then | ||
| eval "$(/usr/local/bin/brew shellenv)" | ||
| fi | ||
| fi | ||
|
|
||
|
|
||
| # ============================================================================== | ||
| # Functions | ||
| # ============================================================================== | ||
|
|
@@ -47,6 +57,14 @@ set_default_shell() { | |
| return 0 | ||
| fi | ||
|
|
||
| # chsh requires manual password entry on macOS unless run as root. | ||
| # Skip in non-interactive sessions to avoid hanging. | ||
| if [[ ! -t 0 ]]; then | ||
| echo "Warning: Cannot change default shell to zsh automatically in non-interactive mode." | ||
| echo " Please run manually: chsh -s $zsh_path" | ||
| return 0 | ||
| fi | ||
|
|
||
| echo "Setting default shell to zsh..." | ||
| if chsh -s "$zsh_path" 2>/dev/null; then | ||
| echo "Default shell set to zsh" | ||
|
|
@@ -368,9 +386,9 @@ install_tmux_plugin_manager() { | |
| mkdir -p "$tpm_dir" | ||
| git clone https://github.com/tmux-plugins/tpm "$tpm_dir" | ||
|
|
||
| # Source tmux config if tmux is installed | ||
| if command -v tmux >/dev/null 2>&1 && [[ -e "$HOME/.tmux.conf" ]]; then | ||
| tmux source "$HOME/.tmux.conf" | ||
| # Source tmux config if tmux is installed and running | ||
| if command -v tmux >/dev/null 2>&1 && tmux info &>/dev/null && [[ -e "$HOME/.tmux.conf" ]]; then | ||
| tmux source "$HOME/.tmux.conf" || true | ||
| fi | ||
| } | ||
|
|
||
|
|
@@ -488,9 +506,9 @@ prompt_ai_install() { | |
| return 0 | ||
| fi | ||
|
|
||
| # Non-interactive: default to install | ||
| # Non-interactive: default to skip | ||
| if [[ ! -t 0 ]]; then | ||
| INSTALL_AI=true | ||
| INSTALL_AI=false | ||
| return 0 | ||
| fi | ||
|
|
||
|
|
@@ -809,6 +827,14 @@ install_packages() { | |
| if ! command -v brew >/dev/null 2>&1; then | ||
| echo "Installing Homebrew..." | ||
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | ||
| if [[ -f "/opt/homebrew/bin/brew" ]]; then | ||
| eval "$(/opt/homebrew/bin/brew shellenv)" | ||
| elif [[ -f "/usr/local/bin/brew" ]]; then | ||
| eval "$(/usr/local/bin/brew shellenv)" | ||
| fi | ||
| else | ||
| echo "Updating Homebrew..." | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] This branch now runs only |
||
| brew update | ||
| fi | ||
|
|
||
| install_brew_packages | ||
|
|
@@ -830,15 +856,6 @@ install_packages() { | |
|
|
||
| update_packages() { | ||
| if [[ "$(uname)" == "Darwin" ]]; then | ||
| if command -v brew >/dev/null 2>&1; then | ||
| echo "Updating Homebrew and upgrading packages..." | ||
| brew update | ||
| brew upgrade | ||
| brew cleanup | ||
| else | ||
| echo "Homebrew not installed, skipping" | ||
| fi | ||
|
|
||
| if command -v softwareupdate >/dev/null 2>&1; then | ||
| echo "Checking for macOS software updates..." | ||
| softwareupdate --list | ||
|
|
@@ -869,7 +886,7 @@ update_packages() { | |
| # Update global npm packages | ||
| if command -v npm >/dev/null 2>&1; then | ||
| echo "Updating global npm packages..." | ||
| npm update -g | ||
| npm update -g || true | ||
| fi | ||
|
|
||
| # Update cargo packages | ||
|
|
@@ -881,7 +898,7 @@ update_packages() { | |
| fi | ||
|
|
||
| # Update pip packages (no safe "update all" — list managed packages explicitly) | ||
| if command -v pip3 >/dev/null 2>&1; then | ||
| if [[ "$INSTALL_AI" == true ]] && command -v pip3 >/dev/null 2>&1; then | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Gating the pip update on INSTALL_AI == true means it never runs on Linux: prompt_ai_install (the only setter of INSTALL_AI) is invoked only inside the macOS install_brew_packages path. On Debian/Ubuntu/SteamOS, INSTALL_AI stays empty, so piper-tts is no longer installed or upgraded there, with no way to opt in. If that is intended (AI is macOS/gateway-only), a short comment here would keep the asymmetry from being read as a bug later. |
||
| echo "Updating pip packages..." | ||
| pip3 install --upgrade piper-tts 2>/dev/null || pip3 install --upgrade --break-system-packages piper-tts 2>/dev/null || true | ||
| fi | ||
|
|
@@ -985,19 +1002,48 @@ install_brew_packages() { | |
| return 0 | ||
| fi | ||
|
|
||
| local temp_pip_conf=false | ||
| # Check if we need to temporarily override pip.conf to bypass corporate mirror issues for gcloud-cli | ||
| if [[ "$(uname)" == "Darwin" ]] && [[ -f "/Library/Application Support/pip/pip.conf" ]]; then | ||
| if [[ ! -f "$HOME/.config/pip/pip.conf" ]]; then | ||
| echo "Temporarily configuring PyPI mirror for Homebrew Python dependencies..." | ||
| mkdir -p "$HOME/.config/pip" | ||
| echo -e "[global]\nindex-url = https://pypi.org/simple" > "$HOME/.config/pip/pip.conf" | ||
| temp_pip_conf=true | ||
| # Guarantee cleanup even if a brew bundle below aborts under set -euo | ||
| # pipefail. A leftover temp file would otherwise mask the corporate | ||
| # pip.conf on every subsequent run (the guard above skips re-creating | ||
| # it once $HOME/.config/pip/pip.conf exists). | ||
| trap 'rm -f "$HOME/.config/pip/pip.conf"' EXIT | ||
| fi | ||
| fi | ||
|
|
||
| echo "Installing Homebrew packages..." | ||
| # Use --adopt to take ownership of existing apps instead of erroring | ||
| HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$brewfile" | ||
| # Use --adopt to take ownership of existing apps instead of erroring. | ||
| # Tolerate a non-zero exit (e.g. a `mas` entry failing because the machine | ||
| # isn't signed into the App Store) so Phase 1 doesn't abort before | ||
| # sync_dotfiles runs — brew bundle already reports which packages failed. | ||
| HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$brewfile" \ | ||
| || echo " Warning: some Homebrew packages failed to install (continuing)." | ||
|
|
||
| # Conditionally install AI assistant packages (Claude, OpenClaw tools) | ||
| prompt_ai_install | ||
| if [[ "$INSTALL_AI" == true ]]; then | ||
| local ai_brewfile="$dotfiles_dir/Brewfile.ai" | ||
| if [[ -f "$ai_brewfile" ]]; then | ||
| echo "Installing AI assistant packages..." | ||
| HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$ai_brewfile" | ||
| HOMEBREW_CASK_OPTS="--adopt" brew bundle --file="$ai_brewfile" \ | ||
| || echo " Warning: some AI Homebrew packages failed to install (continuing)." | ||
| fi | ||
| fi | ||
|
|
||
| # Remove the temporary pip.conf on the success path and clear the | ||
| # safety-net trap (cleanup only ran via the trap on an early abort). | ||
| if [[ "$temp_pip_conf" == true ]]; then | ||
| rm -f "$HOME/.config/pip/pip.conf" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Important] The script runs under set -euo pipefail. If brew bundle at line 1018 (or the AI bundle at 1026) exits non-zero, the script aborts before reaching this cleanup, leaving the temp ~/.config/pip/pip.conf in place. Since that temp file masks the corporate /Library/Application Support/pip/pip.conf (which this code only touches when no user config exists), a failed brew bundle permanently shadows the corporate mirror config on subsequent runs too: the existence guard at line 1008 then sees the leftover file and skips recreating/cleaning it. Guarantee cleanup with a trap set right after creating the temp file (trap to rm the file on RETURN/EXIT) instead of an inline rm at the end of the function. |
||
| trap - EXIT | ||
| echo "Removed temporary PyPI mirror override." | ||
| fi | ||
| } | ||
|
|
||
| run_brew_hooks() { | ||
|
|
@@ -1072,7 +1118,7 @@ install_yazi_flavor() { | |
| # Install catppuccin-mocha flavor if not present | ||
| if ! $ya_cmd pkg list 2>/dev/null | grep -q "catppuccin-mocha"; then | ||
| echo "Installing yazi catppuccin-mocha flavor..." | ||
| $ya_cmd pkg add yazi-rs/flavors:catppuccin-mocha | ||
| $ya_cmd pkg add yazi-rs/flavors:catppuccin-mocha || true | ||
| fi | ||
| } | ||
|
|
||
|
|
@@ -1143,6 +1189,42 @@ install_claude_mcp_servers() { | |
| fi | ||
| } | ||
|
|
||
| configure_npm_registry() { | ||
| # Skip if ~/.npmrc already contains the registry auth configurations | ||
| if [[ -f "$HOME/.npmrc" ]] && grep -q "ah-3p-staging-npm" "$HOME/.npmrc" 2>/dev/null; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Require gcloud and npm to be installed | ||
| if ! command -v gcloud >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then | ||
| return 0 | ||
| fi | ||
|
|
||
| # Check if user has an active gcloud account | ||
| local active_account | ||
| active_account=$(gcloud config get-value account 2>/dev/null) || return 0 | ||
| if [[ -z "$active_account" ]]; then | ||
| return 0 | ||
| fi | ||
|
|
||
| echo "Configuring NPM registry credentials via gcloud..." | ||
| local settings | ||
| if settings=$(gcloud artifacts print-settings npm --project=artifact-foundry-prod --repository=ah-3p-staging-npm --location=us 2>/dev/null); then | ||
| # Strip the deprecated always-auth line. The trailing `|| true` keeps a | ||
| # fully-filtered output (grep exit 1) from aborting bootstrap under | ||
| # `set -euo pipefail`. | ||
| # Note: print-settings embeds a short-lived OAuth _authToken that is not | ||
| # refreshed here (the guard above skips re-running once ah-3p-staging-npm | ||
| # is present in ~/.npmrc). Re-run this step if npm auth starts failing. | ||
| # Prepend a newline so the first appended line can't concatenate onto an | ||
| # existing ~/.npmrc entry that lacks a trailing newline. | ||
| { printf '\n'; echo "$settings" | { grep -v "always-auth" || true; }; } >> "$HOME/.npmrc" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Two small notes on this append: (1) the unconditional leading newline writes a blank first line into a freshly-created ~/.npmrc; guarding on a non-empty file before prepending the newline avoids the stray blank line. (2) As the comment above notes, the embedded short-lived OAuth token is not refreshed on later runs because the ah-3p-staging-npm guard skips re-execution. That is documented, so just flagging it as a known re-run footgun for when npm auth starts failing. |
||
| echo "NPM registry credentials configured in ~/.npmrc" | ||
| else | ||
| echo " Warning: Failed to configure NPM credentials automatically" | ||
| fi | ||
| } | ||
|
|
||
| sync_dotfiles() { | ||
| # Set default shell to zsh | ||
| set_default_shell | ||
|
|
@@ -1201,6 +1283,9 @@ sync_dotfiles() { | |
| install_launch_agents | ||
| cleanup_legacy_cron | ||
|
|
||
| # Configure NPM registry auth if authenticated with gcloud | ||
| configure_npm_registry | ||
|
|
||
| # Reload zsh configuration | ||
| if [[ -n "${ZSH_VERSION:-}" ]]; then | ||
| source ~/.zshrc 2>/dev/null || echo "Restart your terminal or run: source ~/.zshrc" | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env bash | ||
| # Post-install hook for Stats app | ||
| # Imports Stats preferences from dotfiles | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| dotfiles_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" | ||
| plist_src="$dotfiles_dir/preferences/Stats.plist" | ||
|
|
||
| if [[ ! -f "$plist_src" ]]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| # Only run on macOS | ||
| if [[ "$(uname)" != "Darwin" ]]; then | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "Importing Stats preferences..." | ||
| # Close Stats app if running to ensure it picks up the new config on restart | ||
| killall Stats 2>/dev/null || true | ||
|
|
||
| defaults import eu.exelban.Stats "$plist_src" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] This |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,15 @@ | |
| # GEMINI_API_KEY - Required for Gemini API access | ||
| # AGENT_EVENT_BUS_URL - URL for event bus MCP server (e.g., https://host/agent-event-bus/mcp) | ||
|
|
||
| # Set up Homebrew PATH for macOS | ||
| if [[ "$(uname)" == "Darwin" ]]; then | ||
| if [[ -f "/opt/homebrew/bin/brew" ]]; then | ||
| eval "$(/opt/homebrew/bin/brew shellenv)" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] eval of brew shellenv forks a brew subprocess on every interactive shell startup; with many panes or tabs (zellij) that latency adds up. Since bootstrap.sh already runs the same block at its top for its own session, you could skip it here or guard on an unset HOMEBREW_PREFIX so it evaluates only once per shell tree. |
||
| elif [[ -f "/usr/local/bin/brew" ]]; then | ||
| eval "$(/usr/local/bin/brew shellenv)" | ||
| fi | ||
| fi | ||
|
|
||
| # PATH configuration | ||
| if [ -d "$HOME/.local/bin" ]; then | ||
| export PATH="$HOME/.local/bin:$PATH" | ||
|
|
||
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion]
masinstall requires an active App Store session. On a machine not signed in, these entries causebrew bundleto exit non-zero, and since bootstrap.sh runs underset -euo pipefailwith no|| trueon thebrew bundle --filecall, the whole bootstrap aborts during Phase 1 — before sync_dotfiles runs. Given the PR's robustness theme, consider tolerating bundle failure or noting the App Store sign-in prerequisite.