Skip to content

Commit 9ca11bc

Browse files
committed
feat(deprecation): add tool deprecation and migration support
- Add qsv catalog entry (successor to xsv with 70+ commands) - Mark xsv as deprecated with superseded_by and deprecation_message - Add catalog helpers: catalog_is_deprecated(), catalog_get_superseded_by(), catalog_get_deprecation_message(), catalog_get_deprecated_tools() - Update guide.sh to skip deprecated tools in main loop - Add separate "Deprecated Tools" section with migration prompts - Migration options: M=migrate to replacement, K=keep, r=remove
1 parent a3c48a0 commit 9ca11bc

4 files changed

Lines changed: 265 additions & 1 deletion

File tree

catalog/qsv.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "qsv",
3+
"category": "general",
4+
"install_method": "github_release_binary",
5+
"description": "Ultra-fast CSV data-wrangling toolkit (70+ commands, successor to xsv)",
6+
"homepage": "https://github.com/dathere/qsv",
7+
"github_repo": "dathere/qsv",
8+
"binary_name": "qsv",
9+
"download_url_template": "https://github.com/dathere/qsv/releases/download/{version}/qsv-{version}-{arch}-unknown-linux-gnu.zip",
10+
"arch_map": {
11+
"x86_64": "x86_64",
12+
"aarch64": "aarch64"
13+
},
14+
"archive_format": "zip"
15+
}

catalog/xsv.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
"x86_64": "x86_64",
1212
"aarch64": "aarch64",
1313
"armv7l": "armv7"
14-
}
14+
},
15+
"deprecated": true,
16+
"superseded_by": "qsv",
17+
"deprecation_message": "xsv is unmaintained; qsv is a drop-in replacement with 70+ commands"
1518
}

scripts/guide.sh

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,137 @@ prompt_pin_version() {
478478
fi
479479
}
480480

