2023-02-15 13:16:31 +01:00
#!/usr/bin/env bash
2025-11-01 09:14:49 +01:00
VERSION = "v0.7.4"
# ChangeNotes: New option -R to pull without recreation. Fixes: value too great error, legacy cleanups.
2023-02-02 22:02:41 +01:00
Github = "https://github.com/mag37/dockcheck"
2023-03-05 21:25:22 +01:00
RawUrl = "https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh"
2023-02-02 22:02:41 +01:00
2025-03-30 22:58:47 +02:00
set -uo pipefail
2025-03-30 13:31:34 +02:00
shopt -s nullglob
shopt -s failglob
2024-11-17 14:54:28 +01:00
# Variables for self updating
2023-03-04 19:56:26 +01:00
ScriptArgs = ( " $@ " )
ScriptPath = " $( readlink -f " $0 " ) "
ScriptWorkDir = " $( dirname " $ScriptPath " ) "
2023-01-18 11:50:00 +01:00
2025-05-25 12:26:13 -04:00
# Source helper functions
source_if_exists_or_fail( ) {
2025-07-01 16:19:15 -04:00
if [ [ -s " $1 " ] ] ; then
source " $1 "
[ [ " ${ DisplaySourcedFiles :- false } " = = true ] ] && echo " * sourced config: ${ 1 } "
return 0
else
return 1
fi
2025-05-25 12:26:13 -04:00
}
2025-02-26 21:23:15 +01:00
# User customizable defaults
2025-07-01 16:19:15 -04:00
source_if_exists_or_fail " ${ HOME } /.config/dockcheck.config " || source_if_exists_or_fail " ${ ScriptWorkDir } /dockcheck.config "
2025-02-26 21:23:15 +01:00
2024-11-17 14:54:28 +01:00
# Help Function
2023-01-19 12:09:29 +01:00
Help( ) {
2025-07-25 10:35:49 +02:00
echo "Syntax: dockcheck.sh [OPTION] [comma separated names to include]"
2025-05-03 10:20:32 +02:00
echo "Example: dockcheck.sh -y -x 10 -d 10 -e nextcloud,heimdall"
2023-02-09 12:03:27 +00:00
echo
echo "Options:"
echo "-a|y Automatic updates, without interaction."
2025-01-27 20:57:19 +01:00
echo "-c Exports metrics as prom file for the prometheus node_exporter. Provide the collector textfile directory."
2023-12-14 09:06:19 +01:00
echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower."
2023-12-23 20:47:23 +01:00
echo "-e X Exclude containers, separated by comma."
2025-05-11 20:50:09 +02:00
echo "-f Force stop+start stack after update. Caution: restarts once for every updated container within stack."
echo "-F Only compose up the specific container, not the whole compose stack (useful for master-compose structure)."
2023-12-23 20:47:23 +01:00
echo "-h Print this Help."
2024-01-06 00:11:33 +01:00
echo "-i Inform - send a preconfigured notification."
2025-05-25 18:39:34 +02:00
echo "-I Prints custom releasenote urls alongside each container with updates in CLI output (requires urls.list)."
2025-11-13 16:30:46 +01:00
echo "-k N Number of days to store image backups before pruning - this also enables the backup function."
2025-10-03 09:22:17 +02:00
echo "-l Only include containers with label set. See readme."
2025-04-10 12:03:03 +02:00
echo "-m Monochrome mode, no printf colour codes and hides progress bar."
2025-05-03 10:20:32 +02:00
echo "-M Prints custom releasenote urls as markdown (requires template support)."
2025-03-30 13:31:34 +02:00
echo "-n No updates; only checking availability without interaction."
2024-11-17 14:54:28 +01:00
echo "-p Auto-prune dangling images after update."
2025-11-01 09:14:49 +01:00
echo "-R Skip container recreation after pulling images."
2025-05-31 22:02:52 +02:00
echo "-r Allow checking for updates/updating images for docker run containers. Won't update the container."
2024-02-02 20:52:01 +01:00
echo "-s Include stopped containers in the check. (Logic: docker ps -a)."
2024-06-03 22:15:08 +02:00
echo "-t Set a timeout (in seconds) per container for registry checkups, 10 is default."
2025-04-10 12:03:03 +02:00
echo "-u Allow automatic self updates - caution as this will pull new code and autorun it."
2024-02-02 20:52:01 +01:00
echo "-v Prints current version."
2025-02-26 21:23:15 +01:00
echo "-x N Set max asynchronous subprocesses, 1 default, 0 to disable, 32+ tested."
2024-09-28 14:22:09 +02:00
echo
echo " Project source: $Github "
2023-02-09 12:03:27 +00:00
}
2023-01-19 12:09:29 +01:00
2025-03-30 13:31:34 +02:00
# Initialise variables
2025-06-24 09:16:48 -04:00
Timeout = ${ Timeout :- 10 }
MaxAsync = ${ MaxAsync :- 1 }
BarWidth = ${ BarWidth :- 50 }
AutoMode = ${ AutoMode :- false }
DontUpdate = ${ DontUpdate :- false }
AutoPrune = ${ AutoPrune :- false }
AutoSelfUpdate = ${ AutoSelfUpdate :- false }
OnlyLabel = ${ OnlyLabel :- false }
Notify = ${ Notify :- false }
ForceRestartStacks = ${ ForceRestartStacks :- false }
DRunUp = ${ DRunUp :- false }
MonoMode = ${ MonoMode :- false }
PrintReleaseURL = ${ PrintReleaseURL :- false }
PrintMarkdownURL = ${ PrintMarkdownURL :- false }
Stopped = ${ Stopped :- "" }
2025-03-30 13:31:34 +02:00
CollectorTextFileDirectory = ${ CollectorTextFileDirectory :- }
Exclude = ${ Exclude :- }
DaysOld = ${ DaysOld :- }
2025-11-13 16:30:46 +01:00
DaysKept = ${ DaysKept :- }
2025-06-24 09:16:48 -04:00
OnlySpecific = ${ OnlySpecific :- false }
SpecificContainer = ${ SpecificContainer :- "" }
2025-11-01 09:14:49 +01:00
SkipRecreate = ${ SkipRecreate :- false }
2025-03-30 13:31:34 +02:00
Excludes = ( )
GotUpdates = ( )
NoUpdates = ( )
GotErrors = ( )
SelectedUpdates = ( )
2025-06-24 09:16:48 -04:00
CurlArgs = " --retry ${ CurlRetryCount : =3 } --retry-delay ${ CurlRetryDelay : =1 } --connect-timeout ${ CurlConnectTimeout : =5 } -sf "
2025-03-30 13:31:34 +02:00
regbin = ""
jqbin = ""
# Colours
2023-12-19 20:24:34 +01:00
c_red = "\033[0;31m"
c_green = "\033[0;32m"
c_yellow = "\033[0;33m"
c_blue = "\033[0;34m"
c_teal = "\033[0;36m"
c_reset = "\033[0m"
2025-11-13 20:18:25 +01:00
# Timestamps
RunTimestamp = $( date +'%Y-%m-%d_%H%M' )
RunEpoch = $( date +'%s' )
2025-11-13 16:30:46 +01:00
while getopts "ayfFhiIlmMnprsuvc:e:d:k:t:x:R" options; do
2023-02-09 12:03:27 +00:00
case " ${ options } " in
2025-03-30 13:31:34 +02:00
a| y) AutoMode = true ; ;
c) CollectorTextFileDirectory = " ${ OPTARG } " ; ;
2025-04-10 12:03:03 +02:00
d) DaysOld = ${ OPTARG } ; ;
e) Exclude = ${ OPTARG } ; ;
2024-01-18 20:59:55 +01:00
f) ForceRestartStacks = true ; ;
2025-05-11 20:50:09 +02:00
F) OnlySpecific = true ; ;
2025-03-30 13:31:34 +02:00
i) Notify = true ; ;
2025-04-10 12:03:03 +02:00
I) PrintReleaseURL = true ; ;
2025-11-13 16:30:46 +01:00
k) DaysKept = " ${ OPTARG } " ; ;
2025-04-10 12:03:03 +02:00
l) OnlyLabel = true ; ;
m) MonoMode = true ; ;
2025-05-02 20:08:04 +02:00
M) PrintMarkdownURL = true ; ;
2025-04-10 12:03:03 +02:00
n) DontUpdate = true; AutoMode = true; ;
p) AutoPrune = true ; ;
2025-11-01 09:14:49 +01:00
R) SkipRecreate = true ; ;
2025-04-10 12:03:03 +02:00
r) DRunUp = true ; ;
2023-12-13 19:50:58 +01:00
s) Stopped = "-a" ; ;
2024-08-17 08:50:39 -04:00
t) Timeout = " ${ OPTARG } " ; ;
2025-04-10 12:03:03 +02:00
u) AutoSelfUpdate = true ; ;
2025-03-30 13:31:34 +02:00
v) printf "%s\n" " $VERSION " ; exit 0 ; ;
2025-02-26 21:23:15 +01:00
x) MaxAsync = ${ OPTARG } ; ;
2025-03-30 13:31:34 +02:00
h| *) Help; exit 2 ; ;
2023-02-09 12:03:27 +00:00
esac
done
shift " $(( OPTIND-1)) "
2023-01-19 12:09:29 +01:00
2025-07-25 10:35:49 +02:00
# Set $1 to a variable for name filtering later, rewriting if multiple
2025-03-30 13:31:34 +02:00
SearchName = " ${ 1 :- } "
2025-07-25 10:35:49 +02:00
if [ [ ! -z " $SearchName " ] ] ; then
SearchName = " ^( ${ SearchName //,/| } ) $"
fi
2025-03-30 13:31:34 +02:00
2025-06-24 09:16:48 -04:00
# Check if there's a new release of the script
LatestSnippet = " $( curl ${ CurlArgs } -r 0-200 " $RawUrl " || printf "undefined" ) "
LatestRelease = " $( echo " ${ LatestSnippet } " | sed -n "/VERSION/s/VERSION=//p" | tr -d '"' ) "
LatestChanges = " $( echo " ${ LatestSnippet } " | sed -n "/ChangeNotes/s/# ChangeNotes: //p" ) "
2025-05-25 12:26:13 -04:00
# Basic notify configuration check
if [ [ " ${ Notify } " = = true ] ] && [ [ ! -s " ${ ScriptWorkDir } /notify.sh " ] ] && [ [ -z " ${ NOTIFY_CHANNELS :- } " ] ] ; then
printf "Using v2 notifications with -i flag passed but no notify channels configured in dockcheck.config. This will result in no notifications being sent.\n"
fi
2025-03-30 13:31:34 +02:00
# Setting up options and sourcing functions
if [ [ " $DontUpdate " = = true ] ] ; then AutoMode = true; fi
2025-04-10 12:03:03 +02:00
if [ [ " $MonoMode " = = true ] ] ; then declare c_{ red,green,yellow,blue,teal,reset} = "" ; fi
2025-03-30 13:31:34 +02:00
if [ [ " $Notify " = = true ] ] ; then
2025-05-25 12:26:13 -04:00
source_if_exists_or_fail " ${ ScriptWorkDir } /notify.sh " || source_if_exists_or_fail " ${ ScriptWorkDir } /notify_templates/notify_v2.sh " || Notify = false
2025-03-30 13:31:34 +02:00
fi
if [ [ -n " $Exclude " ] ] ; then
IFS = ',' read -ra Excludes <<< " $Exclude "
unset IFS
fi
if [ [ -n " $DaysOld " ] ] ; then
if ! [ [ $DaysOld = ~ ^[ 0-9] +$ ] ] ; then
printf "Days -d argument given (%s) is not a number.\n" " $DaysOld "
exit 2
fi
fi
2025-11-13 16:30:46 +01:00
if [ [ -n " $DaysKept " ] ] ; then
if ! [ [ $DaysKept = ~ ^[ 0-9] +$ ] ] ; then
printf "-k argument given (%s) is not a number.\n" " $DaysKept "
exit 2
fi
fi
2025-03-30 13:31:34 +02:00
if [ [ -n " $CollectorTextFileDirectory " ] ] ; then
if ! [ [ -d $CollectorTextFileDirectory ] ] ; then
printf "The directory (%s) does not exist.\n" " $CollectorTextFileDirectory "
exit 2
else
source " ${ ScriptWorkDir } /addons/prometheus/prometheus_collector.sh "
fi
fi
2025-05-25 12:26:13 -04:00
exec_if_exists( ) {
if [ [ $( type -t $1 ) = = function ] ] ; then " $@ " ; fi
}
exec_if_exists_or_fail( ) {
[ [ $( type -t $1 ) = = function ] ] && " $@ "
}
2023-03-04 19:56:26 +01:00
self_update_curl( ) {
cp " $ScriptPath " " $ScriptPath " .bak
2025-03-30 13:31:34 +02:00
if command -v curl & >/dev/null; then
2025-06-24 09:16:48 -04:00
curl ${ CurlArgs } -L $RawUrl > " $ScriptPath " ; chmod +x " $ScriptPath " || { printf "ERROR: Failed to curl updated Dockcheck.sh script. Skipping update.\n" ; return 1; }
2025-04-10 12:03:03 +02:00
printf "\n%b---%b starting over with the updated version %b---%b\n" " $c_yellow " " $c_teal " " $c_yellow " " $c_reset "
2024-01-31 21:33:14 +01:00
exec " $ScriptPath " " ${ ScriptArgs [@] } " # run the new script with old arguments
2024-11-17 14:54:28 +01:00
exit 1 # Exit the old instance
2025-03-30 13:31:34 +02:00
elif command -v wget & >/dev/null; then
2025-05-25 18:39:34 +02:00
wget --waitretry= 1 --timeout= 15 -t 10 $RawUrl -O " $ScriptPath " ; chmod +x " $ScriptPath "
2025-04-10 12:03:03 +02:00
printf "\n%b---%b starting over with the updated version %b---%b\n" " $c_yellow " " $c_teal " " $c_yellow " " $c_reset "
2023-03-04 19:56:26 +01:00
exec " $ScriptPath " " ${ ScriptArgs [@] } " # run the new script with old arguments
2025-03-30 13:31:34 +02:00
exit 0 # exit the old instance
2023-03-04 19:56:26 +01:00
else
2025-04-10 12:03:03 +02:00
printf "\n%bcurl/wget not available %b- download the update manually: %b%s %b\n" " $c_red " " $c_reset " " $c_teal " " $Github " " $c_reset "
2023-03-04 19:56:26 +01:00
fi
}
2024-01-31 21:33:14 +01:00
self_update( ) {
2025-04-10 12:03:03 +02:00
cd " $ScriptWorkDir " || { printf "%bPath error,%b skipping update.\n" " $c_red " " $c_reset " ; return ; }
2025-03-30 13:31:34 +02:00
if command -v git & >/dev/null && [ [ " $( git ls-remote --get-url 2>/dev/null) " = ~ .*"mag37/dockcheck" .* ] ] ; then
2024-01-31 21:33:14 +01:00
printf "\n%s\n" "Pulling the latest version."
2025-04-10 12:03:03 +02:00
git pull --force || { printf "%bGit error,%b manually pull/clone.\n" " $c_red " " $c_reset " ; return ; }
2024-01-31 21:33:14 +01:00
printf "\n%s\n" "--- starting over with the updated version ---"
2025-04-10 12:03:03 +02:00
cd - || { printf "%bPath error.%b\n" " $c_red " ; return ; }
2024-01-31 21:33:14 +01:00
exec " $ScriptPath " " ${ ScriptArgs [@] } " # run the new script with old arguments
2025-03-30 13:31:34 +02:00
exit 0 # exit the old instance
2024-01-31 21:33:14 +01:00
else
2025-04-10 12:03:03 +02:00
cd - || { printf "%bPath error.%b\n" " $c_red " ; return ; }
2024-01-31 21:33:14 +01:00
self_update_curl
2023-03-04 19:56:26 +01:00
fi
2023-03-05 14:08:56 +01:00
}
choosecontainers( ) {
2025-03-30 13:31:34 +02:00
while [ [ -z " ${ ChoiceClean :- } " ] ] ; do
2023-03-05 14:08:56 +01:00
read -r -p "Enter number(s) separated by comma, [a] for all - [q] to quit: " Choice
2025-03-30 13:31:34 +02:00
if [ [ " $Choice " = ~ [ qQnN] ] ] ; then
2023-03-05 14:08:56 +01:00
exit 0
2025-03-30 13:31:34 +02:00
elif [ [ " $Choice " = ~ [ aAyY] ] ] ; then
2023-03-05 14:08:56 +01:00
SelectedUpdates = ( " ${ GotUpdates [@] } " )
ChoiceClean = ${ Choice //[,. : ;]/ }
else
ChoiceClean = ${ Choice //[,. : ;]/ }
2025-03-30 13:31:34 +02:00
for CC in $ChoiceClean ; do
2025-11-01 09:14:49 +01:00
CC = $(( 10# $CC )) # Base 10 interpretation to strip leading zeroes
2025-03-30 13:31:34 +02:00
if [ [ " $CC " -lt 1 || " $CC " -gt $UpdCount ] ] ; then # Reset choice if out of bounds
echo " Number not in list: $CC " ; unset ChoiceClean; break 1
2023-03-05 14:08:56 +01:00
else
SelectedUpdates += ( " ${ GotUpdates [ $CC -1] } " )
fi
done
fi
done
}
2023-12-13 19:50:58 +01:00
datecheck( ) {
2025-11-13 17:29:53 +01:00
ImageDate = " $1 "
DaysMax = " $2 "
2025-02-13 21:39:29 +01:00
ImageEpoch = $( date -d " $ImageDate " +%s 2>/dev/null) || ImageEpoch = $( date -f "%Y-%m-%d" -j " $ImageDate " +%s)
2025-11-13 17:29:53 +01:00
ImageAge = $(( ( RunEpoch - ImageEpoch ) / 86400 ))
if [ [ " $ImageAge " -gt " $DaysMax " ] ] ; then
2023-12-13 19:50:58 +01:00
return 0
else
return 1
fi
}
2024-01-17 20:26:18 +01:00
progress_bar( ) {
QueCurrent = " $1 "
QueTotal = " $2 "
2025-03-30 13:31:34 +02:00
BarWidth = ${ BarWidth :- 50 }
2024-02-01 20:19:04 +01:00
( ( Percent = 100*QueCurrent/QueTotal) )
2025-03-30 13:31:34 +02:00
( ( Complete = BarWidth*Percent/100) )
( ( Left = BarWidth-Complete) ) || true # to not throw error when result is 0
2024-01-17 20:26:18 +01:00
BarComplete = $( printf " % ${ Complete } s " | tr " " "#" )
BarLeft = $( printf " % ${ Left } s " | tr " " "-" )
2025-03-30 13:31:34 +02:00
if [ [ " $QueTotal " != " $QueCurrent " ] ] ; then
printf "\r[%s%s] %s/%s " " $BarComplete " " $BarLeft " " $QueCurrent " " $QueTotal "
else
printf "\r[%b%s%b] %s/%s \n" " $c_teal " " $BarComplete " " $c_reset " " $QueCurrent " " $QueTotal "
fi
2024-01-17 20:26:18 +01:00
}
2023-12-13 19:50:58 +01:00
2024-11-17 14:54:28 +01:00
# Function to add user-provided urls to releasenotes
2025-02-07 20:47:01 +01:00
releasenotes( ) {
2025-05-02 21:40:24 +02:00
unset Updates
2025-03-30 13:31:34 +02:00
for update in " ${ GotUpdates [@] } " ; do
2024-10-04 23:19:41 +02:00
found = false
while read -r container url; do
2025-05-02 20:08:04 +02:00
if [ [ " $update " = = " $container " ] ] && [ [ " $PrintMarkdownURL " = = true ] ] ; then Updates += ( " - [ $update ]( $url ) " ) ; found = true;
elif [ [ " $update " = = " $container " ] ] ; then Updates += ( " $update -> $url " ) ; found = true;
fi
2025-03-30 13:31:34 +02:00
done < " ${ ScriptWorkDir } /urls.list "
2025-05-02 20:08:04 +02:00
if [ [ " $found " = = false ] ] && [ [ " $PrintMarkdownURL " = = true ] ] ; then Updates += ( " - $update -> url missing " ) ;
elif [ [ " $found " = = false ] ] ; then Updates += ( " $update -> url missing " ) ;
else continue ;
fi
2024-10-04 23:19:41 +02:00
done
2024-10-04 14:39:51 +02:00
}
2024-11-22 11:00:45 +01:00
# Static binary downloader for dependencies
binary_downloader( ) {
BinaryName = " $1 "
BinaryUrl = " $2 "
2025-02-13 21:39:29 +01:00
case " $( uname -m) " in
2024-11-22 11:00:45 +01:00
x86_64| amd64) architecture = "amd64" ; ;
arm64| aarch64) architecture = "arm64" ; ;
2025-03-30 13:31:34 +02:00
*) printf "\n%bArchitecture not supported, exiting.%b\n" " $c_red " " $c_reset " ; exit 1; ;
2024-11-22 11:00:45 +01:00
esac
GetUrl = " ${ BinaryUrl /TEMP/ " $architecture " } "
2025-06-24 09:16:48 -04:00
if command -v curl & >/dev/null; then curl ${ CurlArgs } -L " $GetUrl " > " $ScriptWorkDir / $BinaryName " || { printf "ERROR: Failed to curl binary dependency. Rerun the script to retry.\n" ; exit 1; }
2025-05-25 18:39:34 +02:00
elif command -v wget & >/dev/null; then wget --waitretry= 1 --timeout= 15 -t 10 " $GetUrl " -O " $ScriptWorkDir / $BinaryName " ;
2025-05-12 15:54:34 +02:00
else printf "\n%bcurl/wget not available - get %s manually from the repo link, exiting.%b" " $c_red " " $BinaryName " " $c_reset " ; exit 1;
2024-11-22 11:00:45 +01:00
fi
[ [ -f " $ScriptWorkDir / $BinaryName " ] ] && chmod +x " $ScriptWorkDir / $BinaryName "
}
distro_checker( ) {
2025-05-02 21:40:24 +02:00
isRoot = false
[ [ ${ EUID :- } = = 0 ] ] && isRoot = true
if [ [ -f /etc/alpine-release ] ] ; then
[ [ " $isRoot " = = true ] ] && PkgInstaller = "apk add" || PkgInstaller = "doas apk add"
elif [ [ -f /etc/arch-release ] ] ; then
[ [ " $isRoot " = = true ] ] && PkgInstaller = "pacman -S" || PkgInstaller = "sudo pacman -S"
elif [ [ -f /etc/debian_version ] ] ; then
2025-05-25 18:39:34 +02:00
[ [ " $isRoot " = = true ] ] && PkgInstaller = "apt-get install" || PkgInstaller = "sudo apt-get install"
2025-05-02 21:40:24 +02:00
elif [ [ -f /etc/redhat-release ] ] ; then
[ [ " $isRoot " = = true ] ] && PkgInstaller = "dnf install" || PkgInstaller = "sudo dnf install"
elif [ [ -f /etc/SuSE-release ] ] ; then
[ [ " $isRoot " = = true ] ] && PkgInstaller = "zypper install" || PkgInstaller = "sudo zypper install"
2025-03-30 13:31:34 +02:00
elif [ [ $( uname -s) = = "Darwin" ] ] ; then PkgInstaller = "brew install"
else PkgInstaller = "ERROR" ; printf "\n%bNo distribution could be determined%b, falling back to static binary.\n" " $c_yellow " " $c_reset "
2024-11-22 11:00:45 +01:00
fi
}
2025-02-17 22:23:10 +01:00
# Dependency check + installer function
dependency_check( ) {
2025-02-17 22:38:14 +01:00
AppName = " $1 "
AppVar = " $2 "
AppUrl = " $3 "
2025-03-30 13:31:34 +02:00
if command -v " $AppName " & >/dev/null; then export " $AppVar " = " $AppName " ;
elif [ [ -f " $ScriptWorkDir / $AppName " ] ] ; then export " $AppVar " = " $ScriptWorkDir / $AppName " ;
2025-02-17 22:38:14 +01:00
else
2025-05-12 15:54:34 +02:00
printf "\nRequired dependency %b'%s'%b missing, do you want to install it?\n" " $c_teal " " $AppName " " $c_reset "
2025-02-17 22:38:14 +01:00
read -r -p "y: With packagemanager (sudo). / s: Download static binary. y/s/[n] " GetBin
GetBin = ${ GetBin :- no } # set default to no if nothing is given
2025-03-30 13:31:34 +02:00
if [ [ " $GetBin " = ~ [ yYsS] ] ] ; then
2025-02-17 22:38:14 +01:00
[ [ " $GetBin " = ~ [ yY] ] ] && distro_checker
2025-03-30 13:31:34 +02:00
if [ [ -n " ${ PkgInstaller :- } " && " ${ PkgInstaller :- } " != "ERROR" ] ] ; then
2025-02-18 08:15:52 +01:00
[ [ $( uname -s) = = "Darwin" && " $AppName " = = "regctl" ] ] && AppName = "regclient"
2025-03-30 22:17:15 +02:00
if $PkgInstaller " $AppName " ; then
AppName = " $1 "
2025-03-31 07:22:22 +02:00
export " $AppVar " = " $AppName "
2025-03-30 22:17:15 +02:00
printf "\n%b%b installed.%b\n" " $c_green " " $AppName " " $c_reset "
2025-03-31 07:22:22 +02:00
else
2025-04-10 12:03:03 +02:00
PkgInstaller = "ERROR"
2025-03-30 22:17:15 +02:00
printf "\n%bPackagemanager install failed%b, falling back to static binary.\n" " $c_yellow " " $c_reset "
2025-02-17 22:38:14 +01:00
fi
fi
2025-04-10 12:03:03 +02:00
if [ [ " $GetBin " = ~ [ sS] ] ] || [ [ " $PkgInstaller " = = "ERROR" ] ] ; then
2025-02-17 22:38:14 +01:00
binary_downloader " $AppName " " $AppUrl "
2025-05-02 21:40:24 +02:00
[ [ -f " $ScriptWorkDir / $AppName " ] ] && { export " $AppVar " = " $ScriptWorkDir / $1 " && printf "\n%b%s downloaded.%b\n" " $c_green " " $AppName " " $c_reset " ; }
2025-02-17 22:38:14 +01:00
fi
2025-03-30 13:31:34 +02:00
else printf "\n%bDependency missing, exiting.%b\n" " $c_red " " $c_reset " ; exit 1;
2024-11-22 11:00:45 +01:00
fi
fi
2025-02-17 22:38:14 +01:00
# Final check if binary is correct
[ [ " $1 " = = "jq" ] ] && VerFlag = "--version"
[ [ " $1 " = = "regctl" ] ] && VerFlag = "version"
2025-03-30 13:31:34 +02:00
${ !AppVar } " $VerFlag " & > /dev/null || { printf "%s\n" " $AppName is not working - try to remove it and re-download it, exiting. " ; exit 1; }
2025-02-17 22:23:10 +01:00
}
2024-11-22 11:00:45 +01:00
2025-05-29 16:43:34 -04:00
dependency_check "regctl" "regbin" "https://github.com/regclient/regclient/releases/latest/download/regctl-linux-TEMP"
dependency_check "jq" "jqbin" "https://github.com/jqlang/jq/releases/latest/download/jq-linux-TEMP"
2025-10-03 09:22:17 +02:00
# Numbered List function - pads with zero
2025-05-02 21:40:24 +02:00
list_options( ) {
2025-10-03 09:22:17 +02:00
local total = " ${# Updates [@] } "
[ [ ${# total } < 2 ] ] && local pads = 2 || local pads = " ${# total } "
local num = 1
2025-04-10 12:03:03 +02:00
for update in " ${ Updates [@] } " ; do
2025-10-06 10:18:38 +02:00
printf "%0*d - %s\n" " $pads " " $num " " $update "
2025-03-30 13:31:34 +02:00
( ( num++) )
done
2025-04-10 12:03:03 +02:00
}
2025-03-30 13:31:34 +02:00
# Version check & initiate self update
2025-11-13 06:17:25 +01:00
if [ [ " $LatestSnippet " != "undefined" ] ] ; then
2025-06-24 09:16:48 -04:00
if [ [ " $VERSION " != " $LatestRelease " ] ] ; then
printf "New version available! %b%s%b ⇒ %b%s%b \n Change Notes: %s \n" " $c_yellow " " $VERSION " " $c_reset " " $c_green " " $LatestRelease " " $c_reset " " $LatestChanges "
if [ [ " $AutoMode " = = false ] ] ; then
read -r -p "Would you like to update? y/[n]: " SelfUpdate
[ [ " $SelfUpdate " = ~ [ yY] ] ] && self_update
elif [ [ " $AutoMode " = = true ] ] && [ [ " $AutoSelfUpdate " = = true ] ] ; then self_update;
else
[ [ " $Notify " = = true ] ] && { exec_if_exists_or_fail dockcheck_notification " $VERSION " " $LatestRelease " " $LatestChanges " || printf "Could not source notification function.\n" ; }
fi
2025-03-30 13:31:34 +02:00
fi
2025-06-24 09:16:48 -04:00
else
printf "ERROR: Failed to curl latest Dockcheck.sh release version.\n"
2025-03-30 13:31:34 +02:00
fi
2025-05-25 12:26:13 -04:00
# Version check for notify templates
2025-05-26 07:27:51 +02:00
[ [ " $Notify " = = true ] ] && [ [ ! -s " ${ ScriptWorkDir } /notify.sh " ] ] && { exec_if_exists_or_fail notify_update_notification || printf "Could not source notify notification function.\n" ; }
2025-05-25 12:26:13 -04:00
2024-11-17 14:54:28 +01:00
# Check docker compose binary
2025-05-02 21:40:24 +02:00
docker info & >/dev/null || { printf "\n%bYour current user does not have permissions to the docker socket - may require root / docker group. Exiting.%b\n" " $c_red " " $c_reset " ; exit 1; }
2025-03-30 13:31:34 +02:00
if docker compose version & >/dev/null; then DockerBin = "docker compose" ;
elif docker-compose -v & >/dev/null; then DockerBin = "docker-compose" ;
elif docker -v & >/dev/null; then
2023-02-10 21:06:12 +01:00
printf "%s\n" "No docker compose binary available, using plain docker (Not recommended!)"
printf "%s\n" "'docker run' will ONLY update images, not the container itself."
2023-02-09 12:03:27 +00:00
else
2023-02-10 21:06:12 +01:00
printf "%s\n" "No docker binaries available, exiting."
2023-02-15 13:16:31 +01:00
exit 1
2023-02-09 12:03:27 +00:00
fi
2023-01-30 10:08:13 +01:00
2024-11-17 14:54:28 +01:00
# Listing typed exclusions
2025-03-30 22:58:47 +02:00
if [ [ -n ${ Excludes [*] :- } ] ] ; then
2024-02-01 20:19:04 +01:00
printf "\n%bExcluding these names:%b\n" " $c_blue " " $c_reset "
2023-06-21 20:09:31 +02:00
printf "%s\n" " ${ Excludes [@] } "
printf "\n"
fi
2024-01-17 20:26:18 +01:00
# Variables for progress_bar function
2024-11-18 21:04:02 +01:00
ContCount = $( docker ps $Stopped --filter " name= $SearchName " --format '{{.Names}}' | wc -l)
2024-01-17 20:26:18 +01:00
RegCheckQue = 0
2024-11-17 14:54:28 +01:00
# Testing and setting timeout binary
2025-03-30 13:31:34 +02:00
t_out = $( command -v timeout || echo "" )
2024-06-16 07:51:32 +02:00
if [ [ $t_out ] ] ; then
2025-03-30 13:31:34 +02:00
t_out = $( realpath " $t_out " 2>/dev/null || readlink -f " $t_out " )
2024-06-16 07:51:32 +02:00
if [ [ $t_out = ~ "busybox" ] ] ; then
t_out = " timeout ${ Timeout } "
else t_out = " timeout --foreground ${ Timeout } "
fi
else t_out = ""
fi
2025-02-21 17:57:09 +01:00
check_image( ) {
i = " $1 "
local Excludes = ( $Excludes_string )
2025-03-30 13:31:34 +02:00
for e in " ${ Excludes [@] } " ; do
2025-02-24 21:49:19 +01:00
if [ [ " $i " = = " $e " ] ] ; then
2025-03-30 13:31:34 +02:00
printf "%s\n" " Skip $i "
2025-02-24 21:49:19 +01:00
return
fi
done
2025-02-19 16:28:11 +01:00
2025-05-11 20:50:09 +02:00
# Skipping non-compose containers unless option is set
ContLabels = $( docker inspect " $i " --format '{{json .Config.Labels}}' )
ContPath = $( $jqbin -r '."com.docker.compose.project.working_dir"' <<< " $ContLabels " )
[ [ " $ContPath " = = "null" ] ] && ContPath = ""
if [ [ -z " $ContPath " ] ] && [ [ " $DRunUp " = = false ] ] ; then
printf "%s\n" " NoUpdates ! $i - not checked, no compose file "
return
fi
2025-10-03 09:22:17 +02:00
# Checking if Label Only -option is set, and if container got the label
ContUpdateLabel = $( $jqbin -r '."mag37.dockcheck.update"' <<< " $ContLabels " )
[ [ " $ContUpdateLabel " = = "null" ] ] && ContUpdateLabel = ""
[ [ " $OnlyLabel " = = true ] ] && { [ [ " $ContUpdateLabel " != true ] ] && { echo " Skip $i " ; return ; } }
2025-05-11 20:50:09 +02:00
2025-02-21 17:57:09 +01:00
local NoUpdates GotUpdates GotErrors
2025-01-22 16:07:50 +01:00
ImageId = $( docker inspect " $i " --format= '{{.Image}}' )
2023-02-09 12:03:27 +00:00
RepoUrl = $( docker inspect " $i " --format= '{{.Config.Image}}' )
2025-01-22 16:07:50 +01:00
LocalHash = $( docker image inspect " $ImageId " --format '{{.RepoDigests}}' )
2025-02-19 16:28:11 +01:00
2024-11-17 14:54:28 +01:00
# Checking for errors while setting the variable
2025-03-30 13:31:34 +02:00
if RegHash = $( $t_out " $regbin " -v error image digest --list " $RepoUrl " 2>& 1) ; then
2025-06-24 09:16:48 -04:00
if [ [ " $LocalHash " = = *" $RegHash " * ] ] ; then
2025-03-30 13:31:34 +02:00
printf "%s\n" " NoUpdates $i "
2024-08-17 08:50:39 -04:00
else
2025-11-13 17:29:53 +01:00
if [ [ -n " ${ DaysOld :- } " ] ] && ! datecheck $( " $regbin " -v error image inspect " $RepoUrl " --format= '{{.Created}}' | cut -d" " -f1) " $DaysOld " ; then
2025-03-30 13:31:34 +02:00
printf "%s\n" " NoUpdates + $i ${ ImageAge } d "
2024-08-17 08:50:39 -04:00
else
2025-03-30 13:31:34 +02:00
printf "%s\n" " GotUpdates $i "
2023-12-13 19:50:58 +01:00
fi
fi
2023-02-09 12:03:27 +00:00
else
2025-03-30 13:31:34 +02:00
printf "%s\n" " GotErrors $i - ${ RegHash } " # Reghash contains an error code here
2023-02-09 12:03:27 +00:00
fi
2025-02-21 17:57:09 +01:00
}
# Make required functions and variables available to subprocesses
export -f check_image datecheck
2025-03-31 07:22:22 +02:00
export Excludes_string = " ${ Excludes [*] :- } " # Can only export scalar variables
2025-10-03 09:22:17 +02:00
export t_out regbin RepoUrl DaysOld DRunUp jqbin OnlyLabel
2025-02-21 17:57:09 +01:00
2025-03-16 20:54:34 +01:00
# Check for POSIX xargs with -P option, fallback without async
2025-02-26 21:23:15 +01:00
if ( echo "test" | xargs -P 2 >/dev/null 2>& 1) && [ [ " $MaxAsync " != 0 ] ] ; then
2025-02-24 21:49:19 +01:00
XargsAsync = " -P $MaxAsync "
else
XargsAsync = ""
2025-05-02 21:40:24 +02:00
[ [ " $MaxAsync " != 0 ] ] && printf "%bMissing POSIX xargs, consider installing 'findutils' for asynchronous lookups.%b\n" " $c_yellow " " $c_reset "
2025-02-24 21:49:19 +01:00
fi
2025-02-21 17:57:09 +01:00
# Asynchronously check the image-hash of every running container VS the registry
while read -r line; do
( ( RegCheckQue += 1) )
2025-04-10 12:03:03 +02:00
if [ [ " $MonoMode " = = false ] ] ; then progress_bar " $RegCheckQue " " $ContCount " ; fi
2025-02-21 17:57:09 +01:00
Got = ${ line %% * } # Extracts the first word (NoUpdates, GotUpdates, GotErrors)
item = ${ line #* }
case " $Got " in
NoUpdates) NoUpdates += ( " $item " ) ; ;
GotUpdates) GotUpdates += ( " $item " ) ; ;
GotErrors) GotErrors += ( " $item " ) ; ;
Skip) ; ;
*) echo " Error! Unexpected output from subprocess: ${ line } " ; ;
esac
done < <( \
docker ps $Stopped --filter " name= $SearchName " --format '{{.Names}}' | \
2025-03-30 13:31:34 +02:00
xargs $XargsAsync -I { } bash -c 'check_image "{}"' \
2025-02-21 17:57:09 +01:00
)
2023-01-18 11:50:00 +01:00
2025-10-03 09:22:17 +02:00
[ [ " $OnlyLabel " = = true ] ] && printf "\n%bLabel option active:%b Only checking containers with labels set.\n" " $c_blue " " $c_reset "
2024-11-17 14:54:28 +01:00
# Sort arrays alphabetically
2024-06-12 20:50:10 +02:00
IFS = $'\n'
2025-03-31 11:23:21 +02:00
NoUpdates = ( $( sort <<< " ${ NoUpdates [*] :- } " ) )
GotUpdates = ( $( sort <<< " ${ GotUpdates [*] :- } " ) )
2024-06-12 20:50:10 +02:00
unset IFS
2025-01-31 23:52:10 +01:00
# Run the prometheus exporter function
2025-03-30 13:31:34 +02:00
if [ [ -n " ${ CollectorTextFileDirectory :- } " ] ] ; then
2025-05-25 12:26:13 -04:00
exec_if_exists_or_fail prometheus_exporter ${# NoUpdates [@] } ${# GotUpdates [@] } ${# GotErrors [@] } || printf "%s\n" "Could not source prometheus exporter function."
2025-01-27 20:57:19 +01:00
fi
2024-11-17 14:54:28 +01:00
# Define how many updates are available
2023-02-15 13:16:31 +01:00
UpdCount = " ${# GotUpdates [@] } "
2023-01-30 10:08:13 +01:00
2024-11-17 14:54:28 +01:00
# List what containers got updates or not
2025-03-31 11:23:21 +02:00
if [ [ -n ${ NoUpdates [*] :- } ] ] ; then
2023-12-19 20:24:34 +01:00
printf "\n%bContainers on latest version:%b\n" " $c_green " " $c_reset "
2023-01-18 11:50:00 +01:00
printf "%s\n" " ${ NoUpdates [@] } "
fi
2025-03-31 11:23:21 +02:00
if [ [ -n ${ GotErrors [*] :- } ] ] ; then
2024-11-17 14:54:28 +01:00
printf "\n%bContainers with errors, won't get updated:%b\n" " $c_red " " $c_reset "
2023-01-20 12:47:17 +01:00
printf "%s\n" " ${ GotErrors [@] } "
2024-04-22 21:02:13 +02:00
printf "%binfo:%b 'unauthorized' often means not found in a public registry.\n" " $c_blue " " $c_reset "
2023-01-20 12:47:17 +01:00
fi
2025-03-31 11:23:21 +02:00
if [ [ -n ${ GotUpdates [*] :- } ] ] ; then
2025-05-02 21:40:24 +02:00
printf "\n%bContainers with updates available:%b\n" " $c_yellow " " $c_reset "
if [ [ -s " $ScriptWorkDir /urls.list " ] ] && [ [ " $PrintReleaseURL " = = true ] ] ; then releasenotes; else Updates = ( " ${ GotUpdates [@] } " ) ; fi
[ [ " $AutoMode " = = false ] ] && list_options || printf "%s\n" " ${ Updates [@] } "
2025-05-25 12:26:13 -04:00
[ [ " $Notify " = = true ] ] && { exec_if_exists_or_fail send_notification " ${ GotUpdates [@] } " || printf "\nCould not source notification function.\n" ; }
2025-09-15 05:25:23 -04:00
else
[ [ " $Notify " = = true ] ] && [ [ ! -s " ${ ScriptWorkDir } /notify.sh " ] ] && { exec_if_exists_or_fail send_notification " ${ GotUpdates [@] } " || printf "\nCould not source notification function.\n" ; }
2023-01-30 10:08:13 +01:00
fi
2023-01-18 11:50:00 +01:00
2024-11-17 14:54:28 +01:00
# Optionally get updates if there's any
2025-03-30 13:31:34 +02:00
if [ [ -n " ${ GotUpdates :- } " ] ] ; then
if [ [ " $AutoMode " = = false ] ] ; then
2024-11-17 14:54:28 +01:00
printf "\n%bChoose what containers to update.%b\n" " $c_teal " " $c_reset "
choosecontainers
2023-01-30 10:08:13 +01:00
else
SelectedUpdates = ( " ${ GotUpdates [@] } " )
fi
2025-03-30 13:31:34 +02:00
if [ [ " $DontUpdate " = = false ] ] ; then
2025-05-11 20:50:09 +02:00
printf "\n%bUpdating container(s):%b\n" " $c_blue " " $c_reset "
printf "%s\n" " ${ SelectedUpdates [@] } "
2023-06-27 19:34:20 +02:00
NumberofUpdates = " ${# SelectedUpdates [@] } "
2025-05-11 20:50:09 +02:00
2023-06-27 19:34:20 +02:00
CurrentQue = 0
2025-05-11 20:50:09 +02:00
for i in " ${ SelectedUpdates [@] } " ; do
( ( CurrentQue += 1) )
printf "\n%bNow updating (%s/%s): %b%s%b\n" " $c_teal " " $CurrentQue " " $NumberofUpdates " " $c_blue " " $i " " $c_reset "
2025-11-13 17:49:52 +01:00
# ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}')
# ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
# ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels")
ContConfig = $( docker inspect " $i " --format '{{json .}}' )
ContImage = $( $jqbin -r '."Config"."Image"' <<< " $ContConfig " ) # OLD? Remove if replaced with ContFull
ImageId = $( $jqbin -r '."Image"' <<< " $ContConfig " )
ContFull = $( docker image inspect " $ImageId " --format "{{index .RepoTags 0}}" )
ContPath = $( $jqbin -r '."Config"."Labels"."com.docker.compose.project.working_dir"' <<< " $ContConfig " )
2025-05-11 20:50:09 +02:00
[ [ " $ContPath " = = "null" ] ] && ContPath = ""
2025-11-13 17:49:52 +01:00
# Add new backup tag prior to pulling if option is set
if [ [ -n " ${ DaysKept :- } " ] ] ; then
ContRepo = ${ ContFull % : * }
ContApp = ${ ContRepo #*/ }
ContTag = ${ ContFull #* : }
2025-11-13 20:18:25 +01:00
BackupName = " dockcheck/ $ContApp : $RunTimestamp_ $ContTag "
2025-11-13 17:49:52 +01:00
docker tag " $ImageId " " $BackupName "
printf "%b%s backed up as %s%b\n" " $c_teal " " $i " " $BackupName " " $c_reset "
fi
2025-05-11 20:50:09 +02:00
# Checking if compose-values are empty - hence started with docker run
if [ [ -z " $ContPath " ] ] ; then
if [ [ " $DRunUp " = = true ] ] ; then
2025-11-13 17:49:52 +01:00
docker pull " $ContFull "
2025-05-11 20:50:09 +02:00
printf "%s\n" " $i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters "
else
printf "\n%b%s%b has no compose labels, probably started with docker run - %bskipping%b\n\n" " $c_yellow " " $i " " $c_reset " " $c_yellow " " $c_reset "
fi
continue
fi
2025-11-13 20:18:25 +01:00
docker pull " $ContFull " || { printf "\n%bDocker error, exiting!%b\n" " $c_red " " $c_reset " ; exit 1; }
2025-05-11 20:50:09 +02:00
done
2025-11-01 09:14:49 +01:00
printf "\n%bDone pulling updates.%b\n" " $c_green " " $c_reset "
2025-05-11 20:50:09 +02:00
2025-11-01 09:14:49 +01:00
if [ [ " $SkipRecreate " = = true ] ] ; then
printf "%bSkipping container recreation due to -R.%b\n" " $c_yellow " " $c_reset "
else
printf "%bRecreating updated containers.%b\n" " $c_blue " " $c_reset "
CurrentQue = 0
for i in " ${ SelectedUpdates [@] } " ; do
( ( CurrentQue += 1) )
unset CompleteConfs
# Extract labels and metadata
ContLabels = $( docker inspect " $i " --format '{{json .Config.Labels}}' )
ContImage = $( docker inspect " $i " --format= '{{.Config.Image}}' )
ContPath = $( $jqbin -r '."com.docker.compose.project.working_dir"' <<< " $ContLabels " )
[ [ " $ContPath " = = "null" ] ] && ContPath = ""
ContConfigFile = $( $jqbin -r '."com.docker.compose.project.config_files"' <<< " $ContLabels " )
[ [ " $ContConfigFile " = = "null" ] ] && ContConfigFile = ""
ContName = $( $jqbin -r '."com.docker.compose.service"' <<< " $ContLabels " )
[ [ " $ContName " = = "null" ] ] && ContName = ""
ContEnv = $( $jqbin -r '."com.docker.compose.project.environment_file"' <<< " $ContLabels " )
[ [ " $ContEnv " = = "null" ] ] && ContEnv = ""
ContRestartStack = $( $jqbin -r '."mag37.dockcheck.restart-stack"' <<< " $ContLabels " )
[ [ " $ContRestartStack " = = "null" ] ] && ContRestartStack = ""
ContOnlySpecific = $( $jqbin -r '."mag37.dockcheck.only-specific-container"' <<< " $ContLabels " )
[ [ " $ContOnlySpecific " = = "null" ] ] && ContRestartStack = ""
printf "\n%bNow recreating (%s/%s): %b%s%b\n" " $c_teal " " $CurrentQue " " $NumberofUpdates " " $c_blue " " $i " " $c_reset "
# Checking if compose-values are empty - hence started with docker run
[ [ -z " $ContPath " ] ] && { echo "Not a compose container, skipping." ; continue ; }
# cd to the compose-file directory to account for people who use relative volumes
cd " $ContPath " || { printf "\n%bPath error - skipping%b %s" " $c_red " " $c_reset " " $i " ; continue ; }
## Reformatting path + multi compose
if [ [ $ContConfigFile = = '/' * ] ] ; then
CompleteConfs = $( for conf in ${ ContConfigFile //,/ } ; do printf -- "-f %s " " $conf " ; done )
else
CompleteConfs = $( for conf in ${ ContConfigFile //,/ } ; do printf -- "-f %s/%s " " $ContPath " " $conf " ; done )
fi
# Check if the container got an environment file set and reformat it
ContEnvs = ""
if [ [ -n " $ContEnv " ] ] ; then ContEnvs = $( for env in ${ ContEnv //,/ } ; do printf -- "--env-file %s " " $env " ; done ) ; fi
# Set variable when compose up should only target the specific container, not the stack
if [ [ $OnlySpecific = = true ] ] || [ [ $ContOnlySpecific = = true ] ] ; then SpecificContainer = " $ContName " ; fi
# Check if the whole stack should be restarted
if [ [ " $ContRestartStack " = = true ] ] || [ [ " $ForceRestartStacks " = = true ] ] ; then
${ DockerBin } ${ CompleteConfs } stop; ${ DockerBin } ${ CompleteConfs } ${ ContEnvs } up -d || { printf "\n%bDocker error, exiting!%b\n" " $c_red " " $c_reset " ; exit 1; }
else
${ DockerBin } ${ CompleteConfs } ${ ContEnvs } up -d ${ SpecificContainer } || { printf "\n%bDocker error, exiting!%b\n" " $c_red " " $c_reset " ; exit 1; }
fi
done
2025-11-13 17:49:52 +01:00
# Clean up old backup image tags if -k is used
if [ [ -n " ${ DaysKept :- } " ] ] ; then
IFS = $'\n'
for backup_img in $( docker images --format "{{.Repository}} {{.Tag}}" dockcheck/\* ) ; do
repo_name = ${ backup_img % * }
backup_tag = ${ backup_img #* }
backup_date = ${ backup_tag %%_* }
# UNTAGGING HERE
if datecheck " $backup_date " " $DaysKept " ; then
2025-11-13 20:18:25 +01:00
docker rmi " $repo_name : $backup_tag "
2025-11-13 17:49:52 +01:00
fi
done
unset IFS
fi
2025-11-01 09:14:49 +01:00
fi
2025-10-03 09:22:17 +02:00
if [ [ " $AutoPrune " = = false ] ] && [ [ " $AutoMode " = = false ] ] ; then printf "\n" ; read -rep "Would you like to prune all dangling images? y/[n]: " AutoPrune; fi
2025-08-11 21:36:51 +02:00
if [ [ " $AutoPrune " = = true ] ] || [ [ " $AutoPrune " = ~ [ yY] ] ] ; then printf "\nAuto pruning.." ; docker image prune -f; fi
2025-04-10 12:03:03 +02:00
printf "\n%bAll done!%b\n" " $c_green " " $c_reset "
2023-01-30 10:08:13 +01:00
else
printf "\nNo updates installed, exiting.\n"
2023-01-20 11:52:47 +01:00
fi
2023-01-18 11:50:00 +01:00
else
2023-01-18 21:45:31 +01:00
printf "\nNo updates available, exiting.\n"
2023-01-18 11:50:00 +01:00
fi
2023-01-30 10:08:13 +01:00
exit 0