From 1f6a602555085a32cf55781bb7470a65679664a9 Mon Sep 17 00:00:00 2001 From: Timothy Allen Date: Thu, 22 Oct 2026 21:32:26 +0200 Subject: [PATCH] Add a script to update docker images with docker compose --- update_docker_images.sh | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100755 update_docker_images.sh diff --git a/update_docker_images.sh b/update_docker_images.sh new file mode 100755 index 0000000..2ecddbe --- /dev/null +++ b/update_docker_images.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# Usage: ./update_docker_images.sh [--prune] [--build] [compose-directory] +# By default, pull images and update services without pruning or building. +# WARNING: Pruning removes stopped containers, unused images, build cache, +# and unused anonymous volumes. +set -euo pipefail + +usage() { + printf 'Usage: %s [--prune] [--build] [compose-directory]\n' "$0" + printf '%s\n' \ + ' --prune Remove unused Docker resources before updating (including volumes).' \ + ' --build Build images with --pull before updating services.' \ + ' -h, --help Show this help.' \ + 'Default: pull images and update services; do not prune or build.' +} + +prune=false +build=false +positional=() +while (( $# )); do + case "$1" in + --prune) prune=true ;; + --build) build=true ;; + -h|--help) usage; exit 0 ;; + --) shift; positional+=("$@"); break ;; + -*) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 2 ;; + *) positional+=("$1") ;; + esac + shift +done +if (( ${#positional[@]} > 1 )); then + usage >&2 + exit 2 +fi + +compose_root=${positional[0]:-/etc/docker/compose} +if [[ ! -d "$compose_root" ]]; then + printf 'Compose directory not found: %s\n' "$compose_root" >&2 + exit 1 +fi + +created_builder= +cleanup() { + local status=$? + trap - EXIT + if [[ -n "$created_builder" ]]; then + if ! docker buildx rm "$created_builder"; then + printf 'Could not remove builder: %s\n' "$created_builder" >&2 + if (( status == 0 )); then + status=1 + fi + fi + fi + exit "$status" +} +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +if "$prune"; then + docker system df + docker system prune -a --volumes -f + docker system df +fi + +if "$build"; then + # Select an existing container builder, not merely check whether one exists. + # Capture the list separately so a failed Docker command stops the script. + builders=$(docker buildx ls --format '{{.Name}} {{.Driver}}') + builder= + while read -r name driver; do + if [[ "$driver" == docker-container ]]; then + builder=$name + break + fi + done <<< "$builders" + if [[ -z "$builder" ]]; then + # Bootstrap separately so cleanup can run if startup fails. + created_builder=$(docker buildx create \ + --buildkitd-config /etc/docker/buildx/buildkit.toml \ + --driver docker-container \ + --driver-opt 'network=treehouse') + builder=$created_builder + fi + + # Select the builder for this script without changing the user's default. + export BUILDX_BUILDER="$builder" + docker buildx inspect "$builder" --bootstrap +fi + +for directory in "$compose_root"/*; do + [[ -d "$directory" ]] || continue + [[ -f "$directory/docker-compose.yml" ]] || continue + # Manually update Minecraft, so we're not always on the cutting edge + [[ "$directory" == *minecraft* ]] && continue + + printf '\nUpdating %s\n' "$directory" + # A subshell restores the working directory even when a command fails. + ( + cd -- "$directory" + docker compose pull + if "$build"; then + docker compose build --pull + fi + # Do not let Compose implicitly build images when starting services. + docker compose up -d --no-build + ) +done