|
#!/bin/bash |
|
|
|
ask_for_sudo() { |
|
|
|
# Ask for the administrator password upfront. |
|
|
|
sudo -v &> /dev/null |
|
|
|
# Update existing `sudo` time stamp |
|
# until this script has finished. |
|
# |
|
# https://gist.github.com/cowboy/3118588 |
|
|
|
while true; do |
|
sudo -n true |
|
sleep 60 |
|
kill -0 "$$" || exit |
|
done &> /dev/null & |
|
|
|
} |
|
|
|
show_spinner() { |
|
|
|
local -r FRAMES='/-\|' |
|
|
|
# shellcheck disable=SC2034 |
|
local -r NUMBER_OR_FRAMES=${#FRAMES} |
|
|
|
local -r CMDS="$2" |
|
local -r MSG="$3" |
|
local -r PID="$1" |
|
|
|
local i=0 |
|
local frameText="" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Note: In order for the Travis CI site to display |
|
# things correctly, it needs special treatment, hence, |
|
# the "is Travis CI?" checks. |
|
|
|
if [ "$TRAVIS" != "true" ]; then |
|
|
|
# Provide more space so that the text hopefully |
|
# doesn't reach the bottom line of the terminal window. |
|
# |
|
# This is a workaround for escape sequences not tracking |
|
# the buffer position (accounting for scrolling). |
|
# |
|
# See also: https://unix.stackexchange.com/a/278888 |
|
|
|
printf "\n\n\n" |
|
tput cuu 3 |
|
|
|
tput sc |
|
|
|
fi |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Display spinner while the commands are being executed. |
|
|
|
while kill -0 "$PID" &>/dev/null; do |
|
|
|
frameText=" [${FRAMES:i++%NUMBER_OR_FRAMES:1}] $MSG" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Print frame text. |
|
|
|
if [ "$TRAVIS" != "true" ]; then |
|
printf "%s\n" "$frameText" |
|
else |
|
printf "%s" "$frameText" |
|
fi |
|
|
|
sleep 0.2 |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Clear frame text. |
|
|
|
if [ "$TRAVIS" != "true" ]; then |
|
tput rc |
|
else |
|
printf "\r" |
|
fi |
|
|
|
done |
|
|
|
} |
|
|
|
execute() { |
|
|
|
local -r CMDS="$1" |
|
local -r MSG="$2" |
|
local -r TMP_FILE="$(mktemp /tmp/XXXXX)" |
|
|
|
local exitCode=0 |
|
local cmdsPID="" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# If the current process is ended, |
|
# also end all its subprocesses. |
|
|
|
set_trap "EXIT" "kill_all_subprocesses" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Execute commands in background |
|
|
|
eval "$CMDS" \ |
|
&> /dev/null \ |
|
2> "$TMP_FILE" & |
|
|
|
cmdsPID=$! |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Show a spinner if the commands |
|
# require more time to complete. |
|
|
|
show_spinner "$cmdsPID" "$CMDS" "$MSG" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Wait for the commands to no longer be executing |
|
# in the background, and then get their exit code. |
|
|
|
wait "$cmdsPID" &> /dev/null |
|
exitCode=$? |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
# Print output based on what happened. |
|
|
|
print_result $exitCode "$MSG" |
|
|
|
if [ $exitCode -ne 0 ]; then |
|
print_error_stream < "$TMP_FILE" |
|
fi |
|
|
|
rm -rf "$TMP_FILE" |
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|
|
|
return $exitCode |
|
|
|
} |
|
|
|
set_trap() { |
|
|
|
trap -p "$1" | grep "$2" &> /dev/null \ |
|
|| trap '$2' "$1" |
|
|
|
} |
|
|
|
print_error_stream() { |
|
while read -r line; do |
|
print_error "↳ ERROR: $line" |
|
done |
|
} |
|
|
|
print_error() { |
|
print_in_red " [✖] $1 $2\n" |
|
} |
|
|
|
print_success() { |
|
print_in_green " [✔] $1\n" |
|
} |
|
|
|
print_in_green() { |
|
print_in_color "$1" 2 |
|
} |
|
|
|
print_in_purple() { |
|
print_in_color "$1" 5 |
|
} |
|
|
|
print_in_red() { |
|
print_in_color "$1" 1 |
|
} |
|
|
|
print_in_color() { |
|
printf "%b" \ |
|
"$(tput setaf "$2" 2> /dev/null)" \ |
|
"$1" \ |
|
"$(tput sgr0 2> /dev/null)" |
|
} |
|
|
|
print_result() { |
|
|
|
if [ "$1" -eq 0 ]; then |
|
print_success "$2" |
|
else |
|
print_error "$2" |
|
fi |
|
|
|
return "$1" |
|
|
|
} |
|
|
|
fix_dpkg() { |
|
declare -a files=("/var/lib/dpkg/lock" "/var/cache/apt/archives/lock") |
|
|
|
for i in "${files[@]}" |
|
do |
|
# If there is a dpkg lock, then remove it. |
|
if [ -e "$i" ]; then |
|
sudo rm -rf "$i" &> /dev/null |
|
fi |
|
done |
|
} |
|
|
|
install_package() { |
|
|
|
declare -r PACKAGE="$2" |
|
declare -r PACKAGE_READABLE_NAME="$1" |
|
|
|
if ! package_is_installed "$PACKAGE"; then |
|
fix_dpkg |
|
execute "sudo apt-get install --allow-unauthenticated -qqy $PACKAGE" "$PACKAGE_READABLE_NAME" |
|
# suppress output ─┘│ |
|
# assume "yes" as the answer to all prompts ──┘ |
|
else |
|
print_success "$PACKAGE_READABLE_NAME" |
|
fi |
|
|
|
} |
|
|
|
package_is_installed() { |
|
dpkg -s "$1" &> /dev/null |
|
} |
|
|
|
update() { |
|
|
|
ask_for_sudo |
|
|
|
fix_dpkg |
|
|
|
# Resynchronize the package index files from their sources. |
|
|
|
execute \ |
|
"sudo apt-get update -qqy" \ |
|
"APT (update)" |
|
|
|
} |
|
|
|
upgrade() { |
|
|
|
ask_for_sudo |
|
|
|
fix_dpkg |
|
|
|
# Install the newest versions of all packages installed. |
|
|
|
execute \ |
|
"export DEBIAN_FRONTEND=\"noninteractive\" \ |
|
&& sudo apt-get -o Dpkg::Options::=\"--force-confnew\" upgrade -qqy" \ |
|
"APT (upgrade)" |
|
|
|
} |
|
|
|
|
|
main() { |
|
|
|
print_in_purple "\n Updating System\n\n" |
|
|
|
update |
|
upgrade |
|
|
|
printf "\n" |
|
|
|
} |
|
|
|
main |