Skip to content

Commit 70b43eb

Browse files
authored
Merge pull request #44 from netresearch/fix/upgrade-guide-23-issues
fix(upgrade): resolve 23 issues in make upgrade workflow
2 parents ef4b273 + e550d5d commit 70b43eb

26 files changed

Lines changed: 531 additions & 63 deletions

catalog/black.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"install_method": "uv_tool",
55
"description": "The uncompromising Python code formatter",
66
"homepage": "https://github.com/psf/black",
7-
"package_name": "black"
7+
"package_name": "black",
8+
"version_command": "black --version 2>/dev/null | head -1"
89
}

catalog/flake8.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"install_method": "uv_tool",
55
"description": "Python style guide enforcement tool combining PyFlakes, pycodestyle, and McCabe complexity checker",
66
"homepage": "https://github.com/PyCQA/flake8",
7-
"package_name": "flake8"
7+
"package_name": "flake8",
8+
"version_command": "flake8 --version 2>/dev/null | head -1"
89
}

catalog/gam.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"github_repo": "GAM-team/GAM",
88
"binary_name": "gam",
99
"version_flag": "version",
10+
"version_command": "gam version 2>/dev/null | head -1 | grep -oE 'GAM [0-9]+\\.[0-9]+\\.[0-9]+' | awk '{print $2}'",
11+
"version_regex": "([0-9]+\\.[0-9]+\\.[0-9]+)",
1012
"package_name": "gam7",
1113
"auto_update": true
1214
}

catalog/gcloud.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
"homepage": "https://cloud.google.com/sdk/gcloud",
77
"binary_name": "gcloud",
88
"skip_upstream": true,
9+
"version_command": "gcloud version 2>/dev/null | head -1 | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+'",
910
"notes": "Installed via Google Cloud SDK installer; self-updates with 'gcloud components update'"
1011
}

catalog/isort.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"install_method": "uv_tool",
55
"description": "A Python utility to sort imports alphabetically and automatically separate them into sections",
66
"homepage": "https://github.com/PyCQA/isort",
7-
"package_name": "isort"
7+
"package_name": "isort",
8+
"version_command": "isort --version-number 2>/dev/null"
89
}

catalog/pip.json

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
{
22
"name": "pip",
33
"category": "python",
4-
"install_method": "package_manager",
4+
"install_method": "dedicated_script",
55
"description": "Python package installer",
66
"homepage": "https://pip.pypa.io/",
77
"package_name": "pip",
88
"binary_name": "pip",
9-
"packages": {
10-
"apt": "python3-pip",
11-
"brew": "python3",
12-
"dnf": "python3-pip",
13-
"pacman": "python-pip"
14-
},
15-
"notes": "pip typically comes with Python 3. Use python3 -m pip if pip command is not available."
9+
"script": "install_pip.sh",
10+
"notes": "pip is bundled with Python 3. Use python3 -m pip if pip command is not available."
1611
}

scripts/guide.sh

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,42 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33
trap '' PIPE
4+
# Graceful interrupt handling
5+
INTERRUPTED=0
6+
trap 'INTERRUPTED=1; echo; echo "⚠️ Interrupted. Partial summary:"; print_summary; exit 130' INT
47

58
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
69
ROOT="$(cd "$DIR/.." && pwd)"
710
VERBOSE="${VERBOSE:-0}"
11+
# Suppress Homebrew auto-update during upgrade runs to reduce noise
12+
export HOMEBREW_NO_AUTO_UPDATE=1
813
OFFLINE="${OFFLINE:-0}"
914
CLI="${PYTHON:-python3}"
1015

1116
# Ignore pins: IGNORE_PINS=1 to show all tools regardless of pin status
1217
IGNORE_PINS="${IGNORE_PINS:-0}"
1318

