Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 24 additions & 18 deletions bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,31 @@ if ! command -v git &>/dev/null; then
fi

# Resolve installation directory
echo -e " ${BOLD}Installation directory${RESET}"
echo -e " ${DIM}Default: ${DEFAULT_DEST}${RESET}"
echo ""
read -p " Install here? [Y/n] " _confirm
if [[ "$_confirm" =~ ^[Nn]$ ]]; then
while true; do
read -p " Enter path: " _custom
# Expand ~ manually since read doesn't expand it
_custom="${_custom/#\~/$HOME}"
if [[ -n "$_custom" ]]; then
DEST="$_custom"
break
fi
echo -e " ${RED}[ERROR]${RESET} Path cannot be empty."
done
else
DEST="$DEFAULT_DEST"
if [[ -z "${DEST:-}" ]]; then
echo -e " ${BOLD}Installation directory${RESET}"
echo -e " ${DIM}Default: ${DEFAULT_DEST}${RESET}"
echo ""
if [[ -t 0 ]]; then
read -r -p " Install here? [Y/n] " _confirm
else
_confirm=""
fi
if [[ "$_confirm" =~ ^[Nn]$ ]]; then
while true; do
read -r -p " Enter path: " _custom
# Expand ~ manually since read doesn't expand it
_custom="${_custom/#\~/$HOME}"
if [[ -n "$_custom" ]]; then
DEST="$_custom"
break
fi
echo -e " ${RED}[ERROR]${RESET} Path cannot be empty."
done
else
DEST="$DEFAULT_DEST"
fi
echo ""
fi
echo ""

# Clone or update
if [[ -d "$DEST/.git" ]]; then
Expand Down
9 changes: 6 additions & 3 deletions config/zsh/zsh_aliases.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ git_clean_branches() {
git fetch "$r" --tags --force --prune --prune-tags
done

git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads \
| awk '$2=="[gone]"{print $1}' \
| xargs -r git branch -D
local gone_branches
gone_branches=$(git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads \
| awk '$2=="[gone]"{print $1}')
if [[ -n "$gone_branches" ]]; then
echo "$gone_branches" | xargs git branch -D
fi
}
alias git-clean-branches='git_clean_branches'

Expand Down
3 changes: 3 additions & 0 deletions config/zsh/zsh_postload.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# PATH: prioritize Homebrew ARM, fallback to Intel if needed
export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:$PATH"

# LM Studio CLI
export PATH="$HOME/.cache/lm-studio/bin:$PATH"

# ---- Homebrew ARM (default) ----
if [[ "$(uname -m)" == "arm64" ]] && [[ -x /opt/homebrew/bin/brew ]]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
Expand Down
2 changes: 1 addition & 1 deletion lib/doctor.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ run_doctor() {
echo ""
echo -e " ${BOLD}Logs${RESET}"
local log_count
log_count=$(ls "$LOG_DIR"/*.log 2>/dev/null | wc -l | tr -d ' ')
log_count=$(find "$LOG_DIR" -maxdepth 1 -name "*.log" 2>/dev/null | wc -l | tr -d ' ')
echo -e " ${DIM}${log_count} install log(s) in ${LOG_DIR}${RESET}"

echo ""
Expand Down
21 changes: 13 additions & 8 deletions lib/install_core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# ============================================================================

installBrew() {
((step++))
((step += 1))
log_step "Install Brew"
if [[ "$DRY_RUN" == true ]]; then
log_dry "/bin/bash Homebrew install script"
Expand All @@ -30,7 +30,7 @@ installBrew() {
}

installGit() {
((step++))
((step += 1))
log_step "Install Git"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install git && git config --global user.name/email"
Expand Down Expand Up @@ -63,7 +63,7 @@ installGit() {
}

installZsh() {
((step++))
((step += 1))
log_step "Install ZSH + Oh My Zsh"
if [[ "$DRY_RUN" == true ]]; then
log_dry "Install Zsh, Oh My Zsh, p10k, plugins, Nerd Font, symlinks + inject dotfiles into ~/.zshrc"
Expand Down Expand Up @@ -137,13 +137,18 @@ export DOTFILE_PATH="'"${DOTFILE_PATH}"'"\
fi

log_info "Installing Zsh plugins..."
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "${ZSH_CUSTOM:-${HOME}/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting"
git clone https://github.com/zsh-users/zsh-autosuggestions "${ZSH_CUSTOM:-${HOME}/.oh-my-zsh/custom}/plugins/zsh-autosuggestions"
local zsh_custom="${ZSH_CUSTOM:-${HOME}/.oh-my-zsh/custom}"
[ -d "${zsh_custom}/plugins/zsh-syntax-highlighting" ] || \
git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "${zsh_custom}/plugins/zsh-syntax-highlighting"
[ -d "${zsh_custom}/plugins/zsh-autosuggestions" ] || \
git clone https://github.com/zsh-users/zsh-autosuggestions "${zsh_custom}/plugins/zsh-autosuggestions"
[ "$(uname -s)" == "Darwin" ] && brew_install coreutils
git clone https://github.com/supercrabtree/k "${ZSH_CUSTOM:-${HOME}/.oh-my-zsh/custom}/plugins/k"
[ -d "${zsh_custom}/plugins/k" ] || \
git clone https://github.com/supercrabtree/k "${zsh_custom}/plugins/k"

log_info "Installing PowerLevel10k theme..."
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k"
[ -d "${zsh_custom}/themes/powerlevel10k" ] || \
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git "${zsh_custom}/themes/powerlevel10k"

log_info "Installing Nerd Font..."
if [[ "$(uname -s)" == "Darwin" ]]; then
Expand All @@ -160,7 +165,7 @@ export DOTFILE_PATH="'"${DOTFILE_PATH}"'"\
}

installAsdf() {
((step++))
((step += 1))
log_step "Install ASDF"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install asdf + plugins: nodejs, pnpm, python, java"
Expand Down
16 changes: 8 additions & 8 deletions lib/install_software.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# ============================================================================

installSoftwarePro() {
((step++))
((step += 1))
log_step "Install Software: Pro Bundle"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask visual-studio-code iterm2 sublime-text rectangle cakebrew grandperspective spotify vivaldi audio-hijack whatsapp discord + qemu colima docker"
Expand All @@ -34,7 +34,7 @@ installSoftwarePro() {
}

installSoftwareDevelopment() {
((step++))
((step += 1))
log_step "Install Software: Development"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask visual-studio-code iterm2 wave sublime-text notion anki + qemu colima docker"
Expand All @@ -52,7 +52,7 @@ installSoftwareDevelopment() {
}

installSoftwareLLM() {
((step++))
((step += 1))
log_step "Install Software: LLM Tools"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask lm-studio chatgpt superwhisper opencode opencode-desktop antigravity"
Expand All @@ -69,7 +69,7 @@ installSoftwareLLM() {
}

installSoftwareTools() {
((step++))
((step += 1))
log_step "Install Software: Tools"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask rectangle oversight logi-options-plus jdownloader background-music grandperspective pearcleaner clop protonvpn jordanbaird-ice"
Expand All @@ -90,7 +90,7 @@ installSoftwareTools() {
}

installSoftwareCommunication() {
((step++))
((step += 1))
log_step "Install Software: Communication"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask audio-hijack slack whatsapp discord signal"
Expand All @@ -106,7 +106,7 @@ installSoftwareCommunication() {
}

installSoftwareOffice() {
((step++))
((step += 1))
log_step "Install Software: Office"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask microsoft-office"
Expand All @@ -118,7 +118,7 @@ installSoftwareOffice() {
}

installSoftwareGames() {
((step++))
((step += 1))
log_step "Install Software: Games"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask nvidia-geforce-now epic-games steam prismlauncher scummvm obs openemu sony-ps-remote-play moonlight"
Expand All @@ -138,7 +138,7 @@ installSoftwareGames() {
}

installSoftwareOthers() {
((step++))
((step += 1))
log_step "Install Software: Others"
if [[ "$DRY_RUN" == true ]]; then
log_dry "brew install --cask spotify calibre kindle-previewer send-to-kindle hakuneko affinity vivaldi"
Expand Down
15 changes: 11 additions & 4 deletions tests/bootstrap.bats
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ BOOTSTRAP="${REPO_ROOT}/bootstrap.sh"
# ── Group I: bootstrap.sh non-network logic ────────────────────────────────

@test "I1: bootstrap.sh exits 1 and prints [ERROR] when git is not available" {
# Run bootstrap in a subshell where git is not on PATH
run env PATH="/usr/bin:/bin" bash "${BOOTSTRAP}" 2>&1 || true
# Create a temp empty dir to use as PATH, ensuring git is not found
local empty_path
empty_path=$(mktemp -d)

# Run bootstrap with an empty PATH so git is not on PATH
run /bin/bash -c "PATH='${empty_path}' /bin/bash '${BOOTSTRAP}'" < /dev/null 2>&1 || true

rm -rf "$empty_path"

# The script should mention git is required and exit non-zero
[[ "$output" == *"git is required"* ]]
Expand All @@ -32,7 +38,8 @@ BOOTSTRAP="${REPO_ROOT}/bootstrap.sh"
git_calls=""
bash_calls=""

run env DEST="$tmpdir" bash -c "
# Pipe empty stdin so the non-interactive path is taken (skips read prompt)
run bash -c "
git() {
echo \"GIT:\$*\"
# simulate successful pull
Expand All @@ -45,7 +52,7 @@ BOOTSTRAP="${REPO_ROOT}/bootstrap.sh"
export -f git bash
DEST='${tmpdir}'
source '${BOOTSTRAP}'
" 2>&1 || true
" < /dev/null 2>&1 || true

# Should have called git pull, not git clone
[[ "$output" == *"pulling latest"* || "$output" == *"GIT:-C"* || "$output" == *"pull"* ]]
Expand Down
4 changes: 2 additions & 2 deletions tests/zshrc_config.bats
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ ALIASES="${REPO_ROOT}/config/zsh/zsh_aliases.zsh"
[[ "$output" == *"git-clean-branches"* ]]
}

@test "M9: git_clean_branches function is defined in zsh_postload.zsh" {
run grep -n 'git_clean_branches()' "$POSTLOAD"
@test "M9: git_clean_branches function is defined in zsh_aliases.zsh" {
run grep -n 'git_clean_branches()' "$ALIASES"
[ "$status" -eq 0 ]
}

Expand Down