diff options
| author | emmett1 <me@emmett1.my> | 2026-06-20 16:59:18 +0800 |
|---|---|---|
| committer | emmett1 <me@emmett1.my> | 2026-06-20 16:59:18 +0800 |
| commit | 1e19e28d455b893171a5c91ead9aefbfe7c96b86 (patch) | |
| tree | 3b9f4c29c19ae7ed5b0f4c1c4fa61b53732fed78 | |
| parent | d215a2198e396698c72bfc2e4166d8f6b5269c2b (diff) | |
| download | autils-1e19e28d455b893171a5c91ead9aefbfe7c96b86.tar.gz autils-1e19e28d455b893171a5c91ead9aefbfe7c96b86.zip | |
re-added apkg-bin
| -rw-r--r-- | Makefile | 1 | ||||
| -rwxr-xr-x | apkg-bin | 591 | ||||
| -rw-r--r-- | man/apkg-bin.8 | 226 |
3 files changed, 818 insertions, 0 deletions
@@ -3,6 +3,7 @@ BINDIR ?= $(PREFIX)/bin MANDIR ?= $(PREFIX)/share/man/man8 SCRIPTS = apkg \ + apkg-bin \ apkg-chroot \ apkg-clean \ apkg-deps \ diff --git a/apkg-bin b/apkg-bin new file mode 100755 index 0000000..5d8de7f --- /dev/null +++ b/apkg-bin @@ -0,0 +1,591 @@ +#!/bin/sh +# +# APKG-BIN - Alice Binary Package Manager (C) 2023-2026 Emmett1 +# +# Fetches pre-built .spm packages from a binary repo, resolves dependencies, +# and installs via spm. Alternative to source-based 'apkg'. + +msg() { + printf "%s\n" "[apkg-bin] $*" >&2 +} + +die() { + [ "$1" ] && printf "%s\n" "error: $1" >&2 + exit 1 +} + +needroot() { + [ "$(id -u)" = 0 ] || die "This operation requires root access" +} + +prompt_user() { + [ "$APKG_NOPROMPT" ] && return + msg "Press ENTER to continue. Press Ctrl+C to abort." + read -r _null +} + +needbinsrc() { + [ "$APKGBIN_REPO" ] || die "APKGBIN_REPO is not set" +} + +needcachedb() { + [ -f "$APKGBIN_CACHE_DIR/APKGBINDB" ] || die "No cached APKGBINDB found. Run 'apkg-bin -S' first." + [ -r "$APKGBIN_CACHE_DIR/APKGBINDB" ] || die "No read permission on $APKGBIN_CACHE_DIR/APKGBINDB" +} + +pkg_is_installed() { + [ -s "$SPM_PKGDB/$1" ] +} + +pkg_installed_ver() { + head -n1 "$SPM_PKGDB/$1" 2>/dev/null +} + +# get field from a APKGBINDB line: bin_fetch_field <line> <fieldnum> +# 1=namever, 2=size, 3=sha3sum, 4=deps, 5=preinstall, 6=postinstall, 7=desc +bin_fetch_field() { + _line=$1 + _field=$2 + _rest=$_line + _i=1 + while [ "$_i" -lt "$_field" ]; do + _rest=${_rest#*|} + _i=$((_i + 1)) + done + case $_field in + 7) printf "%s\n" "${_rest#*|}" ;; + *) printf "%s\n" "${_rest%%|*}" ;; + esac +} + +# find a package name in APKGBINDB, output the full line +bin_find_pkg() { + _pkg=$1 + _line=$(grep "^$_pkg#" "$APKGBIN_CACHE_DIR/APKGBINDB" 2>/dev/null | head -n1) + [ "$_line" ] && printf "%s\n" "$_line" +} + +# get full name#version-release for a package name +bin_get_pkgid() { + _line=$(bin_find_pkg "$1") || return 1 + bin_fetch_field "$_line" 1 +} + +# get deps for a package (space-separated names) from APKGBINDB only +bin_get_deps() { + _line=$(bin_find_pkg "$1" 2>/dev/null) + if [ "$_line" ]; then + _deps=$(bin_fetch_field "$_line" 4) + if [ "$_deps" ]; then + printf "%s\n" "$_deps" | tr ',' ' ' + fi + fi +} + +# get description for a package +bin_get_desc() { + _line=$(bin_find_pkg "$1") || return 0 + bin_fetch_field "$_line" 7 +} + +# get preinstall script (base64) for a package +bin_get_preinstall() { + _line=$(bin_find_pkg "$1" 2>/dev/null) || return 0 + bin_fetch_field "$_line" 5 +} + +# get postinstall script (base64) for a package +bin_get_postinstall() { + _line=$(bin_find_pkg "$1" 2>/dev/null) || return 0 + bin_fetch_field "$_line" 6 +} + +# run a pre/post install script from base64 +bin_run_script() { + _name=$1 + _b64=$2 + _tag=$3 # "pre" or "post" + [ "$_b64" ] || return 0 + msg "Running ${_tag}install script for $_name ..." + printf "%s\n" "$_b64" | base64 -d | sh -e || { + msg "Warning: ${_tag}install script for $_name failed" + } +} + +solve_alias() { + [ "$APKG_ALIAS" ] || { + printf "%s\n" "$@" + return + } + for _a in "$@"; do + _d=$(printf "%s\n" $APKG_ALIAS | tr ' ' '\n' | grep "^$_a:" | head -n1 | awk -F : '{print $2}') + printf "%s\n" "${_d:-$_a}" + done +} + +bin_checkdep() { + # skip packages not in APKGBINDB + if ! bin_find_pkg "$1" >/dev/null 2>&1; then + [ "$_process" ] && msg "Warning: '$1' not found in APKGBINDB, skipping" + return + fi + _process="$_process $1" + for _d in $(solve_alias $(bin_get_deps "$1")); do + [ "$_d" = "$1" ] && continue + if [ "$_skip_installed" ]; then + pkg_is_installed "$_d" && continue + fi + # cycle detection + printf "%s\n" $_process | tr ' ' '\n' | grep -Fxq "$_d" && continue + # already resolved + printf "%s\n" $DEPS | tr ' ' '\n' | grep -Fxq "$_d" && continue + bin_checkdep "$_d" + done + printf "%s\n" $DEPS | tr ' ' '\n' | grep -Fxq "$1" || DEPS="$DEPS $1" +} + +bin_deplist() { + DEPS="" + _process="" + _skip_installed="" + for _p in "$@"; do + printf "%s\n" $DEPS | tr ' ' '\n' | grep -Fxq "$_p" || bin_checkdep "$_p" + done + printf "%s\n" $DEPS | tr ' ' '\n' | grep -v ^$ +} + +bin_sync() { + needbinsrc + mkdir -p "$APKGBIN_CACHE_DIR" 2>/dev/null + [ -w "$APKGBIN_CACHE_DIR" ] || die "No write permission on $APKGBIN_CACHE_DIR" + case $APKGBIN_REPO in + http://*|https://*|ftp://*) + msg "Syncing $APKGBIN_REPO ..." + curl -fL -o "$APKGBIN_CACHE_DIR/APKGBINDB.tmp" "$APKGBIN_REPO/APKGBINDB" || { + rm -f "$APKGBIN_CACHE_DIR/APKGBINDB.tmp" + die "Failed to fetch APKGBINDB from $APKGBIN_REPO" + } + mv "$APKGBIN_CACHE_DIR/APKGBINDB.tmp" "$APKGBIN_CACHE_DIR/APKGBINDB" + ;; + *) + [ -f "$APKGBIN_REPO/APKGBINDB" ] || die "No APKGBINDB found at $APKGBIN_REPO" + msg "Syncing $APKGBIN_REPO (local) ..." + cp "$APKGBIN_REPO/APKGBINDB" "$APKGBIN_CACHE_DIR/APKGBINDB" + ;; + esac + msg "Sync complete." +} + +bin_generate() { + [ "$APKG_REPO" ] || die "APKG_REPO is not set (needed to read abuild metadata)" + [ -d "$APKG_PACKAGE_DIR" ] || die "APKG_PACKAGE_DIR ($APKG_PACKAGE_DIR) not found" + [ -r "$APKG_PACKAGE_DIR" ] || die "No read permission on $APKG_PACKAGE_DIR" + [ -w "$APKG_PACKAGE_DIR" ] || die "No write permission on $APKG_PACKAGE_DIR" + mkdir -p "$APKGBIN_CACHE_DIR" 2>/dev/null + [ -w "$APKGBIN_CACHE_DIR" ] || die "No write permission on $APKGBIN_CACHE_DIR" + + _output="${APKG_PACKAGE_DIR}/APKGBINDB" + > "$_output" + + _count=0 + for _spm in "$APKG_PACKAGE_DIR"/*.spm; do + [ -f "$_spm" ] || continue + _filename=${_spm##*/} + _namever=${_filename%.spm} + _name=${_namever%%#*} + _size=$(stat -c%s "$_spm" 2>/dev/null || wc -c < "$_spm") + _sha3=$(sha3sum "$_spm" 2>/dev/null | awk '{print $1}') + + _deps="" + for _r in $APKG_REPO; do + if [ -f "$_r/$_name/depends" ]; then + for _d in $(grep -Ev '^(#|$)' "$_r/$_name/depends" | awk '{print $1}'); do + for _rr in $APKG_REPO; do + [ -d "$_rr/$_d" ] && { _deps="$_deps$_d,"; break; } + done + done + _deps=${_deps%,} + break + fi + done + + _desc="" + _pre="" + _post="" + for _r in $APKG_REPO; do + [ -f "$_r/$_name/info" ] && [ ! "$_desc" ] && \ + _desc=$(grep '^description:' "$_r/$_name/info" | sed 's/^description:[[:space:]]*//') + [ -f "$_r/$_name/preinstall" ] && [ ! "$_pre" ] && \ + _pre=$(base64 "$_r/$_name/preinstall" 2>/dev/null | tr -d '\n') + [ -f "$_r/$_name/postinstall" ] && [ ! "$_post" ] && \ + _post=$(base64 "$_r/$_name/postinstall" 2>/dev/null | tr -d '\n') + done + + printf "%s|%s|%s|%s|%s|%s|%s\n" "$_namever" "$_size" "$_sha3" "$_deps" "$_pre" "$_post" "$_desc" >> "$_output" + _count=$((_count + 1)) + done + + # also cache locally so -l/-s/-i work immediately + mkdir -p "$APKGBIN_CACHE_DIR" + [ "$_output" = "$APKGBIN_CACHE_DIR/APKGBINDB" ] || cp "$_output" "$APKGBIN_CACHE_DIR/APKGBINDB" + msg "Wrote APKGBINDB with $_count entries to $_output" +} + +bin_download_pkg() { + _pkgid=$1 + _name=${_pkgid%%#*} + _pkgfile="$_pkgid.spm" + + mkdir -p "$APKGBIN_CACHE_DIR" 2>/dev/null + [ -w "$APKGBIN_CACHE_DIR" ] || die "No write permission on $APKGBIN_CACHE_DIR" + + case $APKGBIN_REPO in + http://*|https://*|ftp://*) + _urlfile=$(printf "%s\n" "$_pkgfile" | sed 's/#/%23/g') + _target="$APKGBIN_CACHE_DIR/$_pkgfile" + if [ "$force" ] || [ ! -f "$_target" ]; then + msg "Downloading $APKGBIN_REPO/$_pkgfile" + curl -fL -o "$_target.tmp" "$APKGBIN_REPO/$_urlfile" || { + rm -f "$_target.tmp" + die "Failed to download $_pkgfile from $APKGBIN_REPO" + } + mv "$_target.tmp" "$_target" + fi + ;; + *) + _target="$APKGBIN_REPO/$_pkgfile" + [ -f "$_target" ] || die "Package file not found: $_target" + ;; + esac + + # verify sha3sum if available in APKGBINDB + _line=$(bin_find_pkg "$_name") + _expected=$(bin_fetch_field "$_line" 3) + if [ "$_expected" ]; then + _actual=$(sha3sum "$_target" | awk '{print $1}') + [ "$_actual" = "$_expected" ] || die "sha3sum mismatch for $_pkgfile" + fi + + printf "%s\n" "$_target" +} + +bin_install() { + _resolve=${1:-1}; shift # 1=resolve deps, 0=no deps + needroot + needcachedb + [ "$1" ] || die "No packages specified" + + _todo="" + for _p in "$@"; do + if pkg_is_installed "$_p"; then + msg "Package '$_p' is already installed. Skipping." + else + _todo="$_todo $_p" + fi + done + [ "$_todo" ] || { msg "Nothing to install."; exit 0; } + + if [ "$_resolve" = 1 ]; then + msg "Solving dependencies..." + _deps=$(_skip_installed=1 bin_deplist $_todo) + else + _deps=$_todo + fi + [ "$_deps" ] || { msg "Nothing to install."; exit 0; } + + _total=$(printf "%s\n" "$_deps" | wc -l) + msg "Installing $_total package(s): $(printf "%s\n" "$_deps" | tr '\n' ' ')" + + for _d in $_deps; do + _pkgid=$(bin_get_pkgid "$_d") || continue + msg " $_pkgid" + done + + prompt_user + + for _d in $_deps; do + _pkgid=$(bin_get_pkgid "$_d") || continue + _pkgpath=$(bin_download_pkg "$_pkgid") || continue + bin_run_script "$_d" "$(bin_get_preinstall "$_d")" pre + msg "Installing $_pkgid ..." + SPM_ROOT=${APKG_ROOT%/} spm -i "$_pkgpath" || die "Failed to install $_pkgid" + bin_run_script "$_d" "$(bin_get_postinstall "$_d")" post + done +} + +bin_upgrade() { + needroot + needcachedb + [ "$1" ] || die "No packages specified" + + for _p in "$@"; do + if ! pkg_is_installed "$_p"; then + msg "Package '$_p' not installed. Skipping." + continue + fi + _pkgid=$(bin_get_pkgid "$_p") || { + msg "Package '$_p' not found in APKGBINDB. Skipping." + continue + } + _pkgpath=$(bin_download_pkg "$_pkgid") || continue + _installed=$(pkg_installed_ver "$_p") + _available=${_pkgid#*#} + [ "$_installed" = "$_available" ] && msg "Package '$_p' is up to date, reinstalling." + bin_run_script "$_p" "$(bin_get_preinstall "$_p")" pre + msg "Upgrading $_pkgid ..." + SPM_ROOT=${APKG_ROOT%/} spm -u "$_pkgpath" || die "Failed to upgrade $_pkgid" + bin_run_script "$_p" "$(bin_get_postinstall "$_p")" post + done +} + +bin_sysupgrade() { + needroot + needcachedb + + msg "Checking for outdated packages..." + _outdated=$(_apply_mask=1 bin_list_outdated | awk '{print $1}') + [ "$_outdated" ] || { msg "No outdated packages."; exit 0; } + + msg "Resolving dependencies..." + _skip_installed="" + DEPS="" + for _name in $_outdated; do + printf "%s\n" $DEPS | tr ' ' '\n' | grep -Fxq "$_name" || { + _process="" + bin_checkdep "$_name" + } + done + _all=$(printf "%s\n" $DEPS | tr ' ' '\n' | grep -v ^$ | sort -u) + + _newpkgs="" + _upgradepkgs="" + _seen_new="" + _seen_up="" + for _name in $_all; do + if pkg_is_installed "$_name"; then + printf "%s\n" $_seen_up | tr ' ' '\n' | grep -Fxq "$_name" && continue + _seen_up="$_seen_up $_name" + _pkgid=$(bin_get_pkgid "$_name") || continue + _installed=$(pkg_installed_ver "$_name") + _available=${_pkgid#*#} + [ "$_installed" = "$_available" ] && continue + _upgradepkgs="$_upgradepkgs $_name" + else + printf "%s\n" $_seen_new | tr ' ' '\n' | grep -Fxq "$_name" && continue + _seen_new="$_seen_new $_name" + _newpkgs="$_newpkgs $_name" + fi + done + + [ "$_newpkgs" ] && msg "Installing new package(s): $_newpkgs" + [ "$_upgradepkgs" ] || { msg "All packages up to date."; exit 0; } + _ncount=$(printf "%s\n" $_upgradepkgs | wc -l) + msg "Upgrading $_ncount package(s): $(printf "%s\n" $_upgradepkgs | tr '\n' ' ')" + prompt_user + + if [ "$_newpkgs" ]; then + for _name in $_newpkgs; do + _pkgid=$(bin_get_pkgid "$_name") || continue + _pkgpath=$(bin_download_pkg "$_pkgid") || continue + bin_run_script "$_name" "$(bin_get_preinstall "$_name")" pre + msg "Installing $_pkgid ..." + SPM_ROOT=${APKG_ROOT%/} spm -i "$_pkgpath" || die "Failed to install $_pkgid" + bin_run_script "$_name" "$(bin_get_postinstall "$_name")" post + done + fi + for _name in $_upgradepkgs; do + _pkgid=$(bin_get_pkgid "$_name") || continue + _pkgpath=$(bin_download_pkg "$_pkgid") || continue + bin_run_script "$_name" "$(bin_get_preinstall "$_name")" pre + msg "Upgrading $_pkgid ..." + SPM_ROOT=${APKG_ROOT%/} spm -u "$_pkgpath" || die "Failed to upgrade $_pkgid" + bin_run_script "$_name" "$(bin_get_postinstall "$_name")" post + done + + msg "System upgrade complete." +} + +bin_list_outdated() { + needcachedb + [ -d "$SPM_PKGDB" ] || return 0 + for _db in "$SPM_PKGDB"/*; do + [ -f "$_db" ] || continue + _name=${_db##*/} + if [ "$APKG_MASK" ] && [ "$_apply_mask" ]; then + printf "%s\n" "$APKG_MASK" | tr ' ' '\n' | grep -Fxq "$_name" && continue + fi + _installed=$(head -n1 "$_db") + _line=$(bin_find_pkg "$_name") || continue + _namever=$(bin_fetch_field "$_line" 1) + _available=${_namever#*#} + [ "$_installed" = "$_available" ] || printf "%s\n" "$_name $_installed -> $_available" + done +} + +bin_search() { + needcachedb + _pattern=$1 + while IFS= read -r _line; do + [ "$_line" ] || continue + _namever=$(bin_fetch_field "$_line" 1) + _name=${_namever%%#*} + _display=$(printf "%s\n" "$_namever" | tr '#' ' ') + if [ "$_pattern" ]; then + printf "%s\n" "$_name" | grep -q "$_pattern" || continue + fi + if [ "$verbose" ]; then + _desc=$(bin_fetch_field "$_line" 7) + printf "%s %s\n" "$_display" "$_desc" + else + printf "%s\n" "$_name" + fi + done < "$APKGBIN_CACHE_DIR/APKGBINDB" +} + +bin_list_installed() { + [ -d "$SPM_PKGDB" ] || return 0 + for _db in "$SPM_PKGDB"/*; do + [ -f "$_db" ] || continue + _name=${_db##*/} + if [ "$verbose" ]; then + _ver=$(head -n1 "$_db") + printf "%s %s\n" "$_name" "$_ver" + else + printf "%s\n" "$_name" + fi + done +} + +bin_clean() { + needcachedb + _dldir="$APKGBIN_CACHE_DIR" + [ -d "$_dldir" ] || { msg "No downloaded packages to clean."; exit 0; } + _count=0 + for _spm in "$_dldir"/*.spm; do + [ -f "$_spm" ] || continue + _filename=${_spm##*/} + _name=${_filename%%#*} + _line=$(bin_find_pkg "$_name" 2>/dev/null) + [ "$_line" ] || continue + _expected=$(bin_fetch_field "$_line" 3) + [ "$_expected" ] || continue + _actual=$(sha3sum "$_spm" | awk '{print $1}') + if [ "$_actual" != "$_expected" ]; then + msg "Removing $_filename (sha3sum mismatch)" + rm -f "$_spm" + _count=$((_count + 1)) + fi + done + msg "Removed $_count package(s) with sha3sum mismatch." +} + +bin_show_deps() { + needcachedb + [ "$1" ] || die "No package specified" + _deps=$(bin_get_deps "$1") + [ "$_deps" ] && printf "%s\n" $_deps +} + +bin_show_deptree() { + needcachedb + [ "$1" ] || die "No package specified" + [ "$(bin_find_pkg "$1")" ] || exit 0 + _deps=$(bin_deplist "$1") + printf "%s\n" "$_deps" | grep -Fxq "$1" || _deps="$_deps +$1" + printf "%s\n" "$_deps" | grep -v ^$ +} + +bin_help() { + cat << 'EOF' +usage: apkg-bin <option> [arg(s)] + +options: + -g generate APKGBINDB from APKG_PACKAGE_DIR and APKG_REPO + -S sync APKGBINDB from APKGBIN_REPO + -s [pattern] search available binary packages (optional filter) + -i <pkg(s)> install package(s) without dependency resolution + -I <pkg(s)> install package(s) with dependency resolution + -u <pkg(s)> upgrade package(s) + -U full system upgrade (all outdated packages) + -o <pkg(s)> download package(s) only (no install) + -c clean downloaded packages with sha3sum mismatch + -d <pkg> show direct dependencies for a package + -D <pkg(s)> show full dependency tree for package(s) + -l list outdated packages + -a list all installed packages + -f force re-download of cached packages + -v verbose output + -h print this help message + +environment variables: + APKGBIN_REPO binary repo URL or path + APKGBIN_CACHE_DIR cache directory (required) + APKG_REPO source repo directories (needed for -g) + APKG_PACKAGE_DIR package directory (default: $PWD) + APKG_ROOT alternative install root + APKG_MASK packages to skip during -U (system upgrade) + APKG_ALIAS dependency substitution (real:alias pairs) + APKG_NOPROMPT skip confirmation prompts +EOF +} + +_filter_flags() { + _args="" + for _a in "$@"; do + case $_a in + -f) force=1 ;; + -v) verbose=1 ;; + *) _args="$_args $_a" ;; + esac + done +} + +parseopts() { + while [ "$1" ]; do + case $1 in + -g) bin_generate; exit 0;; + -S) bin_sync; exit 0;; + -s) _do_search=1; if [ "$2" ] && [ "${2#-}" = "$2" ]; then search_pattern="$2"; shift; fi;; + -i) shift; _filter_flags "$@"; bin_install 0 $_args; exit 0;; + -I) shift; _filter_flags "$@"; bin_install 1 $_args; exit 0;; + -u) shift; _filter_flags "$@"; bin_upgrade $_args; exit 0;; + -U) bin_sysupgrade; exit 0;; + -o) shift; _filter_flags "$@"; needcachedb; for _p in $_args; do _pkgid=$(bin_get_pkgid "$_p") || die "Package '$_p' not found in APKGBINDB"; bin_download_pkg "$_pkgid"; done; exit 0;; + -c) bin_clean; exit 0;; + -d) bin_show_deps "$2"; exit 0;; + -D) shift; _filter_flags "$@"; needcachedb; _valid=""; for _p in $_args; do [ "$(bin_find_pkg "$_p")" ] && _valid="$_valid $_p"; done; [ "$_valid" ] || exit 0; _out=$(bin_deplist $_valid); for _p in $_valid; do printf "%s\n" "$_out" | grep -Fxq "$_p" || _out="$_out +$_p"; done; printf "%s\n" "$_out" | grep -v ^$; exit 0;; + -l) bin_list_outdated; exit 0;; + -a) bin_list_installed; exit 0;; + -f) force=1;; + -v) verbose=1;; + -h) bin_help; exit 0;; + -*) die "invalid option '$1'";; + *) pkg="$pkg $1";; + esac + shift + done +} + +main() { + parseopts "$@" + + if [ "$_do_search" ]; then + bin_search "$search_pattern" + exit 0 + fi + + bin_help + exit 0 +} + +umask 022 + +SPM_PKGDB="${APKG_ROOT%/}/var/lib/spm/db" +APKG_PACKAGE_DIR="${APKG_PACKAGE_DIR:-$PWD}" +[ "$APKGBIN_CACHE_DIR" ] || die "APKGBIN_CACHE_DIR is not set" + +main "$@" + +exit 0 diff --git a/man/apkg-bin.8 b/man/apkg-bin.8 new file mode 100644 index 0000000..f872623 --- /dev/null +++ b/man/apkg-bin.8 @@ -0,0 +1,226 @@ +.\" -*- mode: troff; coding: utf-8 -*- +.TH APKG\-BIN 8 +.SH NAME +apkg\-bin \- Alice Linux binary package manager +.SH DESCRIPTION +.LP +\fBapkg\-bin\fR is the binary package manager for Alice Linux. It fetches +pre-built \fI.spm\fR package archives from a binary repository, resolves +dependencies, and installs or upgrades packages via the \fBspm\fR(8) backend. +It is the binary counterpart to the source-based \fBapkg\fR(8). + +Before use, run \fBapkg\-bin \-S\fR to sync the package index from +\fBAPKGBIN_REPO\fR. The index is cached locally at \fBAPKGBIN_CACHE_DIR\fR; +subsequent operations operate against the cache. + +.SH OPTIONS +.TP +\fB\-h\fR +Print help and exit. +.TP +\fB\-g\fR +Generate a binary repository index (\fIAPKGBINDB\fR) from \fI.spm\fR files found +in \fBAPKG_PACKAGE_DIR\fR. Reads \fIdepends\fR, \fIinfo\fR, \fIpreinstall\fR, +and \fIpostinstall\fR files from \fBAPKG_REPO\fR directories. The index is +written to \fBAPKG_PACKAGE_DIR\fR and copied to \fBAPKGBIN_CACHE_DIR\fR for +immediate local use. +.TP +\fB\-S\fR +Sync the binary repository index from \fBAPKGBIN_REPO\fR. For remote URLs (http, +https, ftp), downloads \fIAPKGBINDB\fR via \fBcurl\fR(1). For local paths, copies +the file directly. Cached at \fI$APKGBIN_CACHE_DIR/APKGBINDB\fR. +.TP +\fB\-s\fR [\fIpattern\fR] +Search available binary packages by name pattern. Without a pattern, lists all +packages. With \fB\-v\fR, prints name, version-release, and description. +Without \fB\-v\fR, prints package name only. +.TP +\fB\-i\fR \fI<pkg...>\fR +Install package(s) without dependency resolution. Skips packages that are +already installed. Requires root. +.TP +\fB\-I\fR \fI<pkg...>\fR +Install package(s) with full recursive dependency resolution. Skips packages +that are already installed. Prompts for confirmation before proceeding unless +\fBAPKG_NOPROMPT\fR is set. Requires root. +.TP +\fB\-u\fR \fI<pkg...>\fR +Upgrade or reinstall specific package(s). No dependency resolution, no +confirmation prompt. Reinstalls even if already up to date. Skips packages +not currently installed. Requires root. +.TP +\fB\-U\fR +Full system upgrade. Compares all installed packages against the cached index, +resolves the full dependency closure for outdated packages, installs any new +dependencies, and upgrades all outdated packages. Prompts for confirmation +unless \fBAPKG_NOPROMPT\fR is set. Respects \fBAPKG_MASK\fR. Requires root. +.TP +\fB\-d\fR \fI<pkg>\fR +Show direct dependencies for a package. Prints one dependency name per line. +.TP +\fB\-D\fR \fI<pkg...>\fR +Show full dependency tree for package(s) (recursive, all transitive dependencies). +Prints one dependency name per line, with the input package last. +.TP +\fB\-l\fR +List installed packages that have newer versions available in the binary index. +.TP +\fB\-a\fR +List all installed packages. With \fB\-v\fR, also prints the installed version. +.TP +\fB\-o\fR \fI<pkg...>\fR +Download package(s) only, without installing. Resolves each package name in the +binary index and downloads the \fI.spm\fR to \fI$APKGBIN_CACHE_DIR\fR. +.TP +\fB\-c\fR +Clean downloaded packages with sha3sum mismatch. Scans \fI$APKGBIN_CACHE_DIR/\fR, +compares each \fI.spm\fR against the expected sha3sum in APKGBINDB, and removes +any with a mismatch. +.TP +\fB\-f\fR +Force re-download of cached \fI.spm\fR files. Usable with \fB\-i\fR, \fB\-I\fR, +\fB\-u\fR, and \fB\-o\fR. +.TP +\fB\-v\fR +Verbose output. Affects \fB\-s\fR (show descriptions) and \fB\-a\fR (show versions). + +.SH BINARY REPOSITORY FORMAT +.LP +A binary repository is a directory (local or remote) containing: +.TP +\fIAPKGBINDB\fR +The package index file. Each line is a pipe-delimited record: +.RS +.IP +\fIname\fB#\fIversion-release\fB|\fIsize\fB|\fIsha3sum\fB|\fIdep1,dep2,...\fB|\fIpreinstall_b64\fB|\fIpostinstall_b64\fB|\fIdescription\fR +.RE +.IP +Fields: package identifier, file size in bytes, sha3sum hash, comma-separated +dependency names (empty if none), base64-encoded preinstall script (empty if +none), base64-encoded postinstall script (empty if none), and a human-readable +description. Use \fBapkg\-bin \-g\fR to generate. +.TP +\fI<name>#<version-release>.spm\fR +Pre-built package archives in \fBspm\fR(8) format. The \fB#\fR in filenames +is URL-encoded as \fB%23\fR when served over HTTP. + +.SH ENVIRONMENT +.TP +\fBAPKGBIN_REPO\fR +Binary repository URL or local path. Required for \fB\-S\fR, \fB\-i\fR, +\fB\-I\fR, \fB\-u\fR, \fB\-U\fR, and \fB\-o\fR. +.TP +\fBAPKGBIN_CACHE_DIR\fR +Directory where the synced \fIAPKGBINDB\fR and downloaded \fI.spm\fR packages +are cached. Required. +.TP +\fBAPKG_REPO\fR +Space-separated list of source repository directories. Required for \fB\-g\fR +(to read \fIdepends\fR, \fIinfo\fR, \fIpreinstall\fR, and \fIpostinstall\fR +files). +.TP +\fBAPKG_PACKAGE_DIR\fR +Directory for package storage. Scanned by \fB\-g\fR for \fI.spm\fR files. +Default: current directory. +.TP +\fBAPKG_ROOT\fR +Alternative root directory for installation. Sets \fBSPM_ROOT\fR. Default: \fI/\fR. +.TP +\fBAPKG_MASK\fR +Space-separated list of packages to skip during \fB\-U\fR (system upgrade). +.TP +\fBAPKG_ALIAS\fR +Space-separated list of \fIreal:alias\fR pairs for dependency substitution +(e.g. \fIopenssl:libressl\fR). +.TP +\fBAPKG_NOPROMPT\fR +If set, skip the confirmation prompt in \fB\-I\fR and \fB\-U\fR operations. + +.SH FILES +.TP +\fI$APKGBIN_CACHE_DIR/APKGBINDB\fR +Cached binary repository index. +.TP +\fI$APKGBIN_CACHE_DIR/\fR +Downloaded \fI.spm\fR package files. +.TP +\fI/var/lib/spm/db/\fR +SPM package database. Used to check installed packages and versions. + +.SH EXAMPLES +.LP +Sync the binary repository index: +.RS +\f(CRAPKGBIN_REPO=https://example.com/alice/main apkg\-bin \-S\fR +.RE +.LP +Search for packages: +.RS +\f(CRapkg\-bin \-s browser\fR +.RE +.LP +Install a package without dependency resolution: +.RS +\f(CRapkg\-bin \-i firefox\fR +.RE +.LP +Install a package with all dependencies: +.RS +\f(CRapkg\-bin \-I firefox\fR +.RE +.LP +Show dependencies of a package: +.RS +\f(CRapkg\-bin \-d firefox\fR +.RE +.LP +Reinstall a package (force re-download): +.RS +\f(CRapkg\-bin \-u \-f opus\fR +.RE +.LP +List outdated packages: +.RS +\f(CRapkg\-bin \-l\fR +.RE +.LP +Full system upgrade: +.RS +\f(CRapkg\-bin \-U\fR +.RE +.LP +Generate a binary index (for repo maintainers): +.RS +\f(CRapkg\-bin \-g\fR +.RE +.LP +Clean mismatched downloads: +.RS +\f(CRapkg\-bin \-c\fR +.RE + +.SH SEE ALSO +.BR apkg (8), +.BR apkg\-chroot (8), +.BR apkg\-clean (8), +.BR apkg\-deps (8), +.BR apkg\-foreign (8), +.BR apkg\-genabuild (8), +.BR apkg\-orphan (8), +.BR apkg\-purge (8), +.BR apkg\-redundantdeps (8), +.BR reposync (8), +.BR revdep (8), +.BR updateconf (8), +.BR spm (8) + +.SH AUTHORS +.LP +emmett1 \c +.MT me@emmett1.my +.ME + +.SH REPORTING BUGS +.LP +.UR https://codeberg.org/emmett1/autils/issues +.UE |