19+
# Summary counters
20+
SUMMARY_UPDATED=0
21+
SUMMARY_INSTALLED=0
22+
SUMMARY_SKIPPED=0
23+
SUMMARY_FAILED=0
24+
SUMMARY_REMOVED=0
25+
26+
print_summary() {
27+
local label="${1:-interrupted}"
28+
echo "================================================================================"
29+
echo "Summary${label:+ ($label)}"
30+
echo "================================================================================"
31+
printf " Installed: %d\n" "$SUMMARY_INSTALLED"
32+
printf " Updated: %d\n" "$SUMMARY_UPDATED"
33+
printf " Removed: %d\n" "$SUMMARY_REMOVED"
34+
printf " Skipped: %d\n" "$SUMMARY_SKIPPED"
35+
printf " Failed: %d\n" "$SUMMARY_FAILED"
36+
echo
37+
echo "Re-run: make audit"
38+
}
39+
1440
# Category filter: CATEGORY=python,go or --category=python
1541
CATEGORY_FILTER="${CATEGORY:-}"
1642
for arg in "$@"; do
@@ -139,6 +165,17 @@ osc8() {
139165
[ -n "$url" ] && printf '\e]8;;%s\e\\%s\e]8;;\e\\' "$url" "$text" || printf '%s' "$text"
140166
}
141167

