aboutsummaryrefslogtreecommitdiff
path: root/apkg-bin
diff options
context:
space:
mode:
Diffstat (limited to 'apkg-bin')
-rwxr-xr-xapkg-bin232
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