Skip to content

Instantly share code, notes, and snippets.

@ConnerWill
Last active July 18, 2022 18:08
Show Gist options
  • Save ConnerWill/594f9ffdc9b0b519468c4fa9a2f40b72 to your computer and use it in GitHub Desktop.
Save ConnerWill/594f9ffdc9b0b519468c4fa9a2f40b72 to your computer and use it in GitHub Desktop.
'pkg' package manager wrapper for both Debian and Arch distros. (now in color! and with fzf!!!)
#!/data/data/com.termux/files/usr/bin/bash
set -eu
# Setup TERMUX_APP_PACKAGE_MANAGER
source "/data/data/com.termux/files/usr/bin/termux-setup-package-manager" || exit 1
show_help() {
local cache_size
local cache_dir=""
if [ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" ]; then
cache_dir="/data/data/com.termux/cache/apt/archives"
elif [ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" ]; then
cache_dir="/data/data/com.termux/files/usr/var/cache/pacman/pkg"
fi
cache_size=$(du -sh "$cache_dir" 2>/dev/null | cut -f1)
# \e[0;38;5;255m
# echo 'Usage: pkg [--check-mirror] command [arguments]'
# printf '\n'
# echo 'A tool for managing packages.'
# echo ' --check-mirror forces a re-check of availability of mirrors'
printf '\n'
echo 'Commands:'
printf '\n'
printf '\e[0;38;5;10m autoclean \e[0;38;5;255m- \e[3;4mRemove all outdated packages from .deb package cache\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m clean \e[0;38;5;255m- \e[3;4mRemove all packages from .deb package cache.\e[0m\n'
#[ -n "$cache_size" ] && printf " Using %s now\e[0m\n""$cache_size"
printf '\n'
printf '\e[0;38;5;10m files \e[0;38;5;8m<packages> \e[0;38;5;255m- \e[3;4mShow all files installed by packages.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m install \e[0;38;5;8m<packages> \e[0;38;5;255m- \e[3;4mInstall specified packages.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m reinstall \e[0;38;5;8m<packages> \e[0;38;5;255m- \e[3;4mReinstall specified installed packages at thelatest version.\e[0m\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m list-all \e[0;38;5;255m- \e[3;4mList all packages available in repositories.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m list-installed \e[0;38;5;255m- \e[3;4mList installed packages.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m search \e[0;38;5;8m<query> \e[0;38;5;255m- \e[3;4mSearch package by query, for example by name or description part.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m fzf \e[0;38;5;8m[query] \e[0;38;5;255m- \e[3;4mList/Search all packages using fzf.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m show \e[0;38;5;8m<packages> \e[0;38;5;255m- \e[3;4mShow basic metadata, such as dependencies.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m uninstall \e[0;38;5;8m<packages> \e[0;38;5;255m- \e[3;4mUninstall specified packages. Configuration files will be left intact.\e[0m\n'
printf '\n'
printf '\e[0;38;5;10m upgrade \e[0;38;5;255m- \e[3;4mUpgrade all installed packages to the latest version.\e[0m\n'
printf '\n'
exit 1
}
check_mirror() {
local mirror="${1%/}"
local timeout="${2-5}"
timeout "$((timeout + 1))" curl \
--head \
--fail \
--connect-timeout "$timeout" \
--location \
--user-agent "Termux-PKG/2.0 mirror-checker (termux-tools 1.20) Termux (com.termux; install-prefix:/data/data/com.termux/files/usr)" \
"$mirror/dists/stable/Release" >/dev/null 2>&1
}
hostname() {
echo "$1" | awk -F'[/:]' '{print $4}'
}
last_modified() {
local mtime
local now
mtime=$(date -r "$1" '+%s')
now=$(date '+%s')
echo $((now - mtime))
}
has_repo() {
# Check if root-repo or x11-repo are installed
repo="$1"
if [ -f "/data/data/com.termux/files/usr/etc/apt/sources.list.d/$repo.list" ]; then
echo true
else
echo false
fi
}
_fzf_function(){
local searchquery
searchquery="$@"
apt list | fzf \
--query "${searchquery}" \
--reverse \
--extended --ansi \
--sync --print-query \
--color='hl+:#ff00ff,hl:#00ff00' \
--preview="apt show {} | egrep --regex '(^Package)|(^Description)' | column --separator ':' --table " \
--preview-window='bottom,5' \
}
get_mirror_url() {
source "$1"
echo $MAIN
}
get_mirror_weight() {
source "$1"
echo $WEIGHT
}
select_mirror() {
local default_repo="/data/data/com.termux/files/usr/etc/termux/mirrors/default"
if [ -d "/data/data/com.termux/files/usr/etc/termux/chosen_mirrors" ]; then
mirrors=($(find /data/data/com.termux/files/usr/etc/termux/chosen_mirrors/ -type f ! -name "*\.dpkg-old" ! -name "*~"))
elif [ -L "/data/data/com.termux/files/usr/etc/termux/chosen_mirrors" ]; then
# Broken symlink, use all mirrors
mirrors=("/data/data/com.termux/files/usr/etc/termux/mirrors/default")
mirrors+=($(find /data/data/com.termux/files/usr/etc/termux/mirrors/{asia,china,europe,north_america,russia}/ -type f ! -name "*\.dpkg-old" ! -name "*~"))
else
echo "No group of mirrors selected. You might want to select a group by running 'termux-change-repo'"
mirrors=("/data/data/com.termux/files/usr/etc/termux/mirrors/default")
mirrors+=($(find /data/data/com.termux/files/usr/etc/termux/mirrors/{asia,china,europe,north_america,russia}/ -type f ! -name "*\.dpkg-old" ! -name "*~"))
fi
local current_mirror
current_mirror=$(grep -m 1 -P "^\s*deb\s+" /data/data/com.termux/files/usr/etc/apt/sources.list | grep -oP 'https?://[^\s]+')
# Do not update mirror if $TERMUX_PKG_NO_MIRROR_SELECT was set.
if [ -n "${TERMUX_PKG_NO_MIRROR_SELECT-}" ] && [ -n "$current_mirror" ]; then
return
fi
# Mirrors are rotated if 6 hours timeout has been passed or mirror is no longer accessible.
local pkgcache="/data/data/com.termux/cache/apt/pkgcache.bin"
if [ -e "$pkgcache" ] && (( $(last_modified "$pkgcache") <= 6 * 3600 )) && [ "$force_check_mirror" = "false" ]; then
if [ -n "$current_mirror" ]; then
echo "Checking availability of current mirror:"
echo -n "[*] $current_mirror: "
if check_mirror "$current_mirror"; then
echo "ok"
return
else
echo "bad"
fi
fi
fi
# Test mirror availability, remove unaccessible mirrors from list.
echo "Testing the available mirrors:"
local parallel_jobs_max_count=10
if [[ ! "$parallel_jobs_max_count" =~ ^[0-9]+$ ]] || \
[[ "$parallel_jobs_max_count" -lt 1 ]] || \
[[ "$parallel_jobs_max_count" -gt 1000 ]]; then
parallel_jobs_max_count=1
fi
declare -a parallel_jobs_mirrors=()
declare -a parallel_jobs_weights=()
declare -a parallel_jobs_urls=()
declare -a parallel_jobs_numbers=()
declare -a parallel_jobs_pids=()
declare -a parallel_jobs_return_values=()
local i j mirror url job_number job_pid return_value
local total_mirrors=${#mirrors[@]}
local parallel_jobs_current_count=1
set +e
i=0
for mirror in "${!mirrors[@]}"; do
url="$(get_mirror_url ${mirrors[$mirror]})"
job_number=$parallel_jobs_current_count
parallel_jobs_current_count=$((parallel_jobs_current_count + 1))
# Start mirror check in background
check_mirror "$url" &
job_pid=$!
parallel_jobs_mirrors=("${parallel_jobs_mirrors[@]}" "$mirror")
parallel_jobs_weights=("${parallel_jobs_weights[@]}" "$(get_mirror_weight ${mirrors[$mirror]})")
parallel_jobs_urls=("${parallel_jobs_urls[@]}" "$url")
parallel_jobs_numbers=("${parallel_jobs_numbers[@]}" "$job_number")
parallel_jobs_pids=("${parallel_jobs_pids[@]}" "$job_pid")
# If current job count has reached max value or is the last mirror, wait for already started jobs to finish
if [ "$job_number" -ge $parallel_jobs_max_count ] || \
[ "$i" -ge $((total_mirrors - 1)) ]; then
j=0
# For pids of all jobs
for job_pid in "${parallel_jobs_pids[@]}"; do
# Wait for job with matching pid to return
# echo "waiting for check_mirror job ${parallel_jobs_numbers[j]} for mirror \"${parallel_jobs_urls[j]}\" with pid ${parallel_jobs_pids[j]}"
wait "$job_pid"
return_value=$?
parallel_jobs_return_values=("${parallel_jobs_return_values[@]}" "$return_value")
j=$((j + 1))
done
j=0
# For return_values of all jobs
for return_value in "${parallel_jobs_return_values[@]}"; do
echo -n "[*] (${parallel_jobs_weights[j]}) ${parallel_jobs_urls[j]}: "
if [ "$return_value" -eq 0 ]; then
echo "ok"
else
echo "bad"
# echo "check_mirror job ${parallel_jobs_numbers[j]} for mirror \"${parallel_jobs_urls[j]}\" with pid ${parallel_jobs_pids[j]} failed with exit code $return_value"
unset "mirrors[${parallel_jobs_mirrors[j]}]"
fi
j=$((j + 1))
done
# Reset job related variables
parallel_jobs_current_count=1
parallel_jobs_mirrors=()
parallel_jobs_weights=()
parallel_jobs_urls=()
parallel_jobs_numbers=()
parallel_jobs_pids=()
parallel_jobs_return_values=()
fi
i=$((i + 1))
done
set -e
# Build weighted array of valid mirrors
declare -a weighted_mirrors
local total_mirror_weight=0
local weight
for mirror in "${!mirrors[@]}"; do
# Check if mirror was unset in parallel check
if [ -z "${mirrors[$mirror]-}" ]; then
continue
fi
weight="$(get_mirror_weight ${mirrors[$mirror]})"
total_mirror_weight=$((total_mirror_weight + weight))
j=0
while [ "$j" -lt "$weight" ]; do
weighted_mirrors+=(${mirrors[$mirror]})
j=$((j + 1))
done
done
# Select random mirror
local selected_mirror=""
if ((total_mirror_weight > 0)); then
local random_weight
random_weight=$(( (RANDOM % total_mirror_weight + 1) - 1 ))
echo "Picking mirror: (${random_weight}) ${weighted_mirrors[${random_weight}]}"
selected_mirror="${weighted_mirrors[${random_weight}]}"
fi
if [ -n "$selected_mirror" ]; then
(
source "$selected_mirror"
echo "deb $MAIN stable main" > /data/data/com.termux/files/usr/etc/apt/sources.list
if [ "$(has_repo x11)" == "true" ]; then
echo "deb $X11 x11 main" > /data/data/com.termux/files/usr/etc/apt/sources.list.d/x11.list
fi
if [ "$(has_repo root)" == "true" ]; then
echo "deb $ROOT root stable" > /data/data/com.termux/files/usr/etc/apt/sources.list.d/root.list
fi
)
fi
}
update_apt_cache() {
local current_host
current_host=$(sed -nE -e 's|^\s*deb\s+https?://(.+)\s+stable\s+main$|\1|p' /data/data/com.termux/files/usr/etc/apt/sources.list | head -n 1)
if [ -z "$current_host" ]; then
# No primary repositories configured?
apt update
return
fi
local metadata_file
metadata_file=$(
list_prefix=$(echo "$current_host" | sed 's|/|_|g')
arch=$(dpkg --print-architecture)
echo "/data/data/com.termux/files/usr/var/lib/apt/lists/${list_prefix}_dists_stable_main_binary-${arch}_Packages" | sed 's|__|_|g'
)
if [ ! -e "/data/data/com.termux/cache/apt/pkgcache.bin" ] || [ ! -e "$metadata_file" ]; then
apt update
return
fi
local cache_modified
cache_modified=$(last_modified "/data/data/com.termux/cache/apt/pkgcache.bin")
local sources_modified
sources_modified=$(last_modified "/data/data/com.termux/files/usr/etc/apt/sources.list")
if (( sources_modified <= cache_modified )) || (( cache_modified > 1200 )); then
apt update
fi
}
force_check_mirror=false
if [ "${1-}" = "--check-mirror" ]; then
force_check_mirror=true
shift 1
fi
if [[ $# = 0 || $(echo "$1" | grep "^h") ]]; then
show_help
fi
CMD="$1"
shift 1
ERROR=false
case "$TERMUX_APP_PACKAGE_MANAGER" in
apt)
case "$CMD" in
fi*) dpkg -L "$@";;
sh*|inf*) apt show "$@";;
add|i*) select_mirror; update_apt_cache; apt install "$@";;
autoc*) apt autoclean;;
cl*) apt clean;;
fz*) _fzf_function "$@" ;;
list-a*) apt list "$@";;
list-i*) apt list --installed "$@";;
rei*) apt install --reinstall "$@";;
se*) select_mirror; update_apt_cache; apt search "$@";;
un*|rem*|rm|del*) apt remove "$@";;
up*) select_mirror; apt update; apt full-upgrade "$@";;
*) ERROR=true;;
esac;;
pacman)
case "$CMD" in
f*) pacman -Ql "$@";;
sh*|inf*) pacman -Qi "$@";;
add|i*) pacman -Sy --needed "$@";;
autoc*) pacman -Sc;;
cl*) pacman -Scc;;
list-a*) pacman -Sl "$@";;
list-i*) pacman -Q "$@";;
rei*) pacman -S "$@";;
se*) pacman -Sys "$@";;
un*|rem*|rm|del*) pacman -Rcns "$@";;
up*) pacman -Syu "$@";;
*) ERROR=true;;
esac;;
esac
if $ERROR; then
printf "\n\e[0;38;5;190mUnknown command: '\e[0;1;38;5;196m%s\e[0;38;5;190m'\e[0m \e[0;3;4;38;5;8m(run 'pkg help' for usage information)\e[0m\n\n" "$CMD" ; exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment