From 2c6e860df7e394fff4753796128fd50ce5c04ef3 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Thu, 9 Jul 2026 10:57:56 +0200 Subject: feat: --info/-i shows category/program header + README --- mkhint | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++-- tests/mkhint_test.sh | 42 +++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) diff --git a/mkhint b/mkhint index 8f964a8..bbbce55 100755 --- a/mkhint +++ b/mkhint @@ -796,6 +796,74 @@ EOF fi } +# --info/-i: show a package's category/program path (green on a TTY) then its +# README, paged only when it doesn't fit the terminal. +show_info() { + local pkg="$1" + if [[ -z "$pkg" ]]; then + echo "Error: --info requires a package name" >&2 + exit 1 + fi + + # Find the category dir(s): REPO_DIR/*/pkg/ + local -a matches=() + local d + for d in "${REPO_DIR%/}"/*/"$pkg"/; do + [[ -d "$d" ]] && matches+=("$d") + done + if [[ ${#matches[@]} -eq 0 ]]; then + echo "Error: package not found in ${REPO_DIR%/}: $pkg" >&2 + exit 2 + fi + if [[ ${#matches[@]} -gt 1 ]]; then + echo "Error: multiple matches for $pkg in ${REPO_DIR%/}:" >&2 + printf ' %s\n' "${matches[@]}" >&2 + exit 2 + fi + + local dir="${matches[0]%/}" + local category; category=$(basename "$(dirname "$dir")") + + # Green header on a TTY (same gate as --list). + local g_on="" g_off="" + if [[ -n "$MKHINT_FORCE_COLOR" || -t 1 ]]; then + if command -v tput &>/dev/null && tput setaf 2 &>/dev/null; then + g_on=$(tput setaf 2); g_off=$(tput sgr0) + else + g_on=$'\033[32m'; g_off=$'\033[0m' + fi + fi + local header="${g_on}${category}/${pkg}${g_off}" + + local readme="${dir}/README" + if [[ ! -f "$readme" ]]; then + printf '%s\n' "$header" + echo "(no README)" + exit 0 + fi + + # Non-TTY, or README fits: print inline. Else page with header pinned. + local rows; rows="${LINES:-}" + [[ -z "$rows" ]] && command -v tput &>/dev/null && rows=$(tput lines 2>/dev/null) + [[ -z "$rows" ]] && rows=24 + local lines; lines=$(wc -l < "$readme") + + if [[ ! -t 1 || "$lines" -le "$rows" ]]; then + printf '%s\n' "$header" + cat "$readme" + exit 0 + fi + + # Page: header as line 1 so it survives; pin it if pager is less. + local pager="${PAGER:-less}" + if [[ "$(basename "$pager")" == "less" ]]; then + { printf '%s\n' "$header"; cat "$readme"; } | less -R --header=1 + else + { printf '%s\n' "$header"; cat "$readme"; } | $pager + fi + exit 0 +} + # Emit the TOML section label for a package: bare if the name is a valid # bare key ([A-Za-z0-9_] only), otherwise double-quoted. nvchecker (and TOML) # require quoting for names containing '.', '-', etc. @@ -1468,8 +1536,8 @@ check_updates() { # Main function main() { local parsed - parsed=$(getopt -o vV:f:n:lcCdNhRF \ - --long version,set-version:,hintfile:,new:,list,clean,check,delete,no-dl,help,review,fix-current,force \ + parsed=$(getopt -o vV:f:n:i:lcCdNhRF \ + --long version,set-version:,hintfile:,new:,info:,list,clean,check,delete,no-dl,help,review,fix-current,force \ -n 'mkhint' -- "$@") || { show_help; exit 1; } eval set -- "$parsed" @@ -1635,6 +1703,9 @@ main() { fix-current) fix_current ;; + info) + show_info "$INFO_PKG" + ;; update) check_wget if [[ -z "$VERSION" ]]; then diff --git a/tests/mkhint_test.sh b/tests/mkhint_test.sh index 271e68e..2af6188 100755 --- a/tests/mkhint_test.sh +++ b/tests/mkhint_test.sh @@ -2108,6 +2108,48 @@ assert_not_contains "no alpha" "$ex_out" 'alpha' assert_not_contains "no gamma" "$ex_out" 'gamma' NVCHECKER_CONFIG="$MOCK_BASE/nvchecker.toml" +# README fixture for --info tests (curl dir already exists in setup) +cat > "$MOCK_REPO/network/curl/README" << 'EOF' +curl is a tool to transfer data from or to a server. +It supports many protocols. +EOF + +# ── T-INFO1: --info existing pkg with README → header + README (non-TTY) ─────── +echo "" +echo "T-INFO1: --info prints category/pkg header + README" +run_mkhint -i curl > "$MOCK_BASE/info1.out" 2>&1 +assert_contains "info header path" "$MOCK_BASE/info1.out" 'network/curl' +assert_contains "info README body" "$MOCK_BASE/info1.out" 'transfer data from or to a server' + +# ── T-INFO2: --info pkg without README → header + (no README) ────────────────── +echo "" +echo "T-INFO2: --info pkg with no README notes it" +# clion dir exists, no README written +run_mkhint -i clion > "$MOCK_BASE/info2.out" 2>&1 +info2_rc=$? +assert_contains "info2 header" "$MOCK_BASE/info2.out" 'development/clion' +assert_contains "info2 no README" "$MOCK_BASE/info2.out" 'no README' +assert_exit_code "info2 exit 0" 0 "$info2_rc" + +# ── T-INFO3: --info missing pkg → exit 2 ────────────────────────────────────── +echo "" +echo "T-INFO3: --info missing pkg exits 2" +set +e +run_mkhint -i doesnotexist > "$MOCK_BASE/info3.out" 2>&1 +info3_rc=$? +set -e +assert_exit_code "info3 exit 2" 2 "$info3_rc" +assert_contains "info3 error msg" "$MOCK_BASE/info3.out" 'not found' + +# ── T-INFO4: --info combined with -V → mutually exclusive, exit 1 ────────────── +echo "" +echo "T-INFO4: --info + -V mutually exclusive" +set +e +run_mkhint -i curl -V 9.9.9 > "$MOCK_BASE/info4.out" 2>&1 +info4_rc=$? +set -e +assert_exit_code "info4 exit 1" 1 "$info4_rc" + # ─── SUMMARY ────────────────────────────────────────────────────────────────── teardown -- cgit v1.2.3