Skip to content

Commit c7e71fd

Browse files
committed
feat(check): add dev environment checks to optimize and check
Adds a new Dev Environment section to mo check and mo optimize that surfaces three informational checks: broken LaunchAgents, missing dev tools, and version mismatches (psql vs postgres, python3 vs pyenv). Security fixes applied before merge: - Removed nvm subshell check (sourcing arbitrary shell scripts via $NVM_DIR is unsafe; plain node --version is sufficient) - Replaced echo -e with printf for plist-derived label strings to prevent escape sequence injection from user-owned plist files Also removes now-duplicate section headers from bin/check.sh since each check_all_* function already prints its own header. Closes #666, co-authored by sebastianbreguel
1 parent c49c87c commit c7e71fd

File tree

4 files changed

+378
-5
lines changed

4 files changed

+378
-5
lines changed

bin/check.sh

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ source "$SCRIPT_DIR/lib/manage/update.sh"
1414
source "$SCRIPT_DIR/lib/manage/autofix.sh"
1515

1616
source "$SCRIPT_DIR/lib/check/all.sh"
17+
source "$SCRIPT_DIR/lib/check/dev_environment.sh"
1718

1819
cleanup_all() {
1920
stop_inline_spinner 2> /dev/null || true
@@ -42,6 +43,7 @@ main() {
4243
local health_file=$(mktemp_file)
4344
local security_file=$(mktemp_file)
4445
local config_file=$(mktemp_file)
46+
local dev_file=$(mktemp_file)
4547

4648
# Run all checks in parallel with spinner
4749
if [[ -t 1 ]]; then
@@ -58,6 +60,7 @@ main() {
5860
check_system_health > "$health_file" 2>&1 &
5961
check_all_security > "$security_file" 2>&1 &
6062
check_all_config > "$config_file" 2>&1 &
63+
check_all_dev_environment > "$dev_file" 2>&1 &
6164
wait
6265
}
6366

@@ -66,22 +69,21 @@ main() {
6669
printf '\n'
6770
fi
6871

69-
# Display results
70-
echo -e "${BLUE}${ICON_ARROW}${NC} System updates"
72+
# Display results (headers are printed by the check_all_* functions)
7173
cat "$updates_file"
7274

7375
printf '\n'
74-
echo -e "${BLUE}${ICON_ARROW}${NC} System health"
7576
cat "$health_file"
7677

7778
printf '\n'
78-
echo -e "${BLUE}${ICON_ARROW}${NC} Security posture"
7979
cat "$security_file"
8080

8181
printf '\n'
82-
echo -e "${BLUE}${ICON_ARROW}${NC} Configuration"
8382
cat "$config_file"
8483

84+
printf '\n'
85+
cat "$dev_file"
86+
8587
# Show suggestions
8688
show_suggestions
8789

bin/optimize.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ source "$SCRIPT_DIR/lib/optimize/maintenance.sh"
2121
source "$SCRIPT_DIR/lib/optimize/tasks.sh"
2222
source "$SCRIPT_DIR/lib/check/health_json.sh"
2323
source "$SCRIPT_DIR/lib/check/all.sh"
24+
source "$SCRIPT_DIR/lib/check/dev_environment.sh"
2425
source "$SCRIPT_DIR/lib/manage/whitelist.sh"
2526

2627
print_header() {
@@ -128,6 +129,8 @@ run_system_checks() {
128129
check_all_config
129130
echo ""
130131

132+
check_all_dev_environment
133+
131134
show_suggestions
132135

133136
if ask_for_updates; then

lib/check/dev_environment.sh

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/bin/bash
2+
# Dev Environment Checks Module
3+
# Surfaces developer-relevant system health information.
4+
5+
# ============================================================================
6+
# Helper Functions
7+
# ============================================================================
8+
9+
_extract_major_minor() {
10+
printf '%s' "$1" | sed -E 's/^[^0-9]*//' | grep -oE '^[0-9]+\.[0-9]+'
11+
}
12+
13+
# ============================================================================
14+
# Dev Environment Checks
15+
# ============================================================================
16+
17+
check_launch_agents() {
18+
# Check whitelist
19+
if command -v is_whitelisted > /dev/null && is_whitelisted "check_launch_agents"; then return; fi
20+
21+
local agents_dir="$HOME/Library/LaunchAgents"
22+
23+
if [[ ! -d "$agents_dir" ]]; then
24+
echo -e " ${GREEN}${NC} Launch Agents All healthy"
25+
return
26+
fi
27+
28+
local broken_count=0
29+
local -a broken_labels=()
30+
31+
for plist in "$agents_dir"/*.plist; do
32+
[[ -f "$plist" ]] || continue
33+
34+
local label
35+
label=$(basename "$plist" .plist)
36+
37+
local binary=""
38+
binary=$(/usr/libexec/PlistBuddy -c "Print :ProgramArguments:0" "$plist" 2> /dev/null || true)
39+
if [[ -z "$binary" ]]; then
40+
binary=$(/usr/libexec/PlistBuddy -c "Print :Program" "$plist" 2> /dev/null || true)
41+
fi
42+
43+
if [[ -n "$binary" && ! -e "$binary" ]]; then
44+
broken_count=$((broken_count + 1))
45+
broken_labels+=("$label")
46+
fi
47+
done
48+
49+
if [[ $broken_count -eq 0 ]]; then
50+
echo -e " ${GREEN}${NC} Launch Agents All healthy"
51+
else
52+
printf " ${GRAY}%s${NC} %-14s ${YELLOW}%s${NC}\n" "$ICON_WARNING" "Launch Agents" "${broken_count} broken"
53+
54+
local preview_limit=3
55+
((preview_limit > broken_count)) && preview_limit=$broken_count
56+
57+
local detail=""
58+
for ((i = 0; i < preview_limit; i++)); do
59+
if [[ $i -eq 0 ]]; then
60+
detail="${broken_labels[$i]}"
61+
else
62+
detail="${detail}, ${broken_labels[$i]}"
63+
fi
64+
done
65+
66+
if ((broken_count > preview_limit)); then
67+
local remaining=$((broken_count - preview_limit))
68+
detail="${detail} +${remaining}"
69+
fi
70+
71+
printf " ${GRAY}%s${NC}\n" "$detail"
72+
fi
73+
}
74+
75+
check_dev_tools() {
76+
# Check whitelist
77+
if command -v is_whitelisted > /dev/null && is_whitelisted "check_dev_tools"; then return; fi
78+
79+
local -a tools=(git node python3 brew docker go xcode-select)
80+
local -a missing=()
81+
82+
for tool in "${tools[@]}"; do
83+
if ! command -v "$tool" > /dev/null 2>&1; then
84+
missing+=("$tool")
85+
fi
86+
done
87+
88+
if [[ ${#missing[@]} -eq 0 ]]; then
89+
echo -e " ${GREEN}${NC} Dev Tools All present"
90+
else
91+
local missing_list
92+
missing_list=$(printf '%s, ' "${missing[@]}")
93+
missing_list="${missing_list%, }"
94+
printf " ${GRAY}%s${NC} %-14s ${YELLOW}%s${NC}\n" "$ICON_WARNING" "Dev Tools" "${#missing[@]} not found (${missing_list})"
95+
fi
96+
}
97+
98+
check_version_mismatches() {
99+
# Check whitelist
100+
if command -v is_whitelisted > /dev/null && is_whitelisted "check_version_mismatches"; then return; fi
101+
102+
local -a conflicts=()
103+
104+
# Check psql client vs postgres server
105+
if command -v psql > /dev/null 2>&1 && command -v postgres > /dev/null 2>&1; then
106+
local psql_ver postgres_ver
107+
psql_ver=$(_extract_major_minor "$(psql --version 2> /dev/null || true)")
108+
postgres_ver=$(_extract_major_minor "$(postgres --version 2> /dev/null || true)")
109+
if [[ -n "$psql_ver" && -n "$postgres_ver" && "$psql_ver" != "$postgres_ver" ]]; then
110+
conflicts+=("psql ${psql_ver} vs server ${postgres_ver}")
111+
fi
112+
fi
113+
114+
# Check python3 vs pyenv
115+
if command -v python3 > /dev/null 2>&1 && command -v pyenv > /dev/null 2>&1; then
116+
local python_ver pyenv_ver
117+
python_ver=$(_extract_major_minor "$(python3 --version 2> /dev/null || true)")
118+
pyenv_ver=$(pyenv version 2> /dev/null | awk '{print $1}' || true)
119+
if [[ -n "$pyenv_ver" && "$pyenv_ver" != "system" ]]; then
120+
pyenv_ver=$(_extract_major_minor "$pyenv_ver")
121+
if [[ -n "$python_ver" && -n "$pyenv_ver" && "$python_ver" != "$pyenv_ver" ]]; then
122+
conflicts+=("python3 ${python_ver} vs pyenv ${pyenv_ver}")
123+
fi
124+
fi
125+
fi
126+
127+
if [[ ${#conflicts[@]} -eq 0 ]]; then
128+
echo -e " ${GREEN}${NC} Versions No conflicts"
129+
else
130+
local description
131+
description=$(printf '%s; ' "${conflicts[@]}")
132+
description="${description%; }"
133+
printf " ${GRAY}%s${NC} %-14s ${YELLOW}%s${NC}\n" "$ICON_WARNING" "Versions" "$description"
134+
fi
135+
}
136+
137+
check_all_dev_environment() {
138+
echo -e "${BLUE}${ICON_ARROW}${NC} Dev Environment"
139+
check_launch_agents
140+
check_dev_tools
141+
check_version_mismatches
142+
}

0 commit comments

Comments
 (0)