Podcheck Initial Release

This commit is contained in:
Joe Harrison 2024-11-16 18:24:58 +00:00 committed by Joe Harrison
parent 0009adc2cf
commit 61a38ad6c1
8 changed files with 213 additions and 193 deletions

154
README.md
View file

@ -1,104 +1,100 @@
<p align="center">
<img src="extras/dockcheck_logo_by_booYah187.png" width="160" title="dockcheck">
<img src="extras/podcheck.png" width="160" title="Podcheck">
</p>
<p align="center">
<img src="https://img.shields.io/badge/bash-4.3-green?style=flat-square&logo=gnubash" alt="bash">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/badge/license-GPLv3-red?style=flat-square" alt="GPLv3"></a>
<img src="https://img.shields.io/github/v/tag/mag37/dockcheck?style=flat-square&label=release" alt="release">
<a href="https://ko-fi.com/mag37"><img src="https://img.shields.io/badge/-Ko--fi-grey?style=flat-square&logo=Ko-fi" alt="Buy me a Coffee"></a>
<a href="https://liberapay.com/user-bin-rob/donate"><img src="https://img.shields.io/badge/-LiberaPay-grey?style=flat-square&logo=liberapay" alt="LiberaPay"></a>
<a href="https://github.com/sponsors/mag37"><img src="https://img.shields.io/badge/-Sponsor-grey?style=flat-square&logo=github" alt="Github Sponsor"></a>
<img src="https://img.shields.io/github/v/tag/sudo-kraken/podcheck?style=flat-square&label=release" alt="release">
<a href="https://www.buymeacoffee.com/jharrison94"><img src="https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee" alt="Buy Me A Coffee">
</p>
<h3 align="center">CLI tool to automate docker image updates. <br>No <b>pre-pull</b>, selective, optional notifications and prune when done.</h3>
<h3 align="center">CLI tool to automate Podman image updates. <br>Selective updates, optional notifications, and image pruning when done.</h3>
<h2 align="center">Now with simple notification integrations!</h2>
<h4 align="center">With features like excluding specific containers, custom container labels, auto-prune when done and more.</h4>
<h4 align="center">Features include excluding specific containers, custom container labels, auto-prune when done, and more.</h4>
___
## :bell: Changelog
- **v0.5.0**: Rewritten notify logic - all templates are adjusted and should be migrated!
- Copy the custom settings from your current template to the new version of the same template.
- Look into, copy and customize the `urls.list` file if that's of interest.
- Other changes:
- Added Discord notify template.
- Verbosity changed of `regctl`.
- **v0.4.9**: Added a function to enrich the notify-message with release note URLs. See [Release notes addon](https://github.com/mag37/dockcheck#date-release-notes-addon-to-notifications)
- **v0.4.8**: Rewrote prune logic to not prompt with options `-a|-y` or `-n`. Auto prune with `-p`.
- **v0.4.7**: Notification Template changes to gotify(new!), DSM(improved), SMTP(deprecation alternative).
- **v0.4.6**: Compatibility changes to timeout, due to busybox.
- **v0.4.5**: Bugfixes, compatibility changes to timeout and arrays.
- **v0.4.3**: Added timeout option to skip container if registry check takes too long (10s default).
- **v0.5.6**: Directly checking for systemd units matching container names.
- Improved Quadlet detection by checking for systemd units named after the container.
- Ensures better compatibility with Quadlet-managed containers.
- **v0.5.5**: Switched to podman compose command.
- Adjusted the script to use podman compose instead of podman-compose.
- Removed unnecessary messages.
- **v0.5.4**: Improved Quadlet detection by matching container IDs with systemd units.
- The script now searches systemd unit files for references to the container ID.
- Provides reliable detection of Quadlet-managed containers.
- **v0.5.0**: Initial release of Podcheck, inspired by Dockcheck.
- Supports updating containers managed by Podman Compose and Quadlet.
- Includes options for automatic updates, notifications, and more.
___
![](extras/example.gif)
## :mag_right: `dockcheck.sh`
## :mag_right: `podcheck.sh`
```
$ ./dockcheck.sh -h
Syntax: dockcheck.sh [OPTION] [part of name to filter]
Example: dockcheck.sh -y -d 10 -e nextcloud,heimdall
$ ./podcheck.sh -h
Syntax: podcheck.sh [OPTION] [part of name to filter]
Example: podcheck.sh -y -d 10 -e nextcloud,heimdall
Options:"
Options:
-a|y Automatic updates, without interaction.
-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower.
-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age.
-e X Exclude containers, separated by comma.
-f Force stack restart after update. Caution: restarts once for every updated container within stack.
-f Force pod restart after update.
-h Print this Help.
-i Inform - send a preconfigured notification.
-l Only update if label is set. See readme.
-m Monochrome mode, no printf color codes.
-n No updates, only checking availability.
-p Auto-Prune dangling images after update.
-r Allow updating images for docker run, wont update the container.
-s Include stopped containers in the check. (Logic: docker ps -a).
-n No updates; only checking availability.
-p Auto-prune dangling images after update.
-r Allow updating images for podman run; won't update the container.
-s Include stopped containers in the check.
-t Set a timeout (in seconds) per container for registry checkups, 10 is default.
-v Prints current version.
```
### Basic example:
```
$ ./dockcheck.sh
. . .
$ ./podcheck.sh
...
Containers on latest version:
glances
homer
filebrowser
foundryvtt
Containers with updates available:
1) adguardhome
2) syncthing
3) whoogle-search
1) joplin-db
2) it-tools
Choose what containers to update:
Enter number(s) separated by comma, [a] for all - [q] to quit:
```
Then it proceedes to run `pull` and `up -d` on every container with updates.
After the updates are complete, you'll get prompted if you'd like to prune dangling images.
Then it proceeds to run podman pull and podman compose up -d, or restarts systemd units for every container with updates.
After the updates are complete, you'll be prompted if you'd like to prune dangling images
___
## :nut_and_bolt: Dependencies
- Running docker (duh) and compose, either standalone or plugin.
- Podman: Ensure you have Podman installed and properly configured.
- Podman Compose: For containers managed with podman compose, make sure it's installed.
- Note: podman compose is included in recent versions of Podman.
- Quadlet: If you're using systemd units to manage your containers, ensure they are correctly set up.
- Bash shell or compatible shell of at least v4.3
- [regclient/regctl](https://github.com/regclient/regclient) (Licensed under [Apache-2.0 License](http://www.apache.org/licenses/LICENSE-2.0))
- User will be prompted to download `regctl` if not in `PATH` or `PWD`.
- regctl requires `amd64/arm64` - see [workaround](#roller_coaster-workaround-for-non-amd64--arm64) if other architecture is used.
- jq: Used for parsing JSON output from podman inspect.
- timeout: Optional but recommended for setting timeouts on registry checks.
## :tent: Install Instructions
Download the script to a directory in **PATH**, I'd suggest using `~/.local/bin` as that's usually in **PATH**.
```sh
# basic example with curl:
curl -L https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh -o ~/.local/bin/dockcheck.sh
chmod +x ~/.local/bin/dockcheck.sh
# Using curl:
curl -L https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh -o ~/.local/bin/podcheck.sh
chmod +x ~/.local/bin/podcheck.sh
# or oneliner with wget:
wget -O ~/.local/bin/dockcheck.sh "https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh" && chmod +x ~/.local/bin/dockcheck.sh
# Or using wget:
wget -O ~/.local/bin/podcheck.sh "https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh" && chmod +x ~/.local/bin/podcheck.sh
```
Then call the script anywhere with just `dockcheck.sh`.
Add preferred `notify.sh`-template to the same directory - this will not be touched by the scripts self-update function.
Then call the script anywhere with `podcheck.sh`.
Add your preferred notify.sh template to the same directory—this will not be touched by the script's self-update function.
## :loudspeaker: Notifications
Trigger with the `-i` flag.
@ -120,30 +116,33 @@ Use a `notify_X.sh` template file from the **notify_templates** directory, copy
- [Discord](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) - Discord webhooks.
Further additions are welcome - suggestions or PR!
<sub><sup>Initiated and first contributed by [yoyoma2](https://github.com/yoyoma2).</sup></sub>
<sub><sup>Initiated and first contributed by [mag37](https://github.com/mag37) as dockcheck.</sup></sub>
### :date: Release notes addon to Notifications
There's a function to use a lookup-file to add release note URL's to the notification message.
Copy the notify_templates/`urls.list` file to the script directory, it will be used automatically if it's there. Modify it as necessary, the names of interest in the left column needs to match your container names.
There's a function to use a lookup file to add release note URLs to the notification message.
Copy the notify_templates/urls.list file to the script directory—it will be used automatically if it's there. Modify it as necessary; the names of interest in the left column need to match your container names.
The output of the notification will look something like this:
```
Containers on hostname with updates available:
apprise-api -> https://github.com/linuxserver/docker-apprise-api/releases
homer -> https://github.com/bastienwirtz/homer/releases
nginx -> https://github.com/docker-library/official-images/blob/master/library/nginx
joplin-db -> https://github.com/laurent22/joplin/releases
it-tools -> https://github.com/CorentinTh/it-tools/releases
...
```
The `urls.list` file is just an example and I'd gladly see that people contribute back when they add their preferred URLs to their lists.
## :bookmark: Labels
Optionally add labels to compose-files. Currently these are the usable labels:
Optionally, you can add labels to your containers to control how Podcheck handles them. Currently, these are the usable labels:
```yaml
labels:
sudo-kraken.podcheck.restart-stack: true
sudo-kraken.podcheck.update: true
```
labels:
mag37.dockcheck.restart-stack: true
mag37.dockcheck.update: true
```
- `mag37.dockcheck.restart-stack: true` works instead of the `-f` option, forcing stop+restart on the whole compose-stack (Caution: Will restart on every updated container within stack).
- `mag37.dockcheck.update: true` will when used with the `-l` option only update containers with this label and skip the rest. Will still list updates as usual.
- `sudo-kraken.podcheck.restart-stack`: true works instead of the `-f` option, forcing a restart of the entire pod or compose stack when an update is applied. Caution: This will restart the entire stack for every updated container within it.
- `sudo-kraken.podcheck.update`: true will, when used with the `-l` option, only update containers with this label and skip the rest. It will still list all available updates.
## :roller_coaster: Workaround for non **amd64** / **arm64**
`regctl` provides binaries for amd64/arm64, to use on other architecture you could try this workaround.
@ -169,8 +168,8 @@ Test it with `./regctl --help` and then either add the file to the same path as
**Example** - Change names, paths, and remove cat+password flag if you rather get prompted:
```sh
function dchk {
cat ~/pwd.txt | docker login --username YourUser --password-stdin
~/dockcheck.sh "$@"
cat ~/pwd.txt | podman login --username YourUser --password-stdin docker.io
~/podcheck.sh "$@"
}
```
@ -182,21 +181,22 @@ function dchk {
## :warning: `-r flag` disclaimer and warning
**Wont auto-update the containers, only their images. (compose is recommended)**
`docker run` dont support using new images just by restarting a container.
`podman run` does not support using new images just by restarting a container.
Containers need to be manually stopped, removed and created again to run on the new image.
## :wrench: Debugging
If you hit issues, you could check the output of the `extras/errorCheck.sh` script for clues.
Another option is to run the main script with debugging in a subshell `bash -x dockcheck.sh` - if there's a particular container/image that's causing issues you can filter for just that through `bash -x dockcheck.sh nginx`.
Another option is to run the main script with debugging in a subshell `bash -x podcheck.sh` - if there's a particular container/image that's causing issues you can filter for just that through `bash -x podcheck.sh nginx`.
## :scroll: License
dockcheck is created and released under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) license.
## :heartpulse: Sponsorlist
- [avegy](https://github.com/avegy)
podcheck is created and released under the [GNU GPL v3.0](https://www.gnu.org/licenses/gpl-3.0-standalone.html) license.
___
### :floppy_disk: The [story](https://mag37.org/posts/project_dockcheck/) behind it. 1 year in retrospect.
### :floppy_disk: The Story Behind Podcheck
Podcheck was created to bring the convenience of automated container updates to the Podman ecosystem. As a user of Dockcheck for Docker, the need for a similar tool for Podman became apparent. Podcheck aims to provide the same ease of use and automation, tailored for Podman users.
## :star2: Acknowledgments
Podcheck is inspired by the original Dockcheck script. Without Dockcheck, there wouldn't have been a Podcheck. Many thanks to mag37 and all the contributors to Dockcheck for their work and inspiration.
___
Please feel free to contribute, open issues, or submit pull requests to improve Podcheck!

View file

@ -15,14 +15,14 @@ IFS=',' read -r -a Excludes <<< "$Exclude" ; unset IFS
SearchName="$1"
for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
for i in $(podman ps --filter "name=$SearchName" --format '{{.Names}}') ; do
for e in "${Excludes[@]}" ; do [[ "$i" == "$e" ]] && continue 2 ; done
printf ". "
RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
LocalHash=$(docker image inspect "$RepoUrl" --format '{{.RepoDigests}}')
RepoUrl=$(podman inspect "$i" --format='{{.ImageName}}')
LocalHash=$(podman image inspect "$RepoUrl" --format '{{.Digest}}')
### Checking for errors while setting the variable:
if RegHash=$($regbin image digest --list "$RepoUrl" 2>/dev/null) ; then
if [[ "$LocalHash" = *"$RegHash"* ]] ; then NoUpdates+=("$i"); else GotUpdates+=("$i"); fi
if [[ "$LocalHash" == "$RegHash" ]] ; then NoUpdates+=("$i"); else GotUpdates+=("$i"); fi
else
GotErrors+=("$i")
fi
@ -41,7 +41,7 @@ if [[ -n ${NoUpdates[*]} ]] ; then
printf "%s\n" "${NoUpdates[@]}"
fi
if [[ -n ${GotErrors[*]} ]] ; then
printf "\n\033[0;31mContainers with errors, wont get updated:\033[0m\n"
printf "\n\033[0;31mContainers with errors; won't get updated:\033[0m\n"
printf "%s\n" "${GotErrors[@]}"
fi
if [[ -n ${GotUpdates[*]} ]] ; then

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View file

@ -1,13 +1,16 @@
#!/usr/bin/env bash
SearchName="$1"
for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
for i in $(podman ps --filter "name=$SearchName" --format '{{.Names}}') ; do
echo "------------ $i ------------"
ContPath=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
[ -z "$ContPath" ] && { "$i has no compose labels - skipping" ; continue ; }
ContConfigFile=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
ContName=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
ContEnv=$(docker inspect "$i" --format '{{index .Config.Labels "com.docker.compose.project.environment_file" }}')
ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
ContPath=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
if [ -z "$ContPath" ]; then
echo "$i has no compose labels - skipping"
continue
fi
ContConfigFile=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
ContName=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
ContEnv=$(podman inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.environment_file" }}')
ContImage=$(podman inspect "$i" --format='{{.ImageName}}')
if [[ $ContConfigFile = '/'* ]] ; then
ComposeFile="$ContConfigFile"
@ -22,6 +25,6 @@ for i in $(docker ps --filter "name=$SearchName" --format '{{.Names}}') ; do
echo -e "Container image:\t$ContImage"
echo
echo "Mounts:"
docker inspect -f '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ printf "\n" }}{{ end }}' "$i"
podman inspect -f '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ printf "\n" }}{{ end }}' "$i"
echo
done

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

BIN
extras/podcheck.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

223
dockcheck.sh → podcheck.sh Executable file → Normal file
View file

@ -1,43 +1,43 @@
#!/usr/bin/env bash
VERSION="v0.5.0"
### ChangeNotes: Rewritten notify logic - all templates adjusted, transfer your current settings to a new template! See README.
Github="https://github.com/mag37/dockcheck"
RawUrl="https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh"
VERSION="v0.5.6"
# ChangeNotes: Directly checking for systemd units matching container names.
Github="https://github.com/sudo-kraken/podcheck"
RawUrl="https://raw.githubusercontent.com/sudo-kraken/podcheck/main/podcheck.sh"
### Variables for self updating
# Variables for self-updating
ScriptArgs=( "$@" )
ScriptPath="$(readlink -f "$0")"
ScriptWorkDir="$(dirname "$ScriptPath")"
### Check if there's a new release of the script:
LatestRelease="$(curl -s -r 0-50 $RawUrl | sed -n "/VERSION/s/VERSION=//p" | tr -d '"')"
LatestChanges="$(curl -s -r 0-200 $RawUrl | sed -n "/ChangeNotes/s/### ChangeNotes: //p")"
# Check if there's a new release of the script
LatestRelease="$(curl -s -r 0-100 $RawUrl | sed -n "/VERSION/s/VERSION=//p" | tr -d '"')"
LatestChanges="$(curl -s -r 0-200 $RawUrl | sed -n "/ChangeNotes/s/# ChangeNotes: //p")"
### Help Function:
# Help Function
Help() {
echo "Syntax: dockcheck.sh [OPTION] [part of name to filter]"
echo "Example: dockcheck.sh -y -d 10 -e nextcloud,heimdall"
echo "Syntax: podcheck.sh [OPTION] [part of name to filter]"
echo "Example: podcheck.sh -y -d 10 -e nextcloud,heimdall"
echo
echo "Options:"
echo "-a|y Automatic updates, without interaction."
echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower."
echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age."
echo "-e X Exclude containers, separated by comma."
echo "-f Force stack restart after update. Caution: restarts once for every updated container within stack."
echo "-f Force pod restart after update."
echo "-h Print this Help."
echo "-i Inform - send a preconfigured notification."
echo "-l Only update if label is set. See readme."
echo "-m Monochrome mode, no printf color codes."
echo "-n No updates, only checking availability."
echo "-p Auto-Prune dangling images after update."
echo "-r Allow updating images for docker run, wont update the container."
echo "-s Include stopped containers in the check. (Logic: docker ps -a)."
echo "-n No updates; only checking availability."
echo "-p Auto-prune dangling images after update."
echo "-r Allow updating images for podman run; won't update the container."
echo "-s Include stopped containers in the check."
echo "-t Set a timeout (in seconds) per container for registry checkups, 10 is default."
echo "-v Prints current version."
echo
echo "Project source: $Github"
}
### Colors:
# Colors
c_red="\033[0;31m"
c_green="\033[0;32m"
c_yellow="\033[0;33m"
@ -54,7 +54,7 @@ while getopts "aynpfrhlisvme:d:t:" options; do
r) DRunUp="yes" ;;
p) AutoPrune="yes" ;;
l) OnlyLabel=true ;;
f) ForceRestartStacks=true ;;
f) ForceRestartPods=true ;;
i) [ -s "$ScriptWorkDir"/notify.sh ] && { source "$ScriptWorkDir"/notify.sh ; Notify="yes" ; } ;;
e) Exclude=${OPTARG} ;;
m) declare c_{red,green,yellow,blue,teal,reset}="" ;;
@ -68,18 +68,19 @@ while getopts "aynpfrhlisvme:d:t:" options; do
done
shift "$((OPTIND-1))"
# Self-update functions
self_update_curl() {
cp "$ScriptPath" "$ScriptPath".bak
if [[ $(builtin type -P curl) ]]; then
if [[ $(command -v curl) ]]; then
curl -L $RawUrl > "$ScriptPath" ; chmod +x "$ScriptPath"
printf "\n%s\n" "--- starting over with the updated version ---"
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
exit 1 # exit the old instance
elif [[ $(builtin type -P wget) ]]; then
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
exit 1 # Exit the old instance
elif [[ $(command -v wget) ]]; then
wget $RawUrl -O "$ScriptPath" ; chmod +x "$ScriptPath"
printf "\n%s\n" "--- starting over with the updated version ---"
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
exit 1 # exit the old instance
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
exit 1 # Exit the old instance
else
printf "curl/wget not available - download the update manually: %s \n" "$Github"
fi
@ -87,20 +88,20 @@ self_update_curl() {
self_update() {
cd "$ScriptWorkDir" || { printf "Path error, skipping update.\n" ; return ; }
if [[ $(builtin type -P git) ]] && [[ "$(git ls-remote --get-url 2>/dev/null)" =~ .*"mag37/dockcheck".* ]] ; then
if [[ $(command -v git) ]] && [[ "$(git ls-remote --get-url 2>/dev/null)" =~ .*"yourusername/podcheck".* ]] ; then
printf "\n%s\n" "Pulling the latest version."
git pull --force || { printf "Git error, manually pull/clone.\n" ; return ; }
printf "\n%s\n" "--- starting over with the updated version ---"
cd - || { printf "Path error.\n" ; return ; }
exec "$ScriptPath" "${ScriptArgs[@]}" # run the new script with old arguments
exit 1 # exit the old instance
exec "$ScriptPath" "${ScriptArgs[@]}" # Run the new script with old arguments
exit 1 # Exit the old instance
else
cd - || { printf "Path error.\n" ; return ; }
self_update_curl
fi
}
### Choose from list -function:
# Choose from list function
choosecontainers() {
while [[ -z "$ChoiceClean" ]]; do
read -r -p "Enter number(s) separated by comma, [a] for all - [q] to quit: " Choice
@ -112,7 +113,7 @@ choosecontainers() {
else
ChoiceClean=${Choice//[,.:;]/ }
for CC in $ChoiceClean ; do
if [[ "$CC" -lt 1 || "$CC" -gt $UpdCount ]] ; then # reset choice if out of bounds
if [[ "$CC" -lt 1 || "$CC" -gt $UpdCount ]] ; then
echo "Number not in list: $CC" ; unset ChoiceClean ; break 1
else
SelectedUpdates+=( "${GotUpdates[$CC-1]}" )
@ -139,27 +140,16 @@ progress_bar() {
QueCurrent="$1"
QueTotal="$2"
((Percent=100*QueCurrent/QueTotal))
((Complete=50*Percent/100)) # change first number for width (50)
((Left=50-Complete)) # change first number for width (50)
((Complete=50*Percent/100)) # Change first number for width (50)
((Left=50-Complete)) # Change first number for width (50)
BarComplete=$(printf "%${Complete}s" | tr " " "#")
BarLeft=$(printf "%${Left}s" | tr " " "-")
[[ "$QueTotal" == "$QueCurrent" ]] || printf "\r[%s%s] %s/%s " "$BarComplete" "$BarLeft" "$QueCurrent" "$QueTotal"
[[ "$QueTotal" == "$QueCurrent" ]] && printf "\r[%b%s%b] %s/%s \n" "$c_teal" "$BarComplete" "$c_reset" "$QueCurrent" "$QueTotal"
}
### Function to add user-provided urls to releasenotes
releasenotes() {
for update in ${GotUpdates[@]}; do
found=false
while read -r container url; do
[[ $update == $container ]] && Updates+=("$update -> $url") && found=true
done < "$ScriptWorkDir"/urls.list
[[ $found == false ]] && Updates+=("$update -> url missing") || continue
done
}
### Version check & initiate self update
if [[ "$VERSION" != "$LatestRelease" ]] ; then
# Version check & initiate self update
if [[ "$VERSION" != "$LatestRelease" ]] && [[ -n "$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 [[ -z "$AutoUp" ]] ; then
read -r -p "Would you like to update? y/[n]: " SelfUpdate
@ -167,26 +157,26 @@ if [[ "$VERSION" != "$LatestRelease" ]] ; then
fi
fi
### Set $1 to a variable for name filtering later.
# Set $1 to a variable for name filtering later
SearchName="$1"
### Create array of excludes
# Create array of excludes
IFS=',' read -r -a Excludes <<< "$Exclude" ; unset IFS
### Check if required binary exists in PATH or directory:
if [[ $(builtin type -P "regctl") ]]; then regbin="regctl" ;
# Check if required binary exists in PATH or directory
if [[ $(command -v "regctl") ]]; then regbin="regctl" ;
elif [[ -f "$ScriptWorkDir/regctl" ]]; then regbin="$ScriptWorkDir/regctl" ;
else
read -r -p "Required dependency 'regctl' missing, do you want it downloaded? y/[n] " GetDep
if [[ "$GetDep" =~ [yY] ]] ; then
### Check arch:
# Check architecture
case "$(uname --machine)" in
x86_64|amd64) architecture="amd64" ;;
arm64|aarch64) architecture="arm64";;
*) echo "Architecture not supported, exiting." ; exit 1;;
esac
RegUrl="https://github.com/regclient/regclient/releases/latest/download/regctl-linux-$architecture"
if [[ $(builtin type -P curl) ]]; then curl -L $RegUrl > "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
elif [[ $(builtin type -P wget) ]]; then wget $RegUrl -O "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
if [[ $(command -v curl) ]]; then curl -L $RegUrl > "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
elif [[ $(command -v wget) ]]; then wget $RegUrl -O "$ScriptWorkDir/regctl" ; chmod +x "$ScriptWorkDir/regctl" ; regbin="$ScriptWorkDir/regctl" ;
else
printf "%s\n" "curl/wget not available - get regctl manually from the repo link, quitting."
fi
@ -195,21 +185,20 @@ else
exit 1
fi
fi
### final check if binary is correct
# Final check if binary is correct
$regbin version &> /dev/null || { printf "%s\n" "regctl is not working - try to remove it and re-download it, exiting."; exit 1; }
### Check docker compose binary:
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
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."
# Check podman compose binary
if podman compose version &> /dev/null ; then PodmanComposeBin="podman compose" ;
elif command -v podman-compose &> /dev/null; then PodmanComposeBin="podman-compose" ;
elif podman version &> /dev/null; then
printf "%s\n" "No podman-compose binary available, using plain podman"
else
printf "%s\n" "No docker binaries available, exiting."
printf "%s\n" "No podman binaries available, exiting."
exit 1
fi
### Numbered List -function:
# Numbered List function
options() {
num=1
for i in "${GotUpdates[@]}"; do
@ -218,7 +207,7 @@ for i in "${GotUpdates[@]}"; do
done
}
### Listing typed exclusions:
# Listing typed exclusions
if [[ -n ${Excludes[*]} ]] ; then
printf "\n%bExcluding these names:%b\n" "$c_blue" "$c_reset"
printf "%s\n" "${Excludes[@]}"
@ -226,11 +215,11 @@ if [[ -n ${Excludes[*]} ]] ; then
fi
# Variables for progress_bar function
DocCount=$(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | wc -l)
DocCount=$(podman ps $Stopped --filter "name=$SearchName" --format '{{.Names}}' | wc -l)
RegCheckQue=0
### Testing and setting timeout binary
t_out=$(type -P "timeout")
# Testing and setting timeout binary
t_out=$(command -v "timeout")
if [[ $t_out ]]; then
t_out=$(realpath $t_out 2>/dev/null || readlink -f $t_out)
if [[ $t_out =~ "busybox" ]]; then
@ -240,17 +229,17 @@ if [[ $t_out ]]; then
else t_out=""
fi
### Check the image-hash of every running container VS the registry
for i in $(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}') ; do
# Check the image-hash of every running container VS the registry
for i in $(podman ps $Stopped --filter "name=$SearchName" --format '{{.Names}}') ; do
((RegCheckQue+=1))
progress_bar "$RegCheckQue" "$DocCount"
### Looping every item over the list of excluded names and skipping:
# Looping every item over the list of excluded names and skipping
for e in "${Excludes[@]}" ; do [[ "$i" == "$e" ]] && continue 2 ; done
RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}')
LocalHash=$(docker image inspect "$RepoUrl" --format '{{.RepoDigests}}')
# Checking for errors while setting the variable:
RepoUrl=$(podman inspect "$i" --format='{{.ImageName}}')
LocalHash=$(podman image inspect "$RepoUrl" --format '{{.Digest}}')
# Checking for errors while setting the variable
if RegHash=$(${t_out} $regbin -v error image digest --list "$RepoUrl" 2>&1) ; then
if [[ "$LocalHash" = *"$RegHash"* ]] ; then
if [[ "$LocalHash" == "$RegHash" ]] ; then
NoUpdates+=("$i")
else
if [[ -n "$DaysOld" ]] && ! datecheck ; then
@ -260,27 +249,27 @@ for i in $(docker ps $Stopped --filter "name=$SearchName" --format '{{.Names}}')
fi
fi
else
# Here the RegHash is the result of an error code.
# Here the RegHash is the result of an error code
GotErrors+=("$i - ${RegHash}")
fi
done
### Sort arrays alphabetically
# Sort arrays alphabetically
IFS=$'\n'
NoUpdates=($(sort <<<"${NoUpdates[*]}"))
GotUpdates=($(sort <<<"${GotUpdates[*]}"))
unset IFS
### Define how many updates are available
# Define how many updates are available
UpdCount="${#GotUpdates[@]}"
### List what containers got updates or not
# List what containers got updates or not
if [[ -n ${NoUpdates[*]} ]] ; then
printf "\n%bContainers on latest version:%b\n" "$c_green" "$c_reset"
printf "%s\n" "${NoUpdates[@]}"
fi
if [[ -n ${GotErrors[*]} ]] ; then
printf "\n%bContainers with errors, wont get updated:%b\n" "$c_red" "$c_reset"
printf "\n%bContainers with errors; won't get updated:%b\n" "$c_red" "$c_reset"
printf "%s\n" "${GotErrors[@]}"
printf "%binfo:%b 'unauthorized' often means not found in a public registry.\n" "$c_blue" "$c_reset"
fi
@ -290,11 +279,11 @@ if [[ -n ${GotUpdates[*]} ]] ; then
[[ -n "$Notify" ]] && { [[ $(type -t send_notification) == function ]] && send_notification "${GotUpdates[@]}" || printf "Could not source notification function.\n" ; }
fi
### Optionally get updates if there's any
# Optionally get updates if there's any
if [ -n "$GotUpdates" ] ; then
if [ -z "$AutoUp" ] ; then
printf "\n%bChoose what containers to update.%b\n" "$c_teal" "$c_reset"
choosecontainers
printf "\n%bChoose what containers to update.%b\n" "$c_teal" "$c_reset"
choosecontainers
else
SelectedUpdates=( "${GotUpdates[@]}" )
fi
@ -305,47 +294,75 @@ if [ -n "$GotUpdates" ] ; then
do
((CurrentQue+=1))
unset CompleteConfs
ContPath=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')
ContConfigFile=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.project.config_files" }}')
ContName=$(docker inspect "$i" --format '{{ index .Config.Labels "com.docker.compose.service" }}')
ContEnv=$(docker inspect "$i" --format '{{index .Config.Labels "com.docker.compose.project.environment_file" }}')
ContImage=$(docker inspect "$i" --format='{{.Config.Image}}')
ContUpdateLabel=$(docker inspect "$i" --format '{{ index .Config.Labels "mag37.dockcheck.update" }}')
ContRestartStack=$(docker inspect "$i" --format '{{ index .Config.Labels "mag37.dockcheck.restart-stack" }}')
### Checking if compose-values are empty - hence started with docker run:
# Extract labels and metadata
ContLabels=$(podman inspect "$i" --format '{{json .Config.Labels}}')
ContPath=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.working_dir"')
[ "$ContPath" == "null" ] && ContPath=""
ContConfigFile=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.config_files"')
[ "$ContConfigFile" == "null" ] && ContConfigFile=""
ContName=$(echo "$ContLabels" | jq -r '."com.docker.compose.service"')
[ "$ContName" == "null" ] && ContName=""
ContEnv=$(echo "$ContLabels" | jq -r '."com.docker.compose.project.environment_file"')
[ "$ContEnv" == "null" ] && ContEnv=""
ContImage=$(podman inspect "$i" --format='{{.ImageName}}')
ContUpdateLabel=$(echo "$ContLabels" | jq -r '."sudo-kraken.podcheck.update"')
[ "$ContUpdateLabel" == "null" ] && ContUpdateLabel=""
ContRestartStack=$(echo "$ContLabels" | jq -r '."sudo-kraken.podcheck.restart-stack"')
[ "$ContRestartStack" == "null" ] && ContRestartStack=""
# Checking if compose-values are empty - possibly started with podman run or managed by Quadlet
if [ -z "$ContPath" ] ; then
if [ "$DRunUp" == "yes" ] ; then
docker pull "$ContImage"
printf "%s\n" "$i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters"
# Check if a systemd unit exists with the same name as the container
if systemctl --user status "$i.service" &> /dev/null; then
echo "Detected Quadlet-managed container: $i (unit: $i.service)"
podman pull "$ContImage"
systemctl --user restart "$i.service"
echo "Quadlet container $i updated and restarted."
elif [ "$(id -u)" -eq 0 ] && systemctl status "$i.service" &> /dev/null; then
echo "Detected Quadlet-managed container: $i (unit: $i.service)"
podman pull "$ContImage"
systemctl restart "$i.service"
echo "Quadlet container $i updated and restarted."
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"
if [ "$DRunUp" == "yes" ] ; then
podman pull "$ContImage"
printf "%s\n" "$i got a new image downloaded; rebuild manually with preferred 'podman run' parameters"
else
printf "\n%b%s%b has no compose labels or associated systemd unit; %bskipping%b\n\n" "$c_yellow" "$i" "$c_reset" "$c_yellow" "$c_reset"
fi
fi
continue
fi
### cd to the compose-file directory to account for people who use relative volumes, eg - ${PWD}/data:data
# cd to the compose-file directory to account for people who use relative volumes
cd "$ContPath" || { echo "Path error - skipping $i" ; continue ; }
## Reformatting path + multi compose
# 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
printf "\n%bNow updating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset"
### Checking if Label Only -option is set, and if container got the label
[[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != true ]] && { echo "No update label, skipping." ; continue ; } }
docker pull "$ContImage"
### Check if the container got an environment file set and reformat it
# Checking if Label Only option is set, and if container got the label
[[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != "true" ]] && { echo "No update label, skipping." ; continue ; } }
podman pull "$ContImage"
# Check if the container got an environment file set and reformat it
if [ -n "$ContEnv" ]; then ContEnvs=$(for env in ${ContEnv//,/ } ; do printf -- "--env-file %s " "$env"; done) ; fi
### Check if the whole stack should be restarted
if [[ "$ContRestartStack" == true ]] || [[ "$ForceRestartStacks" == true ]] ; then
$DockerBin ${CompleteConfs} stop ; $DockerBin ${CompleteConfs} ${ContEnvs} up -d
# Check if the whole pod should be restarted
if [[ "$ContRestartStack" == "true" ]] || [[ "$ForceRestartPods" == true ]] ; then
$PodmanComposeBin ${CompleteConfs} down ; $PodmanComposeBin ${CompleteConfs} ${ContEnvs} up -d
else
$DockerBin ${CompleteConfs} ${ContEnvs} up -d ${ContName}
$PodmanComposeBin ${CompleteConfs} ${ContEnvs} up -d ${ContName}
fi
done
printf "\n%bAll done!%b\n" "$c_green" "$c_reset"
if [[ -z "$AutoPrune" ]] && [[ -z "$AutoUp" ]]; then read -r -p "Would you like to prune dangling images? y/[n]: " AutoPrune ; fi
[[ "$AutoPrune" =~ [yY] ]] && docker image prune -f
[[ "$AutoPrune" =~ [yY] ]] && podman image prune -f
else
printf "\nNo updates installed, exiting.\n"
fi