Skip to content
Merged
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
3 changes: 2 additions & 1 deletion catalog/black.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"install_method": "uv_tool",
"description": "The uncompromising Python code formatter",
"homepage": "https://github.com/psf/black",
"package_name": "black"
"package_name": "black",
"version_command": "black --version 2>/dev/null | head -1"
}
3 changes: 2 additions & 1 deletion catalog/flake8.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"install_method": "uv_tool",
"description": "Python style guide enforcement tool combining PyFlakes, pycodestyle, and McCabe complexity checker",
"homepage": "https://github.com/PyCQA/flake8",
"package_name": "flake8"
"package_name": "flake8",
"version_command": "flake8 --version 2>/dev/null | head -1"
}
2 changes: 2 additions & 0 deletions catalog/gam.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"github_repo": "GAM-team/GAM",
"binary_name": "gam",
"version_flag": "version",
"version_command": "gam version 2>/dev/null | head -1 | grep -oE 'GAM [0-9]+\\.[0-9]+\\.[0-9]+' | awk '{print $2}'",
"version_regex": "([0-9]+\\.[0-9]+\\.[0-9]+)",
"package_name": "gam7",
"auto_update": true
}
1 change: 1 addition & 0 deletions catalog/gcloud.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"homepage": "https://cloud.google.com/sdk/gcloud",
"binary_name": "gcloud",
"skip_upstream": true,
"version_command": "gcloud version 2>/dev/null | head -1 | grep -oE '[0-9]+\\.[0-9]+\\.[0-9]+'",
"notes": "Installed via Google Cloud SDK installer; self-updates with 'gcloud components update'"
}
3 changes: 2 additions & 1 deletion catalog/isort.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"install_method": "uv_tool",
"description": "A Python utility to sort imports alphabetically and automatically separate them into sections",
"homepage": "https://github.com/PyCQA/isort",
"package_name": "isort"
"package_name": "isort",
"version_command": "isort --version-number 2>/dev/null"
}
11 changes: 3 additions & 8 deletions catalog/pip.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
{
"name": "pip",
"category": "python",
"install_method": "package_manager",
"install_method": "dedicated_script",
"description": "Python package installer",
"homepage": "https://pip.pypa.io/",
"package_name": "pip",
"binary_name": "pip",
"packages": {
"apt": "python3-pip",
"brew": "python3",
"dnf": "python3-pip",
"pacman": "python-pip"
},
"notes": "pip typically comes with Python 3. Use python3 -m pip if pip command is not available."
"script": "install_pip.sh",
"notes": "pip is bundled with Python 3. Use python3 -m pip if pip command is not available."
}
108 changes: 93 additions & 15 deletions scripts/guide.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
trap '' PIPE
# Graceful interrupt handling
INTERRUPTED=0
trap 'INTERRUPTED=1; echo; echo "⚠️ Interrupted. Partial summary:"; print_summary; exit 130' INT

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

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

# Summary counters
SUMMARY_UPDATED=0
SUMMARY_INSTALLED=0
SUMMARY_SKIPPED=0
SUMMARY_FAILED=0
SUMMARY_REMOVED=0

print_summary() {
local label="${1:-interrupted}"
echo "================================================================================"
echo "Summary${label:+ ($label)}"
echo "================================================================================"
printf " Installed: %d\n" "$SUMMARY_INSTALLED"
printf " Updated: %d\n" "$SUMMARY_UPDATED"
printf " Removed: %d\n" "$SUMMARY_REMOVED"
printf " Skipped: %d\n" "$SUMMARY_SKIPPED"
printf " Failed: %d\n" "$SUMMARY_FAILED"
echo
echo "Re-run: make audit"
}

# Category filter: CATEGORY=python,go or --category=python
CATEGORY_FILTER="${CATEGORY:-}"
for arg in "$@"; do
Expand Down Expand Up @@ -139,6 +165,17 @@ osc8() {
[ -n "$url" ] && printf '\e]8;;%s\e\\%s\e]8;;\e\\' "$url" "$text" || printf '%s' "$text"
}

# Print installed status line (reusable for auto-update and interactive prompts)
print_installed_status() {
local installed="$1"
local method="$2"
if [ -z "$installed" ]; then
printf " installed: not installed\n"
else
printf " installed: %s via %s\n" "$installed" "${method:-unknown}"
fi
}

