Last active
November 30, 2025 21:30
-
-
Save akrez/01b38fc760e6269a023058accaa9d96b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| function error() { | |
| echo -e "$1" 1>&2 | |
| exit 1 | |
| } | |
| function change_owner() { | |
| chown -R _apt:root "$1" || error "FAILED TO CHANGE OWNER : $1" | |
| } | |
| function make_directory() { | |
| mkdir -p -m755 "$1" || error "FAILED TO MAKE DIRECTORY : $1" | |
| } | |
| function help() { | |
| error "Usage:\n\tdownload <package1> <package2> ...\n\tinstall <package1> <package2> ..." | |
| } | |
| if [ $# -gt 0 ]; then | |
| COMMAND="$1" | |
| shift | |
| else | |
| help | |
| fi | |
| if [[ $EUID -ne 0 ]]; then | |
| error "RUN AS ROOT" | |
| fi | |
| OS_RELEASE=$(source /etc/os-release && echo $ID-$VERSION_ID) | |
| ABSOLUTE_BASE_PATH=$(realpath $(dirname ${BASH_SOURCE[0]}))/$OS_RELEASE | |
| make_directory "$ABSOLUTE_BASE_PATH" | |
| change_owner "$ABSOLUTE_BASE_PATH" | |
| cd "$ABSOLUTE_BASE_PATH" | |
| if [[ "$COMMAND" == "download" ]]; then | |
| for PACKAGE in "$@"; do | |
| if ! apt-cache show "$PACKAGE" >/dev/null 2>&1; then | |
| error "π΄ [$PACKAGE] APT-CACHE NOT FOUND PACKAGE" | |
| fi | |
| done | |
| for PACKAGE in "$@"; do | |
| mapfile -t ALL_PACKAGE_AND_DEPENDS < <(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances --no-pre-depends -o APT::Architectures="amd64" "$PACKAGE" | grep -E "^\w" | sort -u) | |
| mapfile -t PACKAGE_AND_DEPENDS < <(apt-cache policy "${ALL_PACKAGE_AND_DEPENDS[@]}" | awk '/^[a-zA-Z]/ {pkg=$1} /\*\*\*/ {sub(":", "", pkg); print pkg}') | |
| mapfile -t LINES < <(apt-get install -o Dir::Cache::archives=/dev/null --print-uris --reinstall "${PACKAGE_AND_DEPENDS[@]}" 2>&1 | grep "'http") | |
| FILENAMES=() | |
| for LINE in "${LINES[@]}"; do | |
| URI=$(echo "$LINE" | sed "s/^'\([^']*\)'.*/\1/") | |
| DECODED_FILENAME=$(echo "$LINE" | sed "s/.*'[^']*' \([^ ]*\.deb\) .*/\1/") | |
| EXPECTED_SIZE=$(echo "$LINE" | cut -d' ' -f3) | |
| FILEPATH="$ABSOLUTE_BASE_PATH/$DECODED_FILENAME" | |
| if [[ -f "$FILEPATH" && "$EXPECTED_SIZE" == $(stat -c%s "$FILEPATH" 2>/dev/null || wc -c < "$FILEPATH") ]]; then | |
| echo "β¬οΈ [$PACKAGE] DOWNLOADED $DECODED_FILENAME" | |
| else | |
| echo "β¬οΈ [$PACKAGE] DOWNLOADING $DECODED_FILENAME" | |
| wget -O "$FILEPATH" "$URI" | |
| fi | |
| if [[ ! -f "$FILEPATH" ]]; then | |
| error "π΄ [$PACKAGE] ($DECODED_FILENAME) IS NOT EXIST: $URI" | |
| elif [[ "$EXPECTED_SIZE" != $(stat -c%s "$FILEPATH" 2>/dev/null || wc -c < "$FILEPATH") ]]; then | |
| error "π΄ [$PACKAGE] ($DECODED_FILENAME) FILE SIZE MISMATCH: $URI" | |
| else | |
| FILENAMES+=("$DECODED_FILENAME") | |
| fi | |
| done | |
| MANIFESTPATH="$ABSOLUTE_BASE_PATH/${PACKAGE}.txt" | |
| echo "π [$PACKAGE] WRITE MANIFEST $MANIFESTPATH" | |
| printf '%s\n' "${FILENAMES[@]}" > "$MANIFESTPATH" | |
| echo | |
| done | |
| change_owner "$ABSOLUTE_BASE_PATH" | |
| elif [[ "$COMMAND" == "install" ]]; then | |
| for PACKAGE in "$@"; do | |
| LISTFILE="$ABSOLUTE_BASE_PATH/${PACKAGE}.txt" | |
| if [[ ! -f "$LISTFILE" ]]; then | |
| error "π΄ [$PACKAGE] NOT FOUND" | |
| fi | |
| DEB_FILES=() | |
| while IFS= read -r LINE; do | |
| [[ -z "$LINE" ]] && continue | |
| FILEPATH="$ABSOLUTE_BASE_PATH/$LINE" | |
| if [[ ! -f "$FILEPATH" ]]; then | |
| error "π΄ [$PACKAGE] MISSING DEB FILE ($LINE)" | |
| fi | |
| DEB_FILES+=("$FILEPATH") | |
| done < "$LISTFILE" | |
| if [[ ${#DEB_FILES[@]} -eq 0 ]]; then | |
| error "π΄ [$PACKAGE] NO PACKAGES TO INSTALL" | |
| fi | |
| dpkg -i "${DEB_FILES[@]}" || error "π΄ [$PACKAGE] FAILED TO INSTALL" -o APT::Architectures="amd64" | |
| done | |
| elif [[ "$COMMAND" == "cleanup" ]]; then | |
| declare -A VALID_DEBS | |
| for MANIFEST_FILE in "$ABSOLUTE_BASE_PATH"/*.txt; do | |
| while IFS= read -r DEB_FILE; do | |
| [[ -z "$DEB_FILE" ]] && continue | |
| VALID_DEBS["$DEB_FILE"]=1 | |
| done < "$MANIFEST_FILE" | |
| done | |
| for DEB_FILE in "$ABSOLUTE_BASE_PATH"/*.deb; do | |
| [[ ! -f "$DEB_FILE" ]] && continue | |
| FILENAME=$(basename "$DEB_FILE") | |
| if [[ -z "${VALID_DEBS[$FILENAME]}" ]]; then | |
| echo "ποΈ REMOVE ($FILENAME)" | |
| rm -f "$DEB_FILE" | |
| fi | |
| done | |
| else | |
| help | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment