apkg-bin (4938B)
1 #!/bin/sh 2 # apkg-bin - a simple binary package manager helper script 3 4 REMOTE="${REMOTE:-https://emmett1.my/files/alice/packages}" 5 PKGDIR="${PKGDIR:-/var/cache/apkg-bin}" 6 REPODB="${REPODB:-REPO}" 7 DBFILE="$PKGDIR/$REPODB" 8 9 usage() { 10 echo "Usage: $0 [options]" 11 echo " g <path> Generate database from package dir" 12 echo " S Sync database from remote server" 13 echo " l List packages" 14 echo " s <pattern> Search packages" 15 echo " a <pkg> Show package info" 16 echo " d <pkg...> Download package(s)" 17 echo " i <pkg...> Install package(s)" 18 echo " u <pkg...> Upgrade/reinstall package(s)" 19 echo " c Clean mismatched/unknown packages" 20 exit 0 21 } 22 23 msg() { 24 m=${pkg%#*} 25 echo "[${m:-...}] $*" 26 } 27 28 pkg_existence() { 29 need_db 30 [ "$(cut -d '#' -f1 "$DBFILE" | grep -x "$1" | uniq)" ] || return 1 31 return 0 32 } 33 34 get_pkg() { 35 for p in $@; do 36 pkg_existence $p && pkg="$pkg $p" || msg "No package found matching: $p" 37 done 38 } 39 40 get_entry() { 41 entry=$(grep ^$1# "$DBFILE" | sort -V -t'#' -k2 | tail -n1) 42 } 43 44 need_db() { 45 [ -f "$DBFILE" ] || { msg "Run '$0 S' first to sync database."; exit 1; } 46 } 47 48 need_root() { 49 [ $(id -u) = 0 ] || { msg "This option need root access."; exit 1; } 50 } 51 52 fetch_pkg() { 53 get_entry $1 54 expected_sha=$(echo "$entry" | cut -d'|' -f4) 55 pkg=$(echo "$entry" | cut -d'|' -f1) 56 57 mkdir -p "$PKGDIR" 58 if [ -f "$PKGDIR/$pkg" ]; then 59 msg "$PKGDIR/$pkg found." 60 else 61 url="$REMOTE/$(echo "$pkg" | sed 's/#/%23/g')" 62 msg "Downloading $url..." 63 curl -fsSLo "$PKGDIR/$pkg" "$url" || { msg "Download failed: $pkg" >&2; return 1; } 64 fi 65 66 actual_sha=$(sha256sum "$PKGDIR/$pkg" | awk '{print $1}') 67 if [ "$expected_sha" = "$actual_sha" ]; then 68 msg "Verification PASSED: $pkg" 69 else 70 msg "Verification FAILED: $pkg" >&2 71 rm -f "$pkg" 72 return 1 73 fi 74 75 if [ "$mode" ]; then 76 spm $mode "$PKGDIR/$pkg" 77 fi 78 } 79 80 generate_db() { 81 [ -d "$1" ] || { msg "Invalid path: $1"; exit 1; } 82 > "$1/$REPODB" 83 msg "Generating database, please wait..." 84 for f in "$1"/*.spm; do 85 [ -f "$f" ] || continue 86 size=$(stat -c%s "$f") 87 mtime=$(stat -c%Y "$f") 88 sha=$(sha256sum "$f" | awk '{print $1}') 89 name=$(basename "$f") 90 echo "$name|$size|$mtime|$sha" >> "$1/$REPODB" 91 done 92 msg "Database generated at $1/$REPODB" 93 } 94 95 sync_db() { 96 need_root 97 mkdir -p "${DBFILE%/*}" 98 msg "Downloading database $REMOTE/$REPODB" 99 curl -fsSo "$DBFILE" "$REMOTE/$REPODB" || { msg "Failed to sync $REPODB"; exit 1; } 100 msg "Database synced to $DBFILE" 101 } 102 103 list_pkg() { 104 need_db 105 cut -d'|' -f1 "$DBFILE" | sed 's/#/ /g;s/.spm//g' 106 } 107 108 search_pkg() { 109 need_db 110 cut -d '|' -f1 "$DBFILE" | grep -i "$1" | sed 's/#/ /g;s/.spm//g' 111 exit 0 112 } 113 114 info_pkg() { 115 need_db 116 get_pkg $@ 117 for p in $pkg; do 118 get_entry $p 119 name=$p 120 verrel=$(echo "$entry" | cut -d '#' -f2 | sed 's/\.spm.*//') 121 size=$(echo "$entry" | cut -d '|' -f2) 122 mtime=$(echo "$entry" | cut -d '|' -f3) 123 sha=$(echo "$entry" | cut -d '|' -f4) 124 125 # convert size 126 if [ "$size" -lt 1024 ]; then 127 hsize="${size} B" 128 elif [ "$size" -lt 1048576 ]; then 129 hsize="$(awk "BEGIN{printf \"%.1f\", $size/1024}") KB" 130 else 131 hsize="$(awk "BEGIN{printf \"%.1f\", $size/1048576}") MB" 132 fi 133 134 # convert date 135 hdate=$(date -d @"$mtime" '+%Y-%m-%d %H:%M:%S') 136 137 echo "Package: $name" 138 echo "Version: $verrel" 139 echo "Size: $hsize" 140 echo "Date: $hdate" 141 echo "SHA256: $sha" 142 done 143 } 144 145 download_pkg() { 146 need_db 147 get_pkg $@ 148 for p in $pkg; do 149 fetch_pkg $p 150 done 151 } 152 153 install_pkg() { 154 need_root 155 need_db 156 get_pkg $@ 157 for p in $pkg; do 158 [ "$(spm -a | grep -x $p)" ] && { 159 msg "Package '$p' is installed, skipped." 160 } || { 161 pkg2="$pkg2 $p" 162 } 163 done 164 for p in $pkg2; do 165 mode='-i' fetch_pkg $p 166 done 167 } 168 169 upgrade_pkg() { 170 need_root 171 need_db 172 get_pkg $@ 173 for p in $pkg; do 174 [ "$(spm -a | grep -x $p)" ] || { 175 msg "Package '$p' not installed, skipped." 176 } && { 177 pkg2="$pkg2 $p" 178 } 179 done 180 for p in $pkg2; do 181 mode='-u' fetch_pkg $p 182 done 183 } 184 185 clean_pkg() { 186 need_db 187 need_root 188 msg "Checking local packages in $PKGDIR..." 189 known_pkgs=$(cut -d '|' -f1 "$DBFILE") 190 for f in "$PKGDIR"/*.spm; do 191 [ -f "$f" ] || continue 192 fname=${f##*/} 193 if echo "$known_pkgs" | grep -qx "$fname"; then 194 entry=$(grep "^$fname|" "$DBFILE") 195 expected_size=$(echo "$entry" | cut -d'|' -f2) 196 expected_sha=$(echo "$entry" | cut -d'|' -f4) 197 actual_size=$(stat -c%s "$f") 198 actual_sha=$(sha256sum "$f" | awk '{print $1}') 199 if [ "$expected_size" != "$actual_size" ] || [ "$expected_sha" != "$actual_sha" ]; then 200 msg "Mismatch: $fname (removed)" 201 fi 202 else 203 msg "Unknown package: $fname (removed)" 204 rm -f "$f" 205 fi 206 done 207 msg "Cleanup done." 208 } 209 210 main() { 211 if [ ! "$1" ]; then 212 usage 213 else 214 case $1 in 215 g) generate_db $2 ;; 216 S) sync_db ;; 217 l) list_pkg ;; 218 s) search_pkg $2 ;; 219 a) shift; info_pkg $@ ;; 220 d) shift; download_pkg $@ ;; 221 i) shift; install_pkg $@ ;; 222 u) shift; upgrade_pkg $@ ;; 223 c) clean_pkg ;; 224 *) usage ;; 225 esac 226 exit 0 227 fi 228 } 229 230 main $@ 231 232 exit 0