From 2456315640a9ec33f9f61a13c44a23a69d85d931 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 18 May 2026 20:51:25 +0200 Subject: Initial commit: is_required script and bash completion Bash script to find SBo packages that depend on a given package, with installed/uninstalled marking and color output. Includes bash-completion for package name suggestions. Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 27 +++++++++++ is_required | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ is_required.bash | 30 ++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 CLAUDE.md create mode 100755 is_required create mode 100644 is_required.bash diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..cb115fe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,27 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +Two files: +- `is_required` — bash script: given a package name, searches `/var/lib/sbopkg/SBo-git/**/*.info` for entries whose `REQUIRES=` field contains that package (whole-word match), then marks each result as installed or not by checking `/var/log/packages/` +- `is_required.bash` — bash-completion script for the above + +## Key design facts + +**`/var/log/packages` is a symlink** → `/var/lib/pkgtools/packages`. All `find` calls must use `-follow` or they return nothing. + +**Package name extraction** strips the last three dash-delimited fields from filenames in `/var/log/packages` (version, arch, build tag), e.g. `ffmpeg-7.1.4-x86_64-1` → `ffmpeg`. + +**SBo repo path** defaults to `/var/lib/sbopkg/SBo-git`; overridable via `$SBO_REPO`. Package log defaults to `/var/log/packages`; overridable via `$PKG_LOG`. + +**Color output** is gated on `[[ -t 1 ]]` — auto-disabled when piped/redirected. + +## Installing the completion + +```bash +sudo cp is_required.bash /etc/bash_completion.d/is_required +# or for the current user (requires ~/.bash_completion.d/ sourced in ~/.bashrc): +cp is_required.bash ~/.bash_completion.d/is_required +``` diff --git a/is_required b/is_required new file mode 100755 index 0000000..f64c95f --- /dev/null +++ b/is_required @@ -0,0 +1,139 @@ +#!/bin/bash +# Show which SBo packages require a given package, marking installed ones. + +SBO_REPO="${SBO_REPO:-/var/lib/sbopkg/SBo-git}" +PKG_LOG="${PKG_LOG:-/var/log/packages}" + +# Colors (disabled when stdout is not a terminal) +if [[ -t 1 ]]; then + C_RESET='\033[0m' + C_INSTALLED='\033[1;32m' # bold green — installed + C_MISSING='\033[0;33m' # yellow — not installed + C_TARGET='\033[1;36m' # bold cyan — the queried package + C_CATEGORY='\033[2;37m' # dim white — category label + C_HEADER='\033[1;37m' # bold white — section headers + C_ERROR='\033[1;31m' # bold red — errors + C_SEP='\033[0;90m' # dark grey — separators +else + C_RESET='' C_INSTALLED='' C_MISSING='' + C_TARGET='' C_CATEGORY='' C_HEADER='' C_ERROR='' C_SEP='' +fi + +usage() { + cat < + +Show SBo packages that list in their REQUIRES field. + +Options: + -i show only installed dependents + -u show only uninstalled dependents + -r recursive: also find what requires those packages (one level) + -h this help + +Symbols: + [I] installed on this system + [ ] not installed +EOF + exit 0 +} + +die() { printf "${C_ERROR}error:${C_RESET} %s\n" "$*" >&2; exit 1; } + +OPT_INSTALLED=0 +OPT_UNINSTALLED=0 +OPT_RECURSIVE=0 + +while getopts "iurh" opt; do + case "$opt" in + i) OPT_INSTALLED=1 ;; + u) OPT_UNINSTALLED=1 ;; + r) OPT_RECURSIVE=1 ;; + h) usage ;; + *) usage ;; + esac +done +shift $((OPTIND - 1)) + +[[ $# -lt 1 ]] && usage +TARGET="$1" + +[[ -d "$SBO_REPO" ]] || die "SBo repo not found at $SBO_REPO (set \$SBO_REPO)" +[[ -d "$PKG_LOG" ]] || die "package log not found at $PKG_LOG (set \$PKG_LOG)" + +# Build installed-package name set (strip version-arch-build suffix) +declare -A INSTALLED +while IFS= read -r entry; do + name="${entry##*/}" # basename + name="${name%-*-*-*}" # strip last three dash-fields + INSTALLED["$name"]=1 +done < <(find "$PKG_LOG" -maxdepth 1 -follow -type f) + +is_installed() { [[ -n "${INSTALLED[$1]+set}" ]]; } + +# Search .info files for packages that require TARGET (whole-word match) +find_dependents() { + local pkg="$1" + grep -rl "REQUIRES=.*\b${pkg}\b" "$SBO_REPO" --include="*.info" 2>/dev/null +} + +print_result() { + local info_file="$1" + local prgnam + prgnam=$(grep -m1 '^PRGNAM=' "$info_file" | cut -d'"' -f2) + local category + category=$(awk -F'/' '{print $(NF-2)}' <<< "$info_file") + + if is_installed "$prgnam"; then + [[ $OPT_UNINSTALLED -eq 1 ]] && return + printf "${C_INSTALLED}[I] %-40s${C_RESET} ${C_CATEGORY}(%s)${C_RESET}\n" \ + "$prgnam" "$category" + else + [[ $OPT_INSTALLED -eq 1 ]] && return + printf "${C_MISSING}[ ] %-40s${C_RESET} ${C_CATEGORY}(%s)${C_RESET}\n" \ + "$prgnam" "$category" + fi +} + +printf "${C_HEADER}Packages requiring ${C_TARGET}'%s'${C_RESET}${C_HEADER}:${C_RESET}\n" "$TARGET" +printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────" + +mapfile -t DIRECT < <(find_dependents "$TARGET") + +if [[ ${#DIRECT[@]} -eq 0 ]]; then + printf "${C_SEP}(none found)${C_RESET}\n" + exit 0 +fi + +for f in "${DIRECT[@]}"; do + print_result "$f" +done | sort + +if [[ $OPT_RECURSIVE -eq 1 ]]; then + declare -A SEEN + for f in "${DIRECT[@]}"; do + prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2) + SEEN["$prgnam"]=1 + done + + printf "\n${C_HEADER}Packages requiring those (one level up):${C_RESET}\n" + printf "${C_SEP}%s${C_RESET}\n" "────────────────────────────────────────────────────" + + declare -A SECOND_LEVEL + for f in "${DIRECT[@]}"; do + prgnam=$(grep -m1 '^PRGNAM=' "$f" | cut -d'"' -f2) + while IFS= read -r f2; do + p2=$(grep -m1 '^PRGNAM=' "$f2" | cut -d'"' -f2) + [[ -z "${SEEN[$p2]+set}" && -z "${SECOND_LEVEL[$p2]+set}" ]] || continue + SECOND_LEVEL["$p2"]="$f2" + done < <(find_dependents "$prgnam") + done + + if [[ ${#SECOND_LEVEL[@]} -eq 0 ]]; then + printf "${C_SEP}(none found)${C_RESET}\n" + else + for p2 in "${!SECOND_LEVEL[@]}"; do + print_result "${SECOND_LEVEL[$p2]}" + done | sort + fi +fi diff --git a/is_required.bash b/is_required.bash new file mode 100644 index 0000000..bf90930 --- /dev/null +++ b/is_required.bash @@ -0,0 +1,30 @@ +# is_required(1) completion -*- shell-script -*- + +_is_required() +{ + local cur prev words cword + _init_completion || return + + case $prev in + is_required) + ;; + -*) + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=( $(compgen -W '-i -u -r -h' -- "$cur") ) + return + fi + + # Complete package names from /var/log/packages (strip version-arch-build) + local pkglog="${PKG_LOG:-/var/log/packages}" + local packages + packages=$(find "$pkglog" -maxdepth 1 -follow -type f 2>/dev/null \ + | sed 's|.*/||; s/-[^-]*-[^-]*-[^-]*$//') + + COMPREPLY=( $(compgen -W "$packages" -- "$cur") ) +} && +complete -F _is_required is_required + +# ex: filetype=sh -- cgit v1.2.3