# Check for multiple installations and print warning if found
# Args: catalog_tool_name
# Returns: 0 always (informational only)
Expand Down Expand Up @@ -241,6 +278,7 @@ process_tool() {
printf " installed: %s via %s\n" "$installed" "$method"
printf " target: %s (same)\n" "$(osc8 "$url" "$latest")"
check_multi_installs "$catalog_tool"
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
printf " up-to-date; skipping.\n"
return 0
fi
Expand All @@ -264,8 +302,14 @@ process_tool() {
# BUT: multi-version tools always prompt (more significant operation)
if [ "$auto_update" = "true" ] && [ -z "$is_multi_version" ]; then
printf "\n==> %s %s [auto-update]\n" "$icon" "$display"
printf " installed: %s via %s\n" "${installed:-<none>}" "${method:-unknown}"
printf " target: %s\n" "$(osc8 "$url" "${latest:-<unknown>}")"
print_installed_status "$installed" "$method"
# Show target; for self-managed tools (skip_upstream) show "self-managed" instead of <unknown>
local target_display="${latest:-<unknown>}"
local skip_upstream="$(catalog_get_property "$catalog_tool" skip_upstream)"
if [ "$target_display" = "<unknown>" ] && [ "$skip_upstream" = "true" ]; then
target_display="self-managed"
fi
printf " target: %s\n" "$(osc8 "$url" "$target_display")"
check_multi_installs "$catalog_tool"
printf " auto-updating...\n"

Expand All @@ -278,37 +322,51 @@ process_tool() {
fi

# Execute the install with version-specific environment variables
local auto_update_success=0
if [ "$catalog_tool" = "python" ] || [ -n "$is_multi_version" ] && [ "$catalog_tool" = "python" ]; then
UV_PYTHON_SPEC="$latest" "$ROOT"/scripts/$install_cmd || true
UV_PYTHON_SPEC="$latest" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
elif [ "$catalog_tool" = "ruby" ]; then
RUBY_VERSION="$latest" "$ROOT"/scripts/$install_cmd || true
RUBY_VERSION="$latest" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
elif [ "$catalog_tool" = "php" ] && [ -n "$version_cycle" ]; then
PHP_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
PHP_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
elif [ "$catalog_tool" = "node" ] && [ -n "$version_cycle" ]; then
NODE_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
NODE_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
elif [ "$catalog_tool" = "go" ] && [ -n "$version_cycle" ]; then
GO_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd || true
GO_VERSION="$version_cycle" "$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
else
"$ROOT"/scripts/$install_cmd || true
"$ROOT"/scripts/$install_cmd && auto_update_success=1 || true
fi

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

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

check_multi_installs "$catalog_tool"

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

# Build install command from catalog metadata (use catalog_tool for script name)
local install_cmd="install_tool.sh $catalog_tool"
Expand All @@ -335,7 +393,7 @@ process_tool() {
fi
printf " r = Remove/uninstall this tool\n"
if [ -n "$is_multi_version" ]; then
printf " P = Skip ALL %s cycles (never install any %s)\n" "$catalog_tool" "$catalog_tool"
printf " P = Skip ALL outdated %s cycles\n" "$catalog_tool"
fi
else
printf " y = Install now\n"
Expand All @@ -344,7 +402,7 @@ process_tool() {
printf " s = Skip only %s (ask again when newer patch available)\n" "$latest"
if [ -n "$is_multi_version" ]; then
printf " p = Never install %s (skip entire %s.x cycle)\n" "$display" "$version_cycle"
printf " P = Skip ALL %s cycles (never install any %s)\n" "$catalog_tool" "$catalog_tool"
printf " P = Skip ALL outdated %s cycles\n" "$catalog_tool"
else
printf " p = Never install (permanently skip this tool)\n"
fi
Expand Down Expand Up @@ -419,6 +477,7 @@ process_tool() {
if [ "$upgrade_success" = "0" ]; then
# Install script failed
printf "\n ⚠️ Upgrade failed (install script error)\n"
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
prompt_pin_version "$tool" "$installed"
elif [ -n "$binary_already_current" ]; then
# Binary hash matches target release - upgrade succeeded despite version string
Expand All @@ -435,6 +494,7 @@ process_tool() {
fi
else
# Upgrade succeeded - remove any existing pin to avoid stale pins
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
local existing_pin="$(pins_get "$tool")"
if [ -n "$existing_pin" ] && [ "$existing_pin" != "never" ]; then
"$ROOT"/scripts/unpin_version.sh "$tool" || true
Expand Down Expand Up @@ -478,18 +538,23 @@ process_tool() {
if [ "$upgrade_success_a" = "0" ]; then
printf "\n ⚠️ Upgrade failed (install script error)\n"
printf " Auto-update is still enabled - will try again next time.\n"
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
elif [ -n "$binary_already_current_a" ]; then
printf " ✓ Auto-update enabled. Binary already matches target release.\n"
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
elif [ "$new_installed_a" = "$installed" ] && [ "$new_installed_a" != "$latest" ]; then
# Version didn't change - but check for prefix match (e.g., 3.13 vs 3.13.11)
if [[ "$latest" == "$new_installed_a"* ]] || [[ "$new_installed_a" == "$latest"* ]]; then
printf " ✓ Auto-update enabled. This tool will update automatically in future.\n"
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
else
printf "\n ⚠️ Upgrade did not succeed (version unchanged)\n"
printf " Auto-update is still enabled - will try again next time.\n"
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
fi
else
printf " ✓ Auto-update enabled. This tool will update automatically in future.\n"
SUMMARY_UPDATED=$((SUMMARY_UPDATED + 1))
# Remove any existing pin
local existing_pin_a="$(pins_get "$tool")"
if [ -n "$existing_pin_a" ]; then
Expand All @@ -501,6 +566,7 @@ process_tool() {
# Skip this specific patch version only
printf " Skipping only %s (will prompt again when newer patch available)\n" "$latest"
"$ROOT"/scripts/pin_version.sh "$tool" "$latest" || true
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
;;
[p])
if [ -n "$installed" ]; then
Expand Down Expand Up @@ -550,8 +616,18 @@ process_tool() {
local still_installed="$(json_field "$tool" installed)"
if [ -z "$still_installed" ]; then
printf " ✓ %s has been removed\n" "$tool"
SUMMARY_REMOVED=$((SUMMARY_REMOVED + 1))
else
printf " ⚠️ %s may not have been fully removed (still detected: %s)\n" "$tool" "$still_installed"
# Check if remaining installation is a system/apt binary that we can't remove
local remaining_method="$(json_field "$tool" installed_method)"
if [ "$remaining_method" = "apt" ] || [ "$remaining_method" = "system" ]; then
printf " ✓ User-managed %s removed (system %s still present at %s — managed by OS)\n" \
"$tool" "$still_installed" "$remaining_method"
SUMMARY_REMOVED=$((SUMMARY_REMOVED + 1))
else
printf " ⚠️ %s may not have been fully removed (still detected: %s via %s)\n" "$tool" "$still_installed" "${remaining_method:-unknown}"
SUMMARY_FAILED=$((SUMMARY_FAILED + 1))
fi
fi
else
printf " Tool is not installed, nothing to remove\n"
Expand All @@ -570,6 +646,7 @@ process_tool() {
;;
*)
# User declined (N or empty)
SUMMARY_SKIPPED=$((SUMMARY_SKIPPED + 1))
;;
esac
}
Expand Down Expand Up @@ -863,7 +940,7 @@ for category in $(printf '%s\n' "${!CATEGORY_TOOLS[@]}" | while read c; do echo
# Category-level prompt (skip if auto-yes mode)
if [ "${AUTO_YES_ALL:-}" != "1" ]; then
printf " Tools: %s\n" "$(echo $tools | tr ' ' ', ' | sed 's/^, //')"
printf " Process this category? [Y/n/a=all/s=skip-all] "
printf " Process this category? [Y/n/a=all categories/s=skip-all] "

cat_ans=""
if [ -t 0 ]; then
Expand Down Expand Up @@ -935,5 +1012,6 @@ if [ -n "$DEPRECATED_TOOLS" ]; then
fi
fi

# Print final summary
echo
echo "All done. Re-run: make audit"
print_summary ""
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

print_summary defaults the label to "interrupted" when the first argument is empty (uses ${1:-interrupted}), so calling print_summary "" at normal completion prints "Summary (interrupted)". Pass a non-empty label for successful completion (e.g., "complete"), or change the defaulting logic so an empty string means no label.

Suggested change
print_summary ""
print_summary "complete"

Copilot uses AI. Check for mistakes.
4 changes: 4 additions & 0 deletions scripts/install_node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ get_specific_node_version() {
install_node() {
ensure_nvm
nvm install "$NODE_CHANNEL"
# Re-source nvm to ensure the new version is active in this shell
ensure_nvm_loaded

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

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