Created
August 27, 2017 21:33
-
-
Save l3laze/ba63e3a9dafed50b9d28041a10cd4673 to your computer and use it in GitHub Desktop.
A little Bash "dependency manager" with download/install/cache powers. Beta at best.
This file contains 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
#!/usr/bin/env bash | |
# | |
# lilman - A little "dependency manager" with download/cache/install powers. | |
# | |
# Follows the suggested exit code range of 0 & 64-113 from http://www.tldp.org/LDP/abs/html/exitcodes.html | |
# | |
# Note: This is beta at best; still trying to work out some problems and may actually rewrite this (again) completely. | |
# Note 2: Moved to a Gist because it doesn't need it's own repo as far as I'm concerned. | |
# Include source file and line number in xtrace messages | |
# From http://wiki.bash-hackers.org/scripting/debuggingtips#making_xtrace_more_useful | |
# export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' | |
# Enable xtrace for debugging. | |
# set -x | |
# Enable "exit on first error" | |
# set -e | |
function rtfm() { | |
echo -e "Usage:\\n\\tlilman [ -n <string> ]* [ -o <string> ]* [ -s <string>]* [ -t <string> ]* [ ... ]\\n\\n\ | |
[ -c | --cacheloc <path> ]\\t\\tWhere to cache the dependency.\\n\ | |
[ -d | --downloadloc <path> ]\\t\\tThe directory to git clone or download the dependency to.\\n\ | |
[ -h | --help ]\\t\\t\\tDisplay this message.\\n\ | |
[ -i | --installloc <path> ]\\t\\tThe directory to unpack/move the dependency to.\\n\ | |
[ -l | --homeloc <path>]\\t\\tThe directory to use as lilman's home folder.\\n\ | |
*[ -n | --name <string>]\\t\\tThe name of the dependency as it will be installed.\\n\ | |
*[ -o | --downloadname <string>]\\tThe name of the downloaded file.\\n\ | |
*[ -s | --source <URL | GIT URI>]\\tA URL to download from using 'curl' or a git URL to 'git clone'.\\n\ | |
*[ -t | --type <URL | GIT>]\\t\\tThe dependency type, either 'URL' or 'GIT', case insensitive.\\n\ | |
[ -m | --mode <args>]\\t\\t\\tMode flags to change operation. Options are:\\n\\t\\t\ | |
[ d ]\\t\\t-\\tDon't delete download after installing/caching; keep it.\\n\\t\\t\ | |
[ q ]\\t\\t-\\tQuiet mode.\\n\\t\\t\ | |
[ r ]\\t\\t-\\tRenew mode; ignore cache, download a new copy of dependency.\\n\\t\\t\ | |
[ t ]\\t\\t-\\tTest mode -- runs internal test suite.\\n\\n\ | |
[ u ]\\t\\t-\\tUnpack mode; if the download is archived, unpack it to install.\\n\\t\\t\ | |
Where * = Required except for \"test mode\" and when using -h || --help, in which case other args are ignored.\\n" | |
} | |
function run_script() { | |
local lmhomedef | |
local lmcachedef | |
local lmdownloadsdef | |
local lminstalldef | |
local lmhome | |
local lmcache | |
local lmdownloads | |
local lminstall | |
local lmispacked | |
local lmunpack | |
local lmkeepdl | |
local lmrenew | |
local lmquiet | |
local logfile | |
local key | |
lmhomedef=~/Desktop/lil-man | |
lmcachedef=$lmhomedef/cache | |
lmdownloadsdef=$lmhomedef/downloads | |
lminstalldef=$lmhomedef/install | |
lmispacked=false | |
lmunpack=false | |
lmkeepdl=false | |
lmrenew=false | |
lmquiet=false | |
logfile=$lmhomedef/log.txt | |
#------------------------- | |
# | |
# Parse arguments. | |
# | |
if [[ $# -eq 0 ]]; then | |
# Display usage.. | |
echo "Error! -- no args!" | |
rtfm | |
exit 64 | |
else | |
while [[ $# -gt 0 ]] | |
do | |
key="$1" | |
case $key in | |
-c | --cacheloc) | |
lmcache="$2" | |
shift | |
;; | |
-d | --downloadloc) | |
lmdownloads="$2" | |
shift | |
;; | |
-i | --installloc) | |
lminstall="$2" | |
shift | |
;; | |
-l | --homeloc) | |
lmhome="$2" | |
shift | |
;; | |
-h | --help) | |
rtfm | |
exit 64; | |
;; | |
-n | --name) | |
lmname="$2" | |
shift | |
;; | |
-s | --source) | |
lmsource="$2" | |
shift | |
;; | |
-t | --type) | |
lmsrctype=$(echo "$2" | tr '[:upper:]' '[:lower:]') | |
case $lmsrctype in | |
git) | |
;; | |
url) | |
;; | |
*) | |
echo -e "\\n\\nError! Invalid dependency type: $2" | |
echo -e "Valid dependency types are 'GIT' and 'URL', case insensitive.\\n\\n" | |
exit 65; | |
esac | |
shift | |
;; | |
-o | --downloadname) | |
lmdlname="$2" | |
shift | |
;; | |
-m | --mode) | |
key2=$(echo "$2" | tr '[:upper:]' '[:lower:]') | |
if [[ "$key2" == *"d"* ]]; then | |
lmkeepdl=true | |
fi | |
if [[ "$key2" == *"r"* ]]; then | |
lmrenew=true | |
fi | |
if [[ "$key2" == *"q"* ]]; then | |
lmquiet=true | |
fi | |
if [[ "$key2" == *"u"* ]]; then | |
lmunpack=true | |
fi | |
if [[ "$key2" == *"t"* ]]; then | |
lmtest=true | |
fi | |
shift | |
;; | |
*) | |
clear | |
echo -e "\\n\\nError! Unknown option: $1\\n\\n" | |
rtfm | |
exit 64; | |
;; | |
esac | |
shift # To get past the -o/-option. | |
done | |
if [[ "$lmtest" == true ]]; then | |
echo -e "Mode = \"Test\"." | |
do_tests | |
exit 0 | |
fi | |
if [[ -z "$lmname" || "$lmname" == "" ]]; then | |
echo "You must set the name of the package." | |
exit 66; | |
fi | |
if [[ -z "$lmdlname" || "$lmdlname" == "" ]]; then | |
echo "You must set a download name to get the package $lmname." | |
exit 67; | |
else | |
[[ "$lmquiet" == false && ! -z "$lmpkdtype" ]] && echo "Checking if package $lmdlname is archived..." | |
if [[ "$lmdlname" == *".tar.gz"* ]]; then | |
lmispacked=true | |
lmpkdtype=".tar.gz" | |
elif [[ "$lmdlname" == *".zip"* ]]; then | |
lmispacked=true | |
lmpkdtype=".zip" | |
else | |
[[ "$lmquiet" == false ]] && echo "Package is either not archived, or is of an unsupported type: $lmdlname." | |
fi | |
fi | |
if [[ -z "$lmsource" || "$lmsource" == "" ]]; then | |
echo "You must set a source to get the package $lmname." | |
exit 68; | |
fi | |
if [[ -z "$lmsrctype" || "$lmsrctype" == "" ]]; then | |
echo "Error! You must set a dependency source type to get the package $lmname." | |
exit 69; # Giggity. | |
fi | |
if [[ -z "$lmhome" || "$lmhome" == "" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Using default home directory \"$lmhomedef\"." | |
lmhome="$lmhomedef" | |
fi | |
if [[ -z "$lmcache" || "$lmcache" == "" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Using default cache directory \"$lmcachedef\"." | |
lmcache="$lmcachedef" | |
fi | |
if [[ -z "$lmdownloads" || "$lmdownloads" == "" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Using default download directory \"$lmdownloadsdef\"." | |
lmdownloads="$lmdownloadsdef" | |
fi | |
if [[ -z "$lminstall" || "$lminstall" == "" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Using default install directory \"$lmcachedef\"." | |
lminstall="$lminstalldef" | |
fi | |
if [[ "$lmispacked" == true ]]; then | |
[[ "$lmquiet" == false ]] && echo "Package is archived ($lmpkdtype); will unarchive if in unpack mode (-m u)". | |
fi | |
if [[ "$lmkeepdl" == true ]]; then | |
[[ "$lmquiet" == false ]] && echo "Mode = \"Keep download\". The download will not be deleted after caching the dependency." | |
fi | |
if [[ "$lmrenew" == true ]]; then | |
[[ "$lmquiet" == false ]] && echo "Mode = \"Renew\". Dependency will be downloaded again and replace cached copy." | |
fi | |
if [[ "$lmunpack" == true ]]; then | |
[[ "$lmquiet" == false ]] && echo "Mode = \"Unpack\". If the package is archived it will be unarchived before installation." | |
fi | |
if [[ "$lmquiet" == false ]]; then | |
local status | |
status="\\n---- Status ----\\n" | |
status+="Name\\t\\t=\\t$lmname\\n" | |
status+="DL Name\\t\\t=\\t$lmdlname\\n" | |
status+="Type\\t\\t=\\t$lmsrctype\\n" | |
status+="Source\\t\\t=\\t$lmsource\\n" | |
status+="Download Dir\\t=\\t$lmdownloads\\n" | |
status+="Install Dir\\t=\\t$lminstall\\n" | |
status+="Cache Dir\\t=\\t$lmcache\\n" | |
status+="Keep DL\\t\\t=\\t$lmkeepdl\\n" | |
status+="Quiet Mode\\t=\\t$lmquiet\\n" | |
status+="Renew Mode\\t=\\t$lmrenew\\n" | |
status+="Unpack Mode\\t=\\t$lmunpack\\n" | |
status+="Test Mode\\t=\\t$lmtest\\n" | |
echo "$status" | |
fi | |
fi | |
[[ -e "$lmhome" ]] || mkdir "$lmhome" | |
[[ -e "$lmcache" ]] || mkdir "$lmcache" | |
[[ -e "$lmdownloads" ]] || mkdir "$lmdownloads" | |
[[ -e "$lminstall" ]] || mkdir "$lminstall" | |
if [[ ! -e "$lmdownloads/$lmdlname" && ! -e "$lmcache/$lmname.tar.gz" ]]; then | |
if [[ "$lmsrctype" == "url" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Package doesn't exist; downloading it." | |
curl -Lso "$lmdownloads/$lmdlname" "$lmsource" | |
elif [[ "$lmsrctype" == "git" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Package doesn't exist; 'git clone'-ing it." | |
git clone "$lmsource" "$lmdownloads/$lmdlname" >> "$logfile" 2>&1 | |
else | |
echo "Invalid source type: \"$lmsrctype\"." | |
fi | |
fi | |
if [[ "$lmispacked" == true && "$lmunpack" == true && -e "$lmdownloads/$lmdlname" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Unpacking archive $lmdlname and installing as package $lmname." | |
if [[ "$lmpkdtype" == ".zip" ]]; then | |
unzip -oq "$lmdownloads/$lmdlname" -d "$lminstall" | |
elif [[ "$lmpkdtype" == ".tar.gz" ]]; then | |
tar -xzf "$lmdownloads/$lmdlname" -C "$lminstall" | |
else | |
echo "Invalid packed type: \"$lmpkdtype\"." | |
fi | |
fi | |
if [[ ! -e "$lmcache/$lmname.tar.gz" ]]; then | |
echo "Caching $lmname as $lmname.tar.gz..." | |
if [[ "$lmpkdtype" == *".tar.gz"* ]]; then | |
[[ "$lmquiet" == false ]] && echo "Downloaded package was a tarball; caching it..." | |
cp -X "$lmdownloads/$lmdlname" "$lmcache/$lmdlname" | |
[[ "$lmquiet" == false && "$lmkeepdl" == false ]] && { echo "Removing download $lmdlname..."; rm -f "$lmdownloads/$lmdlname"; } | |
else | |
cd "$lmcache" || { echo "Error! -- Couldn't cd to \"$lmcache\"!"; exit 70; } | |
[[ "$lmquiet" == false ]] && echo "Archiving installed package $lmname in cache..." | |
tar -czf "$lmname.tar.gz" -C "$lmdownloads" "$lmdlname" | |
[[ "$lmquiet" == false && "$lmkeepdl" == false ]] && { echo "Removing download $lmdlname..."; rm -f "$lmdownloads/$lmdlname"; } | |
cd - > /dev/null || { echo "Error! -- Couldn't cd to \"${-}\"!"; exit 71; } | |
fi | |
else | |
[[ "$lmquiet" == false ]] && echo "$lmname is already cached!" | |
fi | |
if [[ ! -e "$lminstall/$lmname" && ! -e "$lmdownloads/$lmdlname" && -e "$lmcache/$lmname.tar.gz" ]]; then | |
cd "$lmcache" || { echo "Error! -- Couldn't cd to \"$lmcache\"!"; exit 70; } | |
[[ "$lmquiet" == false ]] && echo "Installing $lmname from cached copy..." | |
tar -xzf "$lmname".tar.gz -C "$lminstall" | |
cd - > /dev/null || { echo "Error! -- Couldn't cd to \"${-}\"!"; exit 71; } | |
elif [[ -e "$lmdownloads/$lmdlname" ]]; then | |
[[ "$lmquiet" == false ]] && echo "Installing $lmname from downloaded package..." | |
mv -f "$lmdownloads/$lmdlname" "$lminstall/$lmname" | |
else | |
[[ "$lmquiet" == false ]] && echo "$lmname is already installed!" | |
fi | |
} | |
#------------------------- | |
# Test suite for lilman. | |
# | |
# | |
# Function cleanup() | |
# Removes the home folder and all files so test is on fresh files. | |
# | |
function cleanup() { | |
rm -rf ~/Desktop/lil-man/downloads | |
rm -rf ~/Desktop/lil-man/install | |
rm -rf ~/Desktop/lil-man/cache | |
return 0 | |
} | |
function run_test() { | |
local testsrun=$7 | |
[[ ! -e ~/Desktop/lil-man ]] && mkdir ~/Desktop/lil-man | |
if [[ "$testsrun" != 1 ]]; then | |
printf "%*s\\n\\n" "$termwidth" | tr ' ' '-' >> "$logfile" | |
fi | |
printf "Test # %s \\t" "${testsrun}" | |
echo -e "Test # ${testsrun}\\n\"$1 $2->$3 (Options: $4; $5)\"\\n" >> "$logfile" | |
[[ -z "$1" || -z "$2" || -z "$3" || -z "$4" || -z "$5" || -z "$6" || -z "$7" ]] && { echo -e "\\xE2\\x9D\\x8C (bad test args)."; echo -e "\\nResult: bad test args\\n" >> "$logfile"; ((hadfailure++)); return 64; } | |
( $6 ) >> "$logfile" | |
if [[ -e ~/Desktop/lil-man/install/"$1" && -e ~/Desktop/lil-man/cache/"$1.$3" ]]; then | |
if [[ "$4" == *"d"* && -e ~/Desktop/lil-man/downloads/"$1.$2" || | |
"$4" != *"d"* && ! -e ~/Desktop/lil-man/downloads/"$1.$2" || | |
"$4" == *"D"* && -e ~/Desktop/lil-man/downloads/"$1.$2" || | |
"$4" != *"D"* && ! -e ~/Desktop/lil-man/downloads/"$1.$2" | |
]]; then | |
echo -e " = $?\\t\\xE2\\x9C\\x94" | |
echo -e "\\nResult: 0\\n" >> "$logfile" | |
return 0 | |
elif [[ "$4" != *"d"* && ! -e ~/Desktop/lil-man/downloads/"$1.$2" || | |
"$4" != *"D"* && ! -e ~/Desktop/lil-man/downloads/"$1.$2" | |
]]; then | |
echo -e " = $?\\t\\xE2\\x9D\\x8C" | |
echo -e "\\nResult: $?\\n" >> "$logfile" | |
((hadfailure++)) | |
return 65 | |
fi | |
fi | |
} | |
function finishtesting() { | |
local passed | |
passed=$(( ${1} - ${2} )) # testsrun - hadfailure = diff | |
echo "$passed/$1 passed." | |
} | |
function do_tests() { | |
# | |
# Script vars | |
# | |
set -e | |
local logfile | |
local testsrun | |
local termwidth | |
local hadfailure | |
local result | |
logfile=~/Desktop/lil-man/log.txt | |
testsrun=0 | |
hadfailure=0 | |
termwidth=$( tput cols ) | |
# | |
# Create a new log file. | |
# | |
echo "Removing old log file (\"$logfile\")." | |
rm -f "$logfile" | |
# | |
# Self!, do cleanup | |
# | |
cleanup | |
((testsrun++)) | |
run_test "lilman" "lilman" "tar.gz" "-m ud" "do cleanup" "./lilman -n lilman -t git -s https://github.com/l3laze/lilman.git -o lilman -m ud" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.tar.gz, no cleanup, keep download. | |
# | |
((testsrun++)) | |
run_test "7za" "tar.gz" "tar.gz" "-m ud" "no cleanup" "./lilman -n 7za -t urL -s https://www.dropbox.com/s/qsdei4vp65ob1gn/7za.tar.gz?dl=0 -o 7za.tar.gz -m ud" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.tar.gz | |
# | |
cleanup | |
((testsrun++)) | |
run_test "7za" "tar.gz" "tar.gz" "-m ud" "do cleanup" "./lilman -n 7za -t urL -s https://www.dropbox.com/s/qsdei4vp65ob1gn/7za.tar.gz?dl=0 -o 7za.tar.gz -m ud" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.tar.gz, do cleanup, don't keep download. | |
# | |
cleanup | |
((testsrun++)) | |
run_test "7za" "tar.gz" "tar.gz" "-m u" "do cleanup, don't keep download" "./lilman -n 7za -t urL -s https://www.dropbox.com/s/qsdei4vp65ob1gn/7za.tar.gz?dl=0 -o 7za.tar.gz -m u" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.tar.gz, no cleanup, don't keep download. | |
# | |
((testsrun++)) | |
run_test "7za" "tar.gz" "tar.gz" "-m u" "no cleanup, don't keep download" "./lilman -n 7za -t urL -s https://www.dropbox.com/s/qsdei4vp65ob1gn/7za.tar.gz?dl=0 -o 7za.tar.gz -m u" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.tar.gz, do cleanup, don't keep download. | |
# | |
cleanup | |
((testsrun++)) | |
run_test "7za" "tar.gz" "tar.gz" "-m u" "do cleanup, don't keep download" "./lilman -n 7za -t urL -s https://www.dropbox.com/s/qsdei4vp65ob1gn/7za.tar.gz?dl=0 -o 7za.tar.gz -m u" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.zip, no cleanup beforehand | |
# | |
((testsrun++)) | |
run_test "7za" "zip" "tar.gz" "-m UD" "no cleanup" "./lilman -n 7za -t url -s https://www.dropbox.com/s/5pfof15g4vccr6d/7za.zip?dl=0 -o 7za.zip -m ud" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
# | |
# 7za.zip, do cleanup | |
# | |
cleanup | |
((testsrun++)) | |
run_test "7za" "zip" "tar.gz" "-m ud" "do cleanup" "./lilman -n 7za -t url -s https://www.dropbox.com/s/5pfof15g4vccr6d/7za.zip?dl=0 -o 7za.zip -m ud" "$testsrun" | |
result=$? | |
[[ "$result" != 0 ]] && ((hadfailure++)) | |
finishtesting $testsrun $hadfailure | |
} | |
run_script "$@" | |
function test_globals() { | |
local areSet=0 | |
local vars | |
local item | |
local value | |
vars=( "$lmhomedef" "$lmcachedef" "$lmhomedef" "$lminstalldef" "$lmhome" "$lmcache" ) | |
vars+=( "$lmdownloads" "$lminstall" "$lmunpack" "$lmkeepdl" "$lmrenew" "$lmquiet" ) | |
vars+=( "$logfile" "$termwidth" "$line" "$len" "$result" "$testsrun" "$hadfailure" ) | |
vars+=( "$files" "$options" "$cmd" "$status" ) | |
for(( item = 0; item < ${#vars[@]}; item++ )); do | |
value="${vars[ item ]}" | |
if [[ ! -z "$value" ]]; then | |
((areSet++)) | |
fi | |
done | |
echo "Globals set: $areSet/${#vars[@]}" | |
} | |
test_globals |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment