Skip to content

Commit 5abf2e1

Browse files
committed
feat!: reorganize scripts into configure/, lib/, and sync/ directories
- Move configuration scripts from util/ to configure/ - Extract shared utilities into lib/ (colors, backup, packages) - Move repo sync functionality to sync/ directory - Add run.sh as main entry point replacing util/setup.sh - Remove morning.sh (functionality moved to sync/repos.sh) - Add tests for package management functions
1 parent 2a728ae commit 5abf2e1

38 files changed

Lines changed: 1330 additions & 773 deletions

MANUAL_STEPS.md

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,18 @@ git config --global user.name "Your Name"
4141
git config --global user.email "Your email specifically for git"
4242
```
4343

44-
## Morning routine setup
44+
## GitLab repo sync setup
4545

46-
To enable the `morning` command for GitLab repo syncing:
46+
To enable GitLab repo syncing:
4747

48-
1. Create the work directory:
48+
1. Configure your GitLab group in `~/.env.sh`:
4949
```bash
50-
mkdir ~/work
50+
export GITLAB_GROUP="your-group"
51+
# Optional: exclude specific subdirectories (pipe-separated)
52+
export GITLAB_EXCLUDE_DIRS="archived|sandbox"
5153
```
5254

53-
2. Configure your GitLab group in `~/.env.sh`:
54-
```bash
55-
export MORNING_GITLAB_GROUP="your-group"
56-
# Optional: exclude specific subdirectories
57-
export MORNING_EXCLUDE_DIRS="unsynced|bugs"
58-
```
59-
60-
3. Authenticate with GitLab:
55+
2. Authenticate with GitLab:
6156
```bash
6257
glab auth login
6358
```
@@ -112,9 +107,9 @@ sure to complete any steps noted:
112107
1. WhatsApp
113108
1. Zoom
114109

115-
Finally, you need to run the following command in your terminal to finish the
116-
automated setups:
110+
Finally, after signing in to Firefox (to create the profile folder), run the
111+
following command to finish automated setup:
117112

118113
```bash
119-
chmod +x ~/projects/setup/util/after_signin.sh && ~/projects/setup/util/after_signin.sh
114+
~/projects/setup/configure/after_signin.sh
120115
```

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ test:
88
bats tests/
99

1010
setup:
11-
./util/setup.sh
11+
./run.sh
1212

1313
fix:
1414
shellharden --replace **/*.sh

bootstrap.sh

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
# Bash strict mode
88
set -euo pipefail
99

10-
# Print commands as they are run
11-
set -v
10+
# Guard: refuse to run as root
11+
if [ "$(id -u)" -eq 0 ]; then
12+
echo "Error: Do not run this script as root" >&2
13+
exit 1
14+
fi
1215

1316
# Trap handler for cleanup on interruption
1417
CURRENT_STEP=""
15-
# shellcheck disable=SC2329 # Invoked by trap
18+
# shellcheck disable=SC2329 # Function appears unused but is invoked by trap
1619
cleanup_on_interrupt() {
1720
echo "" >&2
1821
echo "Bootstrap interrupted!" >&2
@@ -24,25 +27,22 @@ cleanup_on_interrupt() {
2427
}
2528
trap cleanup_on_interrupt INT TERM
2629

27-
# Start running the print utility first so we can update the user on progress.
28-
# We must save the file first because we are on Bash 3.2
29-
if ! curl -fsSL https://raw.githubusercontent.com/nickineering/setup/master/util/print.sh >/tmp/print.sh; then
30-
echo "Error: Failed to download print utility. Check your internet connection." >&2
31-
exit 1
32-
fi
33-
# shellcheck source=util/print.sh
34-
source /tmp/print.sh
30+
# Colors for output
31+
green='\033[32m'
32+
yellow='\033[33m'
33+
cyan='\033[36m'
34+
bold='\033[1m'
35+
reset='\033[0m'
3536

36-
print_green "Please leave everything closed and wait for your Mac to be configured. \
37-
This will take a while." "AUTOMATICALLY CONFIGURING MAC"
37+
echo -e "${bold}${cyan}=== AUTOMATICALLY CONFIGURING MAC ===${reset}"
38+
echo -e "${green}Please leave everything closed and wait for your Mac to be configured. This will take a while.${reset}"
3839

3940
CURRENT_STEP="installing Homebrew"
4041
# Install Homebrew, a Mac package manager
4142
if command -v brew; then
42-
brew upgrade || print_green "Warning: Some Homebrew packages failed to upgrade"
43-
print_green "Upgraded Homebrew packages"
43+
echo -e "${green}Homebrew already installed${reset}"
4444
else
45-
print_green "Installing Homebrew..."
45+
echo -e "${green}Installing Homebrew...${reset}"
4646
HOMEBREW_INSTALL_SCRIPT=$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)
4747
if [ "$HOMEBREW_INSTALL_SCRIPT" = "" ]; then
4848
echo "Error: Failed to download Homebrew installer" >&2
@@ -53,7 +53,7 @@ else
5353
exit 1
5454
fi
5555
eval "$(/opt/homebrew/bin/brew shellenv)"
56-
print_green "Installed Homebrew"
56+
echo -e "${green}Installed Homebrew${reset}"
5757
fi
5858

5959
CURRENT_STEP="cloning setup repository"
@@ -62,14 +62,14 @@ mkdir -p ~/projects
6262
export SETUP=~/projects/setup
6363
brew install git # Use Homebrew so that updates are easy
6464
if [ -d "$SETUP" ]; then
65-
git -C "$SETUP" pull || print_green "Warning: Failed to pull latest commits"
66-
print_green "Pulled latest commits from repo"
65+
git -C "$SETUP" pull || echo -e "${yellow}Warning: Failed to pull latest commits${reset}"
66+
echo -e "${green}Pulled latest commits from repo${reset}"
6767
else
6868
if ! git clone https://github.com/nickineering/setup.git "$SETUP"; then
6969
echo "Error: Failed to clone setup repo" >&2
7070
exit 1
7171
fi
72-
print_green "Cloned repo into projects directory"
72+
echo -e "${green}Cloned setup repo into projects directory${reset}"
7373
fi
7474

7575
CURRENT_STEP="installing modern Bash"
@@ -79,4 +79,4 @@ if ! brew install bash; then
7979
echo "Error: Failed to install bash via Homebrew" >&2
8080
exit 1
8181
fi
82-
source "$SETUP"/util/setup.sh
82+
"$SETUP"/run.sh

configure/after_signin.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/opt/homebrew/bin/bash
2+
# shellcheck disable=SC2154 # Variables like $green are defined in lib/colors.sh
3+
#
4+
# Post-setup configuration for things that need apps to be launched first:
5+
# - Firefox: needs profile folder to exist (created on first launch)
6+
# - GitHub CLI: interactive authentication
7+
8+
set -euo pipefail
9+
10+
export SETUP=~/projects/setup
11+
export DOTFILES="$SETUP/linked"
12+
cd "$SETUP"
13+
14+
source lib/colors.sh
15+
source lib/backup.sh
16+
17+
echo -e "${bold}${cyan}=== Post-setup configuration ===${reset}"
18+
echo ""
19+
20+
# Firefox settings (requires Firefox to have been launched once)
21+
source configure/firefox.sh
22+
if [[ "${FIREFOX_NEEDS_SETUP:-}" != "1" ]]; then
23+
echo -e "${green}Configured Firefox${reset}"
24+
fi
25+
26+
# GitHub CLI authentication
27+
if ! gh auth status &>/dev/null; then
28+
echo ""
29+
echo -e "${bold}GitHub CLI authentication${reset}"
30+
gh auth login
31+
echo -e "${green}Configured GitHub CLI${reset}"
32+
else
33+
echo -e "${dim}GitHub CLI already authenticated${reset}"
34+
fi
35+
36+
echo ""
37+
echo -e "${green}Done!${reset}"
Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
#!/opt/homebrew/bin/bash
1+
# shellcheck shell=bash
2+
# shellcheck disable=SC2154 # Variables like $green are defined in lib/colors.sh
3+
# Sourced by run.sh
24

35
# Configure Claude Code settings and instructions
46

@@ -10,20 +12,19 @@ CLAUDE_COPIED="$SETUP/copied/claude"
1012
mkdir -p "$CLAUDE_DIR"
1113

1214
# Backup existing files
13-
source backup_or_delete.sh
1415
backup_or_delete "$CLAUDE_DIR/settings.json"
1516
backup_or_delete "$CLAUDE_DIR/CLAUDE.md"
1617
backup_or_delete "$CLAUDE_DIR/skills"
1718

1819
# Symlink settings, instructions, and skills
19-
ln -s "$CLAUDE_DOTFILES/settings.json" "$CLAUDE_DIR/settings.json"
20-
ln -s "$CLAUDE_DOTFILES/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md"
21-
ln -s "$CLAUDE_DOTFILES/skills" "$CLAUDE_DIR/skills"
20+
ln -sf "$CLAUDE_DOTFILES/settings.json" "$CLAUDE_DIR/settings.json"
21+
ln -sf "$CLAUDE_DOTFILES/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md"
22+
ln -sfn "$CLAUDE_DOTFILES/skills" "$CLAUDE_DIR/skills"
2223

2324
# Copy CLAUDE.local.md template if it doesn't exist
2425
if [ ! -f "$CLAUDE_DIR/CLAUDE.local.md" ]; then
2526
cp "$CLAUDE_COPIED/CLAUDE.local.md" "$CLAUDE_DIR/CLAUDE.local.md"
26-
print_green "Created Claude local instructions template"
27+
echo -e "${green}Created Claude local instructions template${reset}"
2728
fi
2829

29-
print_green "Configured Claude Code"
30+
echo -e "${dim}Configured Claude Code${reset}"

configure/firefox.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# shellcheck shell=bash
2+
# shellcheck disable=SC2154 # Variables like $yellow defined in lib/colors.sh
3+
# Sourced by run.sh (requires lib/colors.sh, lib/backup.sh)
4+
5+
# Install custom Firefox settings
6+
FIREFOX_FOLDER="$HOME/Library/Application Support/Firefox/Profiles"
7+
if [ ! -d "$FIREFOX_FOLDER" ]; then
8+
# Firefox not launched yet - run.sh will remind user at the end
9+
export FIREFOX_NEEDS_SETUP=1
10+
else
11+
FIREFOX_PROFILE=$(find "$FIREFOX_FOLDER" -maxdepth 1 -name '*.dev-edition-default' 2>/dev/null | head -n1)
12+
if [ "$FIREFOX_PROFILE" = "" ]; then
13+
echo -e "${dim}Could not find Firefox profile folder. Skipping Firefox settings...${reset}"
14+
else
15+
16+
backup_or_delete "$FIREFOX_PROFILE/user.js"
17+
if ! ln -sf "$DOTFILES/user.js" "$FIREFOX_PROFILE/user.js"; then
18+
echo -e "${yellow}Warning: Failed to link Firefox user.js${reset}"
19+
fi
20+
fi
21+
fi
Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#!/opt/homebrew/bin/bash
1+
# shellcheck shell=bash
2+
# Sourced by run.sh - configure macOS system preferences and Dock
23

34
# Disable screensaver
45
defaults -currentHost write com.apple.screensaver idleTime 0
@@ -21,8 +22,15 @@ defaults write com.apple.dock enable-spring-load-actions-on-all-items -bool true
2122
# Speeds up window minimizing and maximizing
2223
defaults write com.apple.dock mineffect -string "scale"
2324

24-
# Remove all apps kept in Dock by default
25-
defaults write com.apple.dock persistent-apps -array
25+
# Clear Dock apps only if Dock is at default (has Mail/Safari) - skip on re-runs
26+
current_dock=$(defaults read com.apple.dock persistent-apps 2>/dev/null | grep -c "file-label") || current_dock=0
27+
if [[ "$current_dock" -gt 0 ]]; then
28+
# Check if it looks like default Dock (has Safari)
29+
has_safari=$(defaults read com.apple.dock persistent-apps 2>/dev/null | grep -c "Safari") || has_safari=0
30+
if [[ "$has_safari" -gt 0 ]]; then
31+
defaults write com.apple.dock persistent-apps -array
32+
fi
33+
fi
2634

2735
# Don't show recent apps not presently open in the dock
2836
defaults write com.apple.dock show-recents -bool FALSE
@@ -77,27 +85,32 @@ defaults write NSGlobalDomain AppleShowAllExtensions -bool true
7785
defaults write kCFPreferencesAnyApplication TSMLanguageIndicatorEnabled 0
7886

7987
# Import `add_to_dock` function
80-
source add_to_dock.sh
88+
source "$SETUP/lib/add_to_dock.sh"
8189

8290
# Add the following applications to the Mac dock
83-
add_to_dock "1Password"
84-
add_to_dock "Boop"
85-
add_to_dock "Calculator" "System"
86-
add_to_dock "Firefox Developer Edition"
87-
add_to_dock "Google Chrome"
88-
add_to_dock "iPhone Mirroring" "System"
89-
add_to_dock "iTerm"
90-
add_to_dock "NordVPN"
91-
add_to_dock "Notes" "System"
92-
add_to_dock "Photo Booth" "System"
93-
add_to_dock "Reminders" "System"
94-
add_to_dock "Spotify"
95-
add_to_dock "Utilities/Activity Monitor" "System"
96-
add_to_dock "Visual Studio Code"
97-
add_to_dock "Weather" "System"
98-
# Required to make changes to the Dock and Finder take effect
99-
killall Dock
100-
killall Finder
91+
# Track if any apps were added (return 0 = added, 2 = already present)
92+
dock_changed=false
93+
add_to_dock "1Password" && dock_changed=true
94+
add_to_dock "Boop" && dock_changed=true
95+
add_to_dock "Calculator" "System" && dock_changed=true
96+
add_to_dock "Firefox Developer Edition" && dock_changed=true
97+
add_to_dock "Google Chrome" && dock_changed=true
98+
add_to_dock "iPhone Mirroring" "System" && dock_changed=true
99+
add_to_dock "iTerm" && dock_changed=true
100+
add_to_dock "NordVPN" && dock_changed=true
101+
add_to_dock "Notes" "System" && dock_changed=true
102+
add_to_dock "Photo Booth" "System" && dock_changed=true
103+
add_to_dock "Reminders" "System" && dock_changed=true
104+
add_to_dock "Spotify" && dock_changed=true
105+
add_to_dock "Utilities/Activity Monitor" "System" && dock_changed=true
106+
add_to_dock "Visual Studio Code" && dock_changed=true
107+
add_to_dock "Weather" "System" && dock_changed=true
108+
109+
# Only restart Dock if apps were added
110+
if [[ "$dock_changed" == "true" ]]; then
111+
killall Dock
112+
fi
113+
# Finder settings apply on next window open, no restart needed
101114

102115
# Add directories to Finder favorites
103116
# brew install --cask mysides

configure/node.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# shellcheck shell=bash
2+
# shellcheck disable=SC2154 # Variables like $yellow defined in lib/colors.sh
3+
# Sourced by run.sh after verifying nvm exists
4+
5+
# Setup Node Version Manager (NVM) for local JavaScript
6+
mkdir -p ~/.nvm
7+
export NVM_DIR="$HOME/.nvm"
8+
9+
NVM_SCRIPT="$(brew --prefix)/opt/nvm/nvm.sh"
10+
11+
# nvm.sh has unset variables, so temporarily disable strict mode
12+
# Use trap to ensure we always re-enable it
13+
_restore_strict_mode() { set -u; }
14+
trap _restore_strict_mode RETURN
15+
16+
set +u
17+
# shellcheck disable=SC1090 # Can't follow dynamic source path
18+
\. "$NVM_SCRIPT"
19+
20+
PREVIOUS_NODE_VERSION=$(nvm current)
21+
# shellcheck disable=SC1090 # nvm is a shell function, not a file
22+
if ! nvm install --lts; then
23+
echo -e "${yellow}Warning: Failed to install Node LTS${reset}" >&2
24+
return 0
25+
fi
26+
27+
NEW_NODE_VERSION=$(nvm current)
28+
if [ "$PREVIOUS_NODE_VERSION" != "$NEW_NODE_VERSION" ] && [ "$PREVIOUS_NODE_VERSION" != "none" ] && [ "$PREVIOUS_NODE_VERSION" != "system" ]; then
29+
echo -n "Uninstall old Node version ${PREVIOUS_NODE_VERSION}? [y/N]: "
30+
read -r -n 1 confirm </dev/tty
31+
echo ""
32+
if [[ "$confirm" =~ ^[Yy]$ ]]; then
33+
nvm uninstall "$PREVIOUS_NODE_VERSION" || echo -e "${yellow}Warning: Failed to uninstall previous Node version${reset}"
34+
else
35+
echo -e "${dim}Kept ${PREVIOUS_NODE_VERSION}${reset}"
36+
fi
37+
fi
38+
39+
npm install -g npm || echo -e "${yellow}Warning: Failed to upgrade npm${reset}"

configure/python.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# shellcheck shell=bash
2+
# shellcheck disable=SC2154 # Variables like $yellow defined in lib/colors.sh
3+
# Sourced by run.sh after checking `command -v uv`
4+
5+
# --default sets python/python3 commands (experimental, acknowledged via --preview-features)
6+
if ! uv python install --default --preview-features python-install-default; then
7+
echo -e "${yellow}Warning: Failed to install Python via uv${reset}" >&2
8+
fi
9+
10+
# Upgrade can fail gracefully - it's not a critical install
11+
uv python upgrade || echo -e "${yellow}Warning: Failed to upgrade Python${reset}"

configure/ruff.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# shellcheck shell=bash
2+
# shellcheck disable=SC2154 # Variables like $yellow defined in lib/colors.sh
3+
# Sourced by run.sh
4+
5+
export RUFF_CONFIG=~/projects/ruff-config
6+
if [ -d "$RUFF_CONFIG" ]; then
7+
git -C "$RUFF_CONFIG" pull || echo -e "${yellow}Warning: Failed to update Ruff config${reset}"
8+
echo -e "${dim}Pulled the latest commits for Ruff config${reset}"
9+
else
10+
if ! git clone https://github.com/nickineering/ruff-config "$RUFF_CONFIG"; then
11+
echo -e "${yellow}Warning: Failed to clone Ruff config repo, skipping Ruff setup${reset}" >&2
12+
return 0
13+
fi
14+
echo -e "${green}Cloned Ruff config repo${reset}"
15+
fi
16+
17+
# Per platform determined by Rust: https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html
18+
RUFF_DIR="$HOME/Library/Application Support/ruff/"
19+
RUFF_BASE="$RUFF_CONFIG/nickineering_ruff_config/nickineering-ruff-base.toml"
20+
21+
if [ ! -f "$RUFF_BASE" ]; then
22+
echo -e "${yellow}Warning: Ruff base config not found at $RUFF_BASE${reset}" >&2
23+
return 0
24+
fi
25+
26+
mkdir -p "$RUFF_DIR"
27+
if ! ln -sf "$RUFF_BASE" "$RUFF_DIR/ruff.toml"; then
28+
echo -e "${yellow}Warning: Failed to link Ruff config${reset}" >&2
29+
fi
30+
31+
# Install shell completions for ruff - requires code in .zshrc
32+
mkdir -p ~/.zfunc
33+
if ! ruff generate-shell-completion zsh >~/.zfunc/_ruff; then
34+
echo -e "${yellow}Warning: Failed to generate Ruff shell completions${reset}"
35+
fi

0 commit comments

Comments
 (0)