Skip to content

Commit 8c7e42c

Browse files
committed
feat(audit): add JSON output mode to --versions flag
Supports CLI_AUDIT_JSON=1 environment variable for machine-readable output of multi-version runtime status. Returns structured data with: - Runtime name, display name, product - Version cycles with install status, paths, methods - Boolean flags: is_installed, is_up_to_date, needs_upgrade
1 parent 0c484e6 commit 8c7e42c

1 file changed

Lines changed: 72 additions & 28 deletions

File tree

audit.py

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -811,22 +811,11 @@ def cmd_versions(args: argparse.Namespace) -> int:
811811
812812
Displays all runtimes that support multiple concurrent versions (PHP, Python,
813813
Node.js, Ruby, Go) and shows which versions are installed vs available.
814+
815+
Supports JSON output via CLI_AUDIT_JSON=1 environment variable.
814816
"""
815817
from cli_audit.catalog import ToolCatalog
816818

817-
# ANSI colors
818-
GREEN = "\033[32m"
819-
YELLOW = "\033[33m"
820-
RED = "\033[31m"
821-
BLUE = "\033[34m"
822-
BOLD = "\033[1m"
823-
RESET = "\033[0m"
824-
825-
print("=" * 80, file=sys.stderr)
826-
print("Runtime Versions", file=sys.stderr)
827-
print("=" * 80, file=sys.stderr)
828-
print("", file=sys.stderr)
829-
830819
catalog = ToolCatalog()
831820

832821
# Find all tools with multi_version enabled
@@ -838,7 +827,10 @@ def cmd_versions(args: argparse.Namespace) -> int:
838827
multi_version_tools.append((tool_name, data, mv_config))
839828

840829
if not multi_version_tools:
841-
print("No multi-version runtimes configured.", file=sys.stderr)
830+
if JSON_MODE:
831+
print(json.dumps({"runtimes": [], "total": 0}))
832+
else:
833+
print("No multi-version runtimes configured.", file=sys.stderr)
842834
return 0
843835

844836
# Filter to specific tools if requested
@@ -849,39 +841,91 @@ def cmd_versions(args: argparse.Namespace) -> int:
849841
if name in requested
850842
]
851843

852-
# Process each runtime
844+
# Collect all runtime data
845+
runtimes_data = []
853846
for tool_name, data, mv_config in multi_version_tools:
854847
product = mv_config.get("product", tool_name)
855848
max_versions = mv_config.get("max_versions", 4)
856849
display_name = data.get("description", tool_name.upper())
857850

858-
print(f"{BOLD}🔧 {display_name}{RESET}", file=sys.stderr)
859-
print("-" * 40, file=sys.stderr)
851+
runtime_info = {
852+
"name": tool_name,
853+
"display_name": display_name,
854+
"product": product,
855+
"versions": [],
856+
"error": None,
857+
}
860858

861859
# Fetch supported versions from endoflife.date
862860
try:
863861
supported = collect_endoflife(product, max_versions=max_versions)
864862
except Exception as e:
865-
print(f" {RED}✗ Failed to fetch version info: {e}{RESET}", file=sys.stderr)
866-
print("", file=sys.stderr)
863+
runtime_info["error"] = str(e)
864+
runtimes_data.append(runtime_info)
867865
continue
868866

869867
if not supported:
870-
print(f" {YELLOW}No supported versions found{RESET}", file=sys.stderr)
871-
print("", file=sys.stderr)
868+
runtime_info["error"] = "No supported versions found"
869+
runtimes_data.append(runtime_info)
872870
continue
873871

874872
# Detect installed versions
875873
detected = detect_multi_versions(tool_name, mv_config, supported)
876874

877-
# Display results
878875
for version_info in detected:
879-
cycle = version_info.get("cycle", "?")
880-
latest = version_info.get("latest_upstream", "")
881876
installed = version_info.get("installed")
882-
status = version_info.get("status", "unknown")
883-
path = version_info.get("path", "")
884-
method = version_info.get("install_method", "")
877+
latest = version_info.get("latest_upstream", "")
878+
runtime_info["versions"].append({
879+
"cycle": version_info.get("cycle", ""),
880+
"latest_upstream": latest,
881+
"installed": installed,
882+
"is_installed": bool(installed),
883+
"is_up_to_date": installed == latest if installed else False,
884+
"needs_upgrade": bool(installed and installed != latest),
885+
"path": version_info.get("path", ""),
886+
"install_method": version_info.get("install_method", ""),
887+
"status": version_info.get("status", "unknown"),
888+
})
889+
890+
runtimes_data.append(runtime_info)
891+
892+
# JSON output mode
893+
if JSON_MODE:
894+
output = {
895+
"runtimes": runtimes_data,
896+
"total": len(runtimes_data),
897+
}
898+
print(json.dumps(output, indent=2, ensure_ascii=False))
899+
return 0
900+
901+
# Table output mode
902+
GREEN = "\033[32m"
903+
YELLOW = "\033[33m"
904+
RED = "\033[31m"
905+
BLUE = "\033[34m"
906+
BOLD = "\033[1m"
907+
RESET = "\033[0m"
908+
909+
print("=" * 80, file=sys.stderr)
910+
print("Runtime Versions", file=sys.stderr)
911+
print("=" * 80, file=sys.stderr)
912+
print("", file=sys.stderr)
913+
914+
for runtime in runtimes_data:
915+
print(f"{BOLD}🔧 {runtime['display_name']}{RESET}", file=sys.stderr)
916+
print("-" * 40, file=sys.stderr)
917+
918+
if runtime["error"]:
919+
print(f" {RED}{runtime['error']}{RESET}", file=sys.stderr)
920+
print("", file=sys.stderr)
921+
continue
922+
923+
for v in runtime["versions"]:
924+
cycle = v["cycle"]
925+
latest = v["latest_upstream"]
926+
installed = v["installed"]
927+
status = v["status"]
928+
method = v["install_method"]
885929

886930
# Status indicator
887931
if status == "active":
@@ -908,7 +952,7 @@ def cmd_versions(args: argparse.Namespace) -> int:
908952

909953
# Summary
910954
print("=" * 80, file=sys.stderr)
911-
print(f"Total: {len(multi_version_tools)} runtimes configured", file=sys.stderr)
955+
print(f"Total: {len(runtimes_data)} runtimes configured", file=sys.stderr)
912956

913957
return 0
914958

0 commit comments

Comments
 (0)