481+
# Process a deprecated tool with migration options
482+
process_deprecated_tool() {
483+
local tool="$1"
484+
485+
# Determine catalog tool name
486+
local catalog_tool="$tool"
487+
if [[ "$tool" == *"@"* ]]; then
488+
catalog_tool="${tool%%@*}"
489+
fi
490+
491+
# Get tool data
492+
local installed="$(json_field "$tool" installed)"
493+
local method="$(json_field "$tool" installed_method)"
494+
local description="$(catalog_get_property "$catalog_tool" description)"
495+
local superseded_by="$(catalog_get_superseded_by "$catalog_tool")"
496+
local deprecation_msg="$(catalog_get_deprecation_message "$catalog_tool")"
497+
498+
# Get replacement tool info
499+
local replacement_desc=""
500+
if [ -n "$superseded_by" ] && catalog_has_tool "$superseded_by"; then
501+
replacement_desc="$(catalog_get_property "$superseded_by" description)"
502+
fi
503+
504+
printf "\n==> ⚠️ %s %s → DEPRECATED\n" "$tool" "$installed"
505+
[ -n "$description" ] && printf " %s\n" "$description"
506+
[ -n "$deprecation_msg" ] && printf " ⚠️ %s\n" "$deprecation_msg"
507+
if [ -n "$superseded_by" ]; then
508+
printf " Superseded by: %s\n" "$superseded_by"
509+
[ -n "$replacement_desc" ] && printf " └─ %s\n" "$replacement_desc"
510+
fi
511+
printf " installed: %s via %s\n" "$installed" "${method:-unknown}"
512+
513+
printf " Options:\n"
514+
if [ -n "$superseded_by" ]; then
515+
printf " M = Migrate to %s (recommended)\n" "$superseded_by"
516+
fi
517+
printf " K = Keep %s (no further updates)\n" "$tool"
518+
printf " r = Remove %s\n" "$tool"
519+
520+
local prompt_text
521+
if [ -n "$superseded_by" ]; then
522+
prompt_text="Action? [M/K/r] "
523+
else
524+
prompt_text="Action? [K/r] "
525+
fi
526+
527+
local ans=""
528+
if [ -t 0 ]; then
529+
read -r -p "$prompt_text" ans || true
530+
elif [ -r /dev/tty ]; then
531+
read -r -p "$prompt_text" ans </dev/tty || true
532+
fi
533+
534+
# Default to K (keep)
535+
[ -z "$ans" ] && ans="K"
536+
537+
case "$ans" in
538+
[Mm])
539+
if [ -n "$superseded_by" ]; then
540+
printf " Migrating to %s...\n" "$superseded_by"
541+
542+
# Install the replacement
543+
"$ROOT"/scripts/install_tool.sh "$superseded_by" || true
544+
545+
# Re-audit for replacement
546+
CLI_AUDIT_JSON=1 CLI_AUDIT_COLLECT=1 CLI_AUDIT_MERGE=1 "$CLI" audit.py "$superseded_by" >/dev/null 2>&1 || true
547+
548+
# Check if replacement was installed
549+
AUDIT_JSON="$(cd "$ROOT" && CLI_AUDIT_JSON=1 CLI_AUDIT_RENDER=1 "$CLI" audit.py || true)"
550+
local replacement_installed="$(json_field "$superseded_by" installed)"
551+
552+
if [ -n "$replacement_installed" ]; then
553+
printf " ✓ %s %s installed\n" "$superseded_by" "$replacement_installed"
554+
555+
# Ask about removing the old tool
556+
printf " Remove deprecated %s? [Y/n] " "$tool"
557+
local remove_ans=""
558+
if [ -t 0 ]; then
559+
read -r remove_ans || true
560+
elif [ -r /dev/tty ]; then
561+
read -r remove_ans </dev/tty || true
562+
fi
563+
564+
if [ -z "$remove_ans" ] || [[ "$remove_ans" =~ ^[Yy]$ ]]; then
565+
printf " Removing %s...\n" "$tool"
566+
"$ROOT"/scripts/install_tool.sh "$catalog_tool" uninstall || true
567+
CLI_AUDIT_JSON=1 CLI_AUDIT_COLLECT=1 CLI_AUDIT_MERGE=1 "$CLI" audit.py "$tool" >/dev/null 2>&1 || true
568+
AUDIT_JSON="$(cd "$ROOT" && CLI_AUDIT_JSON=1 CLI_AUDIT_RENDER=1 "$CLI" audit.py || true)"
569+
570+
local still_installed="$(json_field "$tool" installed)"
571+
if [ -z "$still_installed" ]; then
572+
printf " ✓ Migration complete: %s → %s\n" "$tool" "$superseded_by"
573+
else
574+
printf " ⚠️ %s may not have been fully removed\n" "$tool"
575+
fi
576+
else
577+
printf " Keeping %s alongside %s\n" "$tool" "$superseded_by"
578+
fi
579+
else
580+
printf " ⚠️ Failed to install %s\n" "$superseded_by"
581+
fi
582+
else
583+
printf " No replacement available, keeping %s\n" "$tool"
584+
fi
585+
;;
586+
[Kk])
587+
printf " Keeping %s (deprecated, no further updates)\n" "$tool"
588+
;;
589+
[Rr])
590+
printf " Removing %s...\n" "$tool"
591+
"$ROOT"/scripts/install_tool.sh "$catalog_tool" uninstall || true
592+
CLI_AUDIT_JSON=1 CLI_AUDIT_COLLECT=1 CLI_AUDIT_MERGE=1 "$CLI" audit.py "$tool" >/dev/null 2>&1 || true
593+
AUDIT_JSON="$(cd "$ROOT" && CLI_AUDIT_JSON=1 CLI_AUDIT_RENDER=1 "$CLI" audit.py || true)"
594+
595+
local still_there="$(json_field "$tool" installed)"
596+
if [ -z "$still_there" ]; then
597+
printf " ✓ %s has been removed\n" "$tool"
598+
else
599+
printf " ⚠️ %s may not have been fully removed\n" "$tool"
600+
fi
601+
;;
602+
*)
603+
printf " Keeping %s\n" "$tool"
604+
;;
605+
esac
606+
}
607+
481608
# Build tool list from audit output, grouped by category
482609
declare -A CATEGORY_TOOLS
610+
# Track deprecated tools separately (for migration prompts)
611+
DEPRECATED_TOOLS=""
483612
while read -r line; do
484613
[[ "$line" =~ ^state ]] && continue
485614
tool_name="$(echo "$line" | awk -F'|' '{gsub(/^ +| +$/,"",$2); print $2}')"
@@ -503,6 +632,17 @@ while read -r line; do
503632