168+
# Print installed status line (reusable for auto-update and interactive prompts)
169+
print_installed_status() {
170+
local installed="$1"
171+
local method="$2"
172+
if [ -z "$installed" ]; then
173+
printf " installed: not installed\n"
174+
else
175+
printf " installed: %s via %s\n" "$installed" "${method:-unknown}"
176+
fi
177+
}
178+
142179
# Check for multiple installations and print warning if found
143180
# Args: catalog_tool_name
144181
# Returns: 0 always (informational only)
@@ -241,6 +278,7 @@ process_tool() {
241278
printf " installed: %s via %s\n" "$installed" "$method"
242279
printf " target: %s (same)\n" "$(osc8 "$url" "$latest")"
243280
check_multi_installs "$catalog_tool"
281+
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
244282
printf " up-to-date; skipping.\n"
245283
return 0
246284
fi
@@ -264,8 +302,14 @@ process_tool() {
264302
# BUT: multi-version tools always prompt (more significant operation)
265303
if [ "$auto_update" = "true" ] && [ -z "$is_multi_version" ]; then
266304
printf "\n==> %s %s [auto-update]\n" "$icon" "$display"
267-
printf " installed: %s via %s\n" "${installed:-<none>}" "${method:-unknown}"
268-
printf " target: %s\n" "$(osc8 "$url" "${latest:-<unknown>}")"
305+
print_installed_status "$installed" "$method"
306+
# Show target; for self-managed tools (skip_upstream) show "self-managed" instead of <unknown>
307+
local target_display="${latest:-<unknown>}"
308+
local skip_upstream="$(catalog_get_property "$catalog_tool" skip_upstream)"
309+
if [ "$target_display" = "<unknown>" ] && [ "$skip_upstream" = "true" ]; then
310+
target_display="self-managed"
311+
fi
312+
printf " target: %s\n" "$(osc8 "$url" "$target_display")"
269313
check_multi_installs "$catalog_tool"
270314
printf " auto-updating...\n"
271315

@@ -278,37 +322,51 @@ process_tool() {
278322
fi
279323

280324
# Execute the install with version-specific environment variables
325+
local auto_update_success=0
281326
if [ "$catalog_tool" = "python" ] || [ -n "$is_multi_version" ] && [ "$catalog_tool" = "python" ]; then
282-
UV_PYTHON_SPEC="$latest" "$ROOT"/scripts/$install_cmd || true
327+
UV_PYTHON_SPEC="$latest" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
283328
elif [ "$catalog_tool" = "ruby" ]; then
284-
RUBY_VERSION="$latest" "$ROOT"/scripts/$install_cmd || true
329+
RUBY_VERSION="$latest" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
285330
elif [ "$catalog_tool" = "php" ] && [ -n "$version_cycle" ]; then
286-
PHP_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
331+
PHP_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
287332
elif [ "$catalog_tool" = "node" ] && [ -n "$version_cycle" ]; then
288-
NODE_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
333+
NODE_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
289334
elif [ "$catalog_tool" = "go" ] && [ -n "$version_cycle" ]; then
290-
GO_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
335+
GO_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
291336
else
292-
"$ROOT"/scripts/$install_cmd || true
337+
"$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
293338
fi
294339

295340
# Re-audit with fresh collection for this specific tool
296341
CLI_AUDIT_JSON=1 CLI_AUDIT_COLLECT=1 CLI_AUDIT_MERGE=1 "$CLI" audit.py "$tool" >/dev/null 2>&1 || true
297342
reload_audit_json
298343
# Clean up any already-current marker left by installer
299344
rm -f "/tmp/.cli-audit/${catalog_tool}.already-current"
345+
if [ "$auto_update_success" = "0" ]; then
346+
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
347+
elif [ -z "$installed" ]; then
348+
SUMMARY_INSTALLED=$((SUMMARY_INSTALLED + 1))
349+
else
350+
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
351+
fi
300352
return 0
301353
fi
302354

303355
# Prompt for installation/update
304356
printf "\n==> %s %s\n" "$icon" "$display"
305357
[ -n "$description" ] && printf " %s\n" "$description"
306358
[ -n "$homepage" ] && printf " Homepage: %s\n" "$(osc8 "$homepage" "$homepage")"
307-
printf " installed: %s via %s\n" "${installed:-<none>}" "${method:-unknown}"
359+
print_installed_status "$installed" "$method"
308360

309361
check_multi_installs "$catalog_tool"
310362

311-
printf " target: %s\n" "$(osc8 "$url" "${latest:-<unknown>}")"
363+
# Show target; for self-managed tools (skip_upstream) show "self-managed" instead of <unknown>
364+
local target_display_p="${latest:-<unknown>}"
365+
local skip_upstream_p="$(catalog_get_property "$catalog_tool" skip_upstream)"
366+
if [ "$target_display_p" = "<unknown>" ] && [ "$skip_upstream_p" = "true" ]; then
367+
target_display_p="self-managed"
368+
fi
369+
printf " target: %s\n" "$(osc8 "$url" "$target_display_p")"
312370

313371
# Build install command from catalog metadata (use catalog_tool for script name)
314372
local install_cmd="install_tool.sh $catalog_tool"
@@ -335,7 +393,7 @@ process_tool() {
335393
fi
336394
printf " r = Remove/uninstall this tool\n"
337395
if [ -n "$is_multi_version" ]; then
338-
printf " P = Skip ALL %s cycles (never install any %s)\n" "$catalog_tool" "$catalog_tool"
396+
printf " P = Skip ALL outdated %s cycles\n" "$catalog_tool"
339397
fi
340398
else
341399
printf " y = Install now\n"
@@ -344,7 +402,7 @@ process_tool() {
344402
printf " s = Skip only %s (ask again when newer patch available)\n" "$latest"
345403
if [ -n "$is_multi_version" ]; then
346404
printf " p = Never install %s (skip entire %s.x cycle)\n" "$display" "$version_cycle"
347-
printf " P = Skip ALL %s cycles (never install any %s)\n" "$catalog_tool" "$catalog_tool"
405+
printf " P = Skip ALL outdated %s cycles\n" "$catalog_tool"
348406
else
349407
printf " p = Never install (permanently skip this tool)\n"
350408
fi
@@ -419,6 +477,7 @@ process_tool() {
419477
if [ "$upgrade_success" = "0" ]; then
420478
# Install script failed
421479
printf "\n ⚠️ Upgrade failed (install script error)\n"
480+
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
422481
prompt_pin_version "$tool" "$installed"
423482
elif [ -n "$binary_already_current" ]; then
424483
# Binary hash matches target release - upgrade succeeded despite version string
@@ -435,6 +494,7 @@ process_tool() {
435494
fi
436495
else
437496
# Upgrade succeeded - remove any existing pin to avoid stale pins
497+
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
438498
local existing_pin="$(pins_get "$tool")"
439499
if [ -n "$existing_pin" ] && [ "$existing_pin" != "never" ]; then
440500
"$ROOT"/scripts/unpin_version.sh "$tool" || true
@@ -478,18 +538,23 @@ process_tool() {
478538
if [ "$upgrade_success_a" = "0" ]; then
479539
printf "\n ⚠️ Upgrade failed (install script error)\n"
480540
printf " Auto-update is still enabled - will try again next time.\n"
541+
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
481542
elif [ -n "$binary_already_current_a" ]; then
482543
printf " ✓ Auto-update enabled. Binary already matches target release.\n"
544+
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
483545
elif [ "$new_installed_a" = "$installed" ] && [ "$new_installed_a" != "$latest" ]; then
484546
# Version didn't change - but check for prefix match (e.g., 3.13 vs 3.13.11)
485547
if [[ "$latest" == "$new_installed_a"* ]] || [[ "$new_installed_a" == "$latest"* ]]; then
486548
printf " ✓ Auto-update enabled. This tool will update automatically in future.\n"
549+
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
487550
else
488551
printf "\n ⚠️ Upgrade did not succeed (version unchanged)\n"
489552
printf " Auto-update is still enabled - will try again next time.\n"
553+
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
490554
fi
491555
else
492556
printf " ✓ Auto-update enabled. This tool will update automatically in future.\n"
557+
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
493558
# Remove any existing pin
494559
local existing_pin_a="$(pins_get "$tool")"
495560
if [ -n "$existing_pin_a" ]; then
@@ -501,6 +566,7 @@ process_tool() {
501566
# Skip this specific patch version only
502567
printf " Skipping only %s (will prompt again when newer patch available)\n" "$latest"
503568
"$ROOT"/scripts/pin_version.sh "$tool" "$latest" || true
569+
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
504570
;;
505571
[p])
506572
if [ -n "$installed" ]; then
@@ -550,8 +616,18 @@ process_tool() {
550616
local still_installed="$(json_field "$tool" installed)"
551617
if [ -z "$still_installed" ]; then
552618
printf " ✓ %s has been removed\n" "$tool"
619+
SUMMARY_REMOVED=$((SUMMARY_REMOVED + 1))
553620
else
554-
printf " ⚠️ %s may not have been fully removed (still detected: %s)\n" "$tool" "$still_installed"
621+
# Check if remaining installation is a system/apt binary that we can't remove
622+
local remaining_method="$(json_field "$tool" installed_method)"
623+
if [ "$remaining_method" = "apt" ] || [ "$remaining_method" = "system" ]; then
624+
printf " ✓ User-managed %s removed (system %s still present at %s — managed by OS)\n" \
625+
"$tool" "$still_installed" "$remaining_method"
626+
SUMMARY_REMOVED=$((SUMMARY_REMOVED + 1))
627+
else
628+
printf " ⚠️ %s may not have been fully removed (still detected: %s via %s)\n" "$tool" "$still_installed" "${remaining_method:-unknown}"
629+
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
630+
fi
555631
fi
556632
else
557633
printf " Tool is not installed, nothing to remove\n"
@@ -570,6 +646,7 @@ process_tool() {
570646
;;
571647
*)
572648
# User declined (N or empty)
649+
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
573650
;;
574651
esac
575652
}
@@ -863,7 +940,7 @@ for category in $(printf '%s\n' "${!CATEGORY_TOOLS[@]}" | while read c; do echo
863940
# Category-level prompt (skip if auto-yes mode)
864941
if [ "${AUTO_YES_ALL:-}" != "1" ]; then
865942
printf " Tools: %s\n" "$(echo $tools | tr ' ' ', ' | sed 's/^, //')"
866-
printf " Process this category? [Y/n/a=all/s=skip-all] "
943+
printf " Process this category? [Y/n/a=all categories/s=skip-all] "
867944

868945
cat_ans=""
869946
if [ -t 0 ]; then
@@ -935,5 +1012,6 @@ if [ -n "$DEPRECATED_TOOLS" ]; then
9351012
fi
9361013
fi
9371014

1015+
# Print final summary
9381016
echo
939-
echo "All done. Re-run: make audit"
1017+
print_summary ""

scripts/install_node.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ get_specific_node_version() {
3838
install_node() {
3939
ensure_nvm
4040
nvm install "$NODE_CHANNEL"
41+
# Re-source nvm to ensure the new version is active in this shell
42+
ensure_nvm_loaded
4143

4244
# Only set default if this is NOT a multi-version install
4345
# (multi-version = specific major version like 24, 25)
@@ -66,6 +68,8 @@ install_node() {
6668
update_node() {
6769
ensure_nvm
6870
nvm install "$NODE_CHANNEL"
71+
# Re-source nvm to ensure the new version is active in this shell
72+
ensure_nvm_loaded
6973

7074
# Only set default and update global packages if NOT a multi-version install
7175
if [ -z "${NODE_VERSION:-}" ]; then

0 commit comments

Comments
 (0)