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
24 changes: 12 additions & 12 deletions Makefile.d/user.mk
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,24 @@ audit: ## Render audit from snapshot (no network, <100ms)
echo " Consider running '\''make update'\'' for fresh version data." >&2; \
fi; \
fi; \
set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header' || true

audit-offline: ## Offline audit with hints (fast local scan)
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
audit-offline: ## Offline audit (fast local scan, no network)
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header' || true

outdated: ## Show only missing and outdated tools
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 CLI_AUDIT_FILTER_STATUS="NOT INSTALLED,OUTDATED" $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 CLI_AUDIT_FILTER_STATUS="NOT INSTALLED,OUTDATED" $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header' || true

audit-%: scripts-perms ## Audit single tool (e.g., make audit-ripgrep)
@bash -c 'set -o pipefail; CLI_AUDIT_RENDER=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py $* | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header' || true

audit-offline-%: scripts-perms ## Offline audit subset (e.g., make audit-offline-python-core)
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py $* | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header' || true
@bash -c 'set -o pipefail; CLI_AUDIT_OFFLINE=1 CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 CLI_AUDIT_COLOR=1 $(PYTHON) audit.py $* | \
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header' || true

SNAP_FILE?=$(shell python3 -c "import os;print(os.environ.get('CLI_AUDIT_SNAPSHOT_FILE','tools_snapshot.json'))")

Expand All @@ -73,8 +73,8 @@ audit-auto: ## Update snapshot if missing, then render
echo "# snapshot missing: $(SNAP_FILE); running update..."; \
CLI_AUDIT_COLLECT=1 CLI_AUDIT_DEBUG=1 CLI_AUDIT_PROGRESS=1 $(PYTHON) audit.py --update || true; \
fi; \
CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_HINTS=1 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,5 --header || true
CLI_AUDIT_RENDER=1 CLI_AUDIT_GROUP=0 CLI_AUDIT_LINKS=1 CLI_AUDIT_EMOJI=1 $(PYTHON) audit.py | \
$(PYTHON) smart_column.py -s "|" -t --right 3,4 --header || true

# ----------------------------------------------------------------------------
# UPGRADE VARIANTS
Expand Down
35 changes: 16 additions & 19 deletions audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from cli_audit.detection import audit_tool_installation, detect_multi_versions # noqa: E402
from cli_audit.snapshot import load_snapshot, write_snapshot, render_from_snapshot, get_snapshot_path # noqa: E402
from cli_audit.render import render_table, print_summary, status_icon # noqa: E402
from cli_audit.pins import lookup_pin, should_skip as _pin_should_skip # noqa: E402
from cli_audit.collectors import get_github_rate_limit, get_github_rate_limit_help, get_gitlab_rate_limit, is_wsl, collect_endoflife # noqa: E402
from cli_audit import collectors # noqa: E402
from cli_audit.logging_config import setup_logging # noqa: E402
Expand Down Expand Up @@ -58,7 +59,8 @@ def _sanitize(s: str) -> str:
# Configuration from environment
OFFLINE_MODE = os.environ.get("CLI_AUDIT_OFFLINE", "0") == "1"
MAX_WORKERS = int(os.environ.get("CLI_AUDIT_MAX_WORKERS", "16"))
SHOW_HINTS = os.environ.get("CLI_AUDIT_HINTS", "1") == "1"
# (CLI_AUDIT_HINTS is gone; canned hints added no information — the row
# state and tool name already tell the user what action is available.)
COLLECT_MODE = os.environ.get("CLI_AUDIT_COLLECT", "0") == "1"
RENDER_MODE = os.environ.get("CLI_AUDIT_RENDER", "0") == "1"
JSON_MODE = os.environ.get("CLI_AUDIT_JSON", "0") == "1"
Expand Down Expand Up @@ -201,13 +203,10 @@ def audit_multi_version_tool(
else:
classification_reason = "No installation detected"

# Hint for not installed or outdated
if status == "NOT INSTALLED":
hint = f"Install {tool_name} {cycle}: check your package manager or version manager"
elif status == "OUTDATED":
hint = f"Upgrade {tool_name} {cycle}: {installed} → {latest}"
else:
hint = ""
# Hint comes from catalog when provided (empty for generic runtimes —
# the tool name + state already tell the user what to do, and a
# canned "check your package manager" line adds no value).
hint = catalog_data.get("hint", "")

results.append({
"tool": versioned_name,
Expand Down Expand Up @@ -453,7 +452,7 @@ def cmd_audit(args: argparse.Namespace) -> int:
print("", file=sys.stderr)

# Render table
render_table(tools, show_hints=SHOW_HINTS)
render_table(tools)

# Print summary
print_summary(snapshot, tools)
Expand Down Expand Up @@ -616,11 +615,12 @@ def cmd_update(args: argparse.Namespace) -> int:
inst_display = _sanitize(inst) if inst else "n/a"
latest_display = _sanitize(latest) if latest else "n/a"

# Add pinned/skip markers (reuse catalog from outer scope)
# Add pinned/skip markers from user pins file
pin_val = lookup_pin(tool.name)
markers = []
if catalog.is_pinned(tool.name):
markers.append("PINNED")
if catalog.should_skip(tool.name, latest):
if pin_val:
markers.append("NEVER" if pin_val == "never" else f"PIN:{pin_val}")
if _pin_should_skip(tool.name, latest):
markers.append("SKIP")

marker_str = f" [{' '.join(markers)}]" if markers else ""
Expand Down Expand Up @@ -929,12 +929,9 @@ def cmd_update_local(args: argparse.Namespace) -> int:
"version_cycle": cycle,
"lifecycle_status": info.get("status", "unknown"),
})
if status_v == "OUTDATED":
entry["hint"] = f"Upgrade {tool.name} {cycle}: {installed_v} \u2192 {latest_v}"
elif status_v == "NOT INSTALLED":
entry["hint"] = f"Install {tool.name} {cycle}: check your package manager or version manager"
else:
entry["hint"] = ""
# Hint stays empty for generic multi-version runtimes;
# the tool name + state already tell the user what to do.
entry["hint"] = ""
tools_by_name[versioned] = entry

# Write merged snapshot
Expand Down
15 changes: 15 additions & 0 deletions cli_audit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@
validate_config,
)
from .package_managers import PackageManager, select_package_manager, get_available_package_managers # noqa: E402
from .pins import ( # noqa: E402
load_pins,
lookup_pin,
is_pinned,
is_never,
should_skip,
apply_pin_to_status,
)
from .install_plan import InstallPlan, InstallStep, generate_install_plan, dry_run_install # noqa: E402