504633
# Only process tools with catalog entries (check base tool for multi-version)
505634
if catalog_has_tool "$catalog_name"; then
635+
# Check if tool is deprecated
636+
if catalog_is_deprecated "$catalog_name"; then
637+
# Deprecated tool: only track if installed (for migration prompt)
638+
installed="$(json_field "$tool_name" installed)"
639+
if [ -n "$installed" ]; then
640+
DEPRECATED_TOOLS="$DEPRECATED_TOOLS $tool_name"
641+
fi
642+
# Skip deprecated tools in main loop (don't suggest installing them)
643+
continue
644+
fi
645+
506646
# Check if tool is pinned (use catalog name for pin check)
507647
# Skip pin checks if IGNORE_PINS=1
508648
if [ "$IGNORE_PINS" != "1" ]; then
@@ -663,5 +803,46 @@ for category in $(printf '%s\n' "${!CATEGORY_TOOLS[@]}" | while read c; do echo
663803
done
664804
done
665805

806+
# Process deprecated tools (if any installed)
807+
DEPRECATED_TOOLS="$(echo $DEPRECATED_TOOLS | xargs)" # trim whitespace
808+
if [ -n "$DEPRECATED_TOOLS" ]; then
809+
dep_count=$(echo $DEPRECATED_TOOLS | wc -w)
810+
811+
printf "\n"
812+
printf "================================================================================\n"
813+
printf "⚠️ Deprecated Tools (%d installed)\n" "$dep_count"
814+
printf "================================================================================\n"
815+
816+
# Category-level prompt
817+
if [ "${AUTO_YES_ALL:-}" != "1" ]; then
818+
printf " Tools: %s\n" "$(echo $DEPRECATED_TOOLS | tr ' ' ',')"
819+
printf " These tools are no longer maintained and have recommended replacements.\n"
820+
printf " Review deprecated tools? [Y/n] "
821+
822+
dep_ans=""
823+
if [ -t 0 ]; then
824+
read -r dep_ans || true
825+
elif [ -r /dev/tty ]; then
826+
read -r dep_ans </dev/tty || true
827+
fi
828+
829+
case "$dep_ans" in
830+
[Nn])
831+
printf " Skipping deprecated tools\n"
832+
;;
833+
*)
834+
for tool in $DEPRECATED_TOOLS; do
835+
process_deprecated_tool "$tool"
836+
done
837+
;;
838+
esac
839+
else
840+
# AUTO_YES_ALL mode - still process deprecated tools
841+
for tool in $DEPRECATED_TOOLS; do
842+
process_deprecated_tool "$tool"
843+
done
844+
fi
845+
fi
846+
666847
echo
667848
echo "All done. Re-run: make audit"

scripts/lib/catalog.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,71 @@ catalog_get_pinned_version() {
7272
fi
7373
}
7474

75+
# Check if tool is deprecated
76+
catalog_is_deprecated() {
77+
local tool="$1"
78+
local catalog_dir="$ROOT/catalog"
79+
80+
if ! command -v jq >/dev/null 2>&1; then
81+
return 1
82+
fi
83+
84+
local json="$catalog_dir/$tool.json"
85+
if [ -f "$json" ]; then
86+
local deprecated="$(jq -r '.deprecated // false' "$json")"
87+
[ "$deprecated" = "true" ]
88+
else
89+
return 1
90+
fi
91+
}
92+
93+
# Get the tool that supersedes a deprecated tool
94+
catalog_get_superseded_by() {
95+
local tool="$1"
96+
local catalog_dir="$ROOT/catalog"
97+
98+
if ! command -v jq >/dev/null 2>&1; then
99+
return
100+
fi
101+
102+
local json="$catalog_dir/$tool.json"
103+
if [ -f "$json" ]; then
104+
jq -r '.superseded_by // empty' "$json"
105+
fi
106+
}
107+
108+
# Get deprecation message for a tool
109+
catalog_get_deprecation_message() {
110+
local tool="$1"
111+
local catalog_dir="$ROOT/catalog"
112+
113+
if ! command -v jq >/dev/null 2>&1; then
114+
return
115+
fi
116+
117+
local json="$catalog_dir/$tool.json"
118+
if [ -f "$json" ]; then
119+
jq -r '.deprecation_message // empty' "$json"
120+
fi
121+
}
122+
123+
# Get all deprecated tools
124+
catalog_get_deprecated_tools() {
125+
local catalog_dir="$ROOT/catalog"
126+
127+
if ! command -v jq >/dev/null 2>&1; then
128+
echo "Error: jq required for catalog operations" >&2
129+
return 1
130+
fi
131+
132+
for json in "$catalog_dir"/*.json; do
133+
[ -f "$json" ] || continue
134+
if jq -e '.deprecated == true' "$json" >/dev/null 2>&1; then
135+
jq -r '.name' "$json"
136+
fi
137+
done
138+
}
139+
75140
# Get guide-specific metadata from catalog
76141
catalog_get_guide_property() {
77142
local tool="$1"

0 commit comments

Comments
 (0)