Created
January 7, 2026 20:06
-
-
Save davidakerr/674e4a69f895344b8febd166adf63b72 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 | |
| # Pullio Without Tmp Files and Notifications | |
| COMPOSE_BINARY="${COMPOSE_BINARY:-$(which 'docker-compose')}" | |
| DOCKER_BINARY="${DOCKER_BINARY:-$(which 'docker')}" | |
| TAG="" | |
| DEBUG="" | |
| PARALLEL=1 | |
| COMPOSE_TYPE="NONE" | |
| # Check for V1 | |
| if [[ -n "${COMPOSE_BINARY}" ]]; then | |
| COMPOSE_TYPE="V1" | |
| # If V1 is not found, check for V2 | |
| elif [[ -n "${DOCKER_BINARY}" ]] && docker compose version &>/dev/null; then | |
| COMPOSE_TYPE="V2" | |
| fi | |
| while [ "$1" != "" ]; do | |
| PARAM=$(printf "%s\n" $1 | awk -F= '{print $1}') | |
| VALUE=$(printf "%s\n" $1 | sed 's/^[^=]*=//g') | |
| if [[ $VALUE == "$PARAM" ]]; then | |
| shift | |
| VALUE=$1 | |
| fi | |
| case $PARAM in | |
| --tag) | |
| [[ -n $VALUE ]] && [[ $VALUE != "--"* ]] && TAG=".$VALUE" | |
| ;; | |
| --debug) | |
| [[ $VALUE != "--"* ]] && DEBUG="${VALUE:-debug}" | |
| ;; | |
| --parallel) | |
| [[ $VALUE =~ ^[0-9]+$ ]] && PARALLEL=$VALUE | |
| ;; | |
| esac | |
| shift | |
| done | |
| echo "Running with \"DEBUG=$DEBUG\", \"TAG=$TAG\", and \"PARALLEL=$PARALLEL\"." | |
| # Setups the environment variables | |
| setup_environment() { | |
| # Always export these variables | |
| export COMPOSE_BINARY DOCKER_BINARY TAG DEBUG COMPOSE_TYPE | |
| # Export functions and additional variables only for parallel execution | |
| if [ "$PARALLEL" -gt 1 ]; then | |
| export -f process_container compose_pull_wrapper compose_up_wrapper | |
| export -f export_env_vars | |
| export sum | |
| fi | |
| } | |
| compose_pull_wrapper() { | |
| cd "$1" || exit 1 | |
| case $COMPOSE_TYPE in | |
| "V1") | |
| "${COMPOSE_BINARY}" pull "$2" | |
| ;; | |
| "V2") | |
| "${DOCKER_BINARY}" compose pull "$2" | |
| ;; | |
| "NONE") | |
| if [[ -n "${DOCKER_BINARY}" ]]; then | |
| "${DOCKER_BINARY}" run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$1:$1" -w="$1" linuxserver/docker-compose pull "$2" | |
| else | |
| echo "Error: Neither Docker Compose nor Docker binary is available. Cannot pull." >&2 | |
| return 1 | |
| fi | |
| ;; | |
| esac | |
| } | |
| compose_up_wrapper() { | |
| cd "$1" || exit 1 | |
| case $COMPOSE_TYPE in | |
| "V1") | |
| "${COMPOSE_BINARY}" up -d --always-recreate-deps "$2" | |
| ;; | |
| "V2") | |
| "${DOCKER_BINARY}" compose up -d --always-recreate-deps "$2" | |
| ;; | |
| "NONE") | |
| if [[ -n "${DOCKER_BINARY}" ]]; then | |
| "${DOCKER_BINARY}" run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$1:$1" -w="$1" linuxserver/docker-compose up -d --always-recreate-deps "$2" | |
| else | |
| echo "Error: Neither Docker Compose nor Docker binary is available. Cannot bring up services." >&2 | |
| return 1 | |
| fi | |
| ;; | |
| esac | |
| } | |
| export_env_vars() { | |
| export PULLIO_CONTAINER=${1} | |
| export PULLIO_IMAGE=${2} | |
| export PULLIO_AVATAR=${3} | |
| export PULLIO_OLD_IMAGE_ID=${4} | |
| export PULLIO_NEW_IMAGE_ID=${5} | |
| export PULLIO_OLD_VERSION=${6} | |
| export PULLIO_NEW_VERSION=${7} | |
| export PULLIO_OLD_REVISION=${8} | |
| export PULLIO_NEW_REVISION=${9} | |
| export PULLIO_COMPOSE_SERVICE=${10} | |
| export PULLIO_COMPOSE_WORKDIR=${11} | |
| export PULLIO_AUTHOR_URL=${13} | |
| } | |
| process_container() { | |
| local container_name="$1" | |
| echo "$container_name: Checking..." | |
| image_name=$("${DOCKER_BINARY}" inspect --format='{{.Config.Image}}' "$container_name") | |
| container_image_digest=$("${DOCKER_BINARY}" inspect --format='{{.Image}}' "$container_name") | |
| docker_compose_service=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "com.docker.compose.service" }}' "$container_name") | |
| docker_compose_version=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "com.docker.compose.version" }}' "$container_name") | |
| docker_compose_workdir=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' "$container_name") | |
| old_opencontainers_image_version=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.opencontainers.image.version" }}' "$container_name") | |
| old_opencontainers_image_revision=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.opencontainers.image.revision" }}' "$container_name") | |
| pullio_update=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.hotio.pullio'"${TAG}"'.update" }}' "$container_name") | |
| pullio_script_update=($("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.hotio.pullio'"${TAG}"'.script.update" }}' "$container_name")) | |
| pullio_registry_authfile=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.hotio.pullio'"${TAG}"'.registry.authfile" }}' "$container_name") | |
| pullio_author_avatar=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.hotio.pullio'"${TAG}"'.author.avatar" }}' "$container_name") | |
| pullio_author_url=$("${DOCKER_BINARY}" inspect --format='{{ index .Config.Labels "org.hotio.pullio'"${TAG}"'.author.url" }}' "$container_name") | |
| if [[ (-n $docker_compose_version) && ($pullio_update == true) ]]; then | |
| if [[ -f $pullio_registry_authfile ]]; then | |
| echo "$container_name: Registry login..." | |
| jq -r .password <"$pullio_registry_authfile" | "${DOCKER_BINARY}" login --username "$(jq -r .username <"$pullio_registry_authfile")" --password-stdin "$(jq -r .registry <"$pullio_registry_authfile")" | |
| fi | |
| echo "$container_name: Pulling image..." | |
| if ! compose_pull_wrapper "$docker_compose_workdir" "${docker_compose_service}"; then | |
| echo "$container_name: Pulling failed!" | |
| fi | |
| image_digest=${DEBUG}$("${DOCKER_BINARY}" image inspect --format='{{.Id}}' "${image_name}") | |
| new_opencontainers_image_version=$("${DOCKER_BINARY}" image inspect --format='{{ index .Config.Labels "org.opencontainers.image.version" }}' "$image_name") | |
| new_opencontainers_image_revision=$("${DOCKER_BINARY}" image inspect --format='{{ index .Config.Labels "org.opencontainers.image.revision" }}' "$image_name") | |
| if [[ "${image_digest}" != "$container_image_digest" ]] && [[ $pullio_update == true ]]; then | |
| if [[ -n "${pullio_script_update[*]}" ]]; then | |
| echo "$container_name: Stopping container..." | |
| "${DOCKER_BINARY}" stop "${container_name}" | |
| echo "$container_name: Executing update script..." | |
| export_env_vars "$container_name" "${image_name}" "${pullio_author_avatar}" "${container_image_digest/sha256:/}" "${image_digest/sha256:/}" "${old_opencontainers_image_version}" "${new_opencontainers_image_version}" "${old_opencontainers_image_revision}" "${new_opencontainers_image_revision}" "${docker_compose_service}" "${docker_compose_workdir}" "${pullio_author_url}" | |
| "${pullio_script_update[@]}" | |
| fi | |
| echo "$container_name: Updating container..." | |
| if ! compose_up_wrapper "$docker_compose_workdir" "${docker_compose_service}"; then | |
| echo "${container_name}: Updating container failed!" | |
| fi | |
| fi | |
| fi | |
| } | |
| # Respect ctrl+c | |
| trap 'exit 130' INT | |
| # Setup the environment | |
| sum="$(sha1sum "$0" | awk '{print $1}')" | |
| declare -a containers | |
| readarray -t containers < <("${DOCKER_BINARY}" ps --format '{{.Names}}' | sort -k1) | |
| setup_environment | |
| echo "Processing ${#containers[@]} containers with parallelism of $PARALLEL" | |
| if [ "$PARALLEL" -gt 1 ]; then | |
| printf '%s\n' "${containers[@]}" | xargs -P "$PARALLEL" -I {} bash -c 'process_container "$@"' _ {} | |
| else | |
| for container_name in "${containers[@]}"; do | |
| process_container "$container_name" | |
| done | |
| fi | |
| echo "Pruning docker images..." | |
| "${DOCKER_BINARY}" image prune --force |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment