#!/bin/sh # apkg-bin - a simple binary package manager helper script REMOTE="${REMOTE:-https://emmett1.my/pub/alice/packages}" PKGDIR="${PKGDIR:-/var/cache/apkg-bin}" REPODB="${REPODB:-REPO}" DBFILE="$PKGDIR/$REPODB" usage() { echo "Usage: $0 [options]" echo " g Generate database from package dir" echo " S Sync database from remote server" echo " l List packages" echo " s Search packages" echo " a Show package info" echo " d Download package(s)" echo " i Install package(s)" echo " u Upgrade/reinstall package(s)" echo " c Clean mismatched/unknown packages" exit 0 } msg() { m=${pkg%#*} echo "[${m:-...}] $*" } pkg_existence() { need_db [ "$(cut -d '#' -f1 "$DBFILE" | grep -x "$1" | uniq)" ] || return 1 return 0 } get_pkg() { for p in $@; do pkg_existence $p && pkg="$pkg $p" || msg "No package found matching: $p" done } get_entry() { entry=$(grep ^$1# "$DBFILE" | sort -V -t'#' -k2 | tail -n1) } need_db() { [ -f "$DBFILE" ] || { msg "Run '$0 S' first to sync database."; exit 1; } } need_root() { [ $(id -u) = 0 ] || { msg "This option need root access."; exit 1; } } fetch_pkg() { get_entry $1 expected_sha=$(echo "$entry" | cut -d'|' -f4) pkg=$(echo "$entry" | cut -d'|' -f1) mkdir -p "$PKGDIR" if [ -f "$PKGDIR/$pkg" ]; then msg "$PKGDIR/$pkg found." else url="$REMOTE/$(echo "$pkg" | sed 's/#/%23/g')" msg "Downloading $url..." curl -fsSLo "$PKGDIR/$pkg" "$url" || { msg "Download failed: $pkg" >&2; return 1; } fi actual_sha=$(sha256sum "$PKGDIR/$pkg" | awk '{print $1}') if [ "$expected_sha" = "$actual_sha" ]; then msg "Verification PASSED: $pkg" else msg "Verification FAILED: $pkg" >&2 rm -f "$pkg" return 1 fi if [ "$mode" ]; then spm $mode "$PKGDIR/$pkg" fi } generate_db() { [ -d "$1" ] || { msg "Invalid path: $1"; exit 1; } > "$1/$REPODB" msg "Generating database, please wait..." for f in "$1"/*.spm; do [ -f "$f" ] || continue size=$(stat -c%s "$f") mtime=$(stat -c%Y "$f") sha=$(sha256sum "$f" | awk '{print $1}') name=$(basename "$f") echo "$name|$size|$mtime|$sha" >> "$1/$REPODB" done msg "Database generated at $1/$REPODB" } sync_db() { need_root mkdir -p "${DBFILE%/*}" msg "Downloading database $REMOTE/$REPODB" curl -fsSo "$DBFILE" "$REMOTE/$REPODB" || { msg "Failed to sync $REPODB"; exit 1; } msg "Database synced to $DBFILE" } list_pkg() { need_db cut -d'|' -f1 "$DBFILE" | sed 's/#/ /g;s/.spm//g' } search_pkg() { need_db cut -d '|' -f1 "$DBFILE" | grep -i "$1" | sed 's/#/ /g;s/.spm//g' exit 0 } info_pkg() { need_db get_pkg $@ for p in $pkg; do get_entry $p name=$p verrel=$(echo "$entry" | cut -d '#' -f2 | sed 's/\.spm.*//') size=$(echo "$entry" | cut -d '|' -f2) mtime=$(echo "$entry" | cut -d '|' -f3) sha=$(echo "$entry" | cut -d '|' -f4) # convert size if [ "$size" -lt 1024 ]; then hsize="${size} B" elif [ "$size" -lt 1048576 ]; then hsize="$(awk "BEGIN{printf \"%.1f\", $size/1024}") KB" else hsize="$(awk "BEGIN{printf \"%.1f\", $size/1048576}") MB" fi # convert date hdate=$(date -d @"$mtime" '+%Y-%m-%d %H:%M:%S') echo "Package: $name" echo "Version: $verrel" echo "Size: $hsize" echo "Date: $hdate" echo "SHA256: $sha" done } download_pkg() { need_db get_pkg $@ for p in $pkg; do fetch_pkg $p done } install_pkg() { need_root need_db get_pkg $@ for p in $pkg; do [ "$(spm -a | grep -x $p)" ] && { msg "Package '$p' is installed, skipped." } || { pkg2="$pkg2 $p" } done for p in $pkg2; do mode='-i' fetch_pkg $p done } upgrade_pkg() { need_root need_db get_pkg $@ for p in $pkg; do [ "$(spm -a | grep -x $p)" ] || { msg "Package '$p' not installed, skipped." } && { pkg2="$pkg2 $p" } done for p in $pkg2; do mode='-u' fetch_pkg $p done } clean_pkg() { need_db need_root msg "Checking local packages in $PKGDIR..." known_pkgs=$(cut -d '|' -f1 "$DBFILE") for f in "$PKGDIR"/*.spm; do [ -f "$f" ] || continue fname=${f##*/} if echo "$known_pkgs" | grep -qx "$fname"; then entry=$(grep "^$fname|" "$DBFILE") expected_size=$(echo "$entry" | cut -d'|' -f2) expected_sha=$(echo "$entry" | cut -d'|' -f4) actual_size=$(stat -c%s "$f") actual_sha=$(sha256sum "$f" | awk '{print $1}') if [ "$expected_size" != "$actual_size" ] || [ "$expected_sha" != "$actual_sha" ]; then msg "Mismatch: $fname (removed)" rm -f "$f" fi else msg "Unknown package: $fname (removed)" rm -f "$f" fi done msg "Cleanup done." } main() { if [ ! "$1" ]; then usage else case $1 in g) generate_db $2 ;; S) sync_db ;; l) list_pkg ;; s) search_pkg $2 ;; a) shift; info_pkg $@ ;; d) shift; download_pkg $@ ;; i) shift; install_pkg $@ ;; u) shift; upgrade_pkg $@ ;; c) clean_pkg ;; *) usage ;; esac exit 0 fi } main $@ exit 0