# Installation
Expand Down Expand Up @@ -170,6 +178,13 @@
"PackageManager",
"select_package_manager",
"get_available_package_managers",
# Pins
"load_pins",
"lookup_pin",
"is_pinned",
"is_never",
"should_skip",
"apply_pin_to_status",
"InstallPlan",
"InstallStep",
"generate_install_plan",
Expand Down
53 changes: 0 additions & 53 deletions cli_audit/catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ class ToolCatalogEntry:
install_method: str = ""
package_name: str = ""
script: str = ""
pinned_version: str = ""
notes: str = ""
candidates: list[str] | None = None # NEW: Binary names to search for (defaults to [binary_name])
category: str = "" # NEW: Tool category (runtimes, search, editors, etc.)
Expand All @@ -52,7 +51,6 @@ def from_dict(cls, data: dict[str, Any]) -> "ToolCatalogEntry":
install_method=data.get("install_method", ""),
package_name=data.get("package_name", ""),
script=data.get("script", ""),
pinned_version=data.get("pinned_version", ""),
notes=data.get("notes", ""),
candidates=data.get("candidates"), # NEW
category=data.get("category", ""), # NEW
Expand Down Expand Up @@ -221,57 +219,6 @@ def get_raw_data(self, tool_name: str) -> dict[str, Any]:
"""
return self._raw_data.get(tool_name, {})

def is_pinned(self, tool_name: str) -> bool:
"""Check if a tool has a pinned version.

Args:
tool_name: Tool name

Returns:
True if tool has pinned version (not empty and not "never")
"""
entry = self.get(tool_name)
if not entry:
return False

pinned = entry.pinned_version
return bool(pinned and pinned != "never")

def get_pinned_version(self, tool_name: str) -> str:
"""Get pinned version for a tool.

Args:
tool_name: Tool name

Returns:
Pinned version string or empty string if not pinned
"""
entry = self.get(tool_name)
if not entry:
return ""

pinned = entry.pinned_version
if pinned and pinned != "never":
return pinned
return ""

def should_skip(self, tool_name: str, latest_version: str) -> bool:
"""Check if tool should be skipped (pinned and already at pinned version).

Args:
tool_name: Tool name
latest_version: Latest available version

Returns:
True if tool should be skipped
"""
pinned = self.get_pinned_version(tool_name)
if not pinned:
return False

# Simple version comparison - if pinned matches latest, skip
return pinned == latest_version

def all_tools(self) -> list[str]:
"""Get list of all tool names in catalog.

Expand Down
Loading
Loading