diff options
Diffstat (limited to 'apkg-bin')
| -rwxr-xr-x | apkg-bin | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/apkg-bin b/apkg-bin new file mode 100755 index 0000000..3f15d85 --- /dev/null +++ b/apkg-bin @@ -0,0 +1,232 @@ +#!/bin/sh +# apkg-bin - a simple binary package manager helper script + +REMOTE="{REMOTE:-https://emmett1.my/files/alice/packages}" +REPODB="REPO" +PKGDIR="/var/cache/apm" +DBFILE="$PKGDIR/$REPODB" + +usage() { + echo "Usage: $0 [options]" + echo " g <path> Generate database from package dir" + echo " S Sync database from remote server" + echo " l List packages" + echo " s <pattern> Search packages" + echo " a <pkg> Show package info" + echo " d <pkg...> Download package(s)" + echo " i <pkg...> Install package(s)" + echo " u <pkg...> 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 (removing)" + 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 |