Created
April 25, 2025 20:36
-
-
Save undying/531396dc3e2987dac78ec9fcb987dec1 to your computer and use it in GitHub Desktop.
Script to bump image versions in docker-compose.yml file
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
#! /bin/bash | |
VERSION_TYPES=( | |
# 29 | |
'^[0-9]{1,}$' | |
# 29-fpm | |
'^[0-9]{1,}-[a-z]{1,}$' | |
# 2024040105 | |
'^[0-9]{10}$' | |
# 4.0.10-develop | |
'^[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}-[a-z]{1,}$' | |
# 1.11.4.4173 | |
'^[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}(\.[0-9]{1,})?$' | |
# 1.11.4.4173-ls45 | |
'^[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}-[0-9a-z]{1,}$' | |
# v0.21.1025 | |
'^v[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}$' | |
# v0.21.1025-ls191 | |
'^v[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,}-[0-9a-z]{1,}$' | |
) | |
OS=$(uname -s | tr '[:upper:]' '[:lower:]') | |
SED_CMD="sed -i -f" | |
INPUT_FILE=docker-compose.yml | |
VERBOSE=0 | |
DRY_RUN=0 | |
declare -A IMAGES_EXCLUDE | |
declare -A IMAGES_ONLY | |
declare -A VERSION_BLACKLIST | |
case "${OS}" in | |
darwin) | |
SED_CMD="sed -i '' -f" | |
;; | |
esac | |
function compare_suffix(){ | |
local version_a=${1} version_b=${2} | |
local suffix_a=${version_a##*-} suffix_b=${version_b##*-} | |
[[ "${suffix_a}" == "${suffix_b}" ]] && return 0 | |
return 1 | |
} | |
export -f compare_suffix | |
function blacklist_file_parse(){ | |
local input_file="${1}" | |
if [[ ! -f "${input_file}" ]];then | |
printf 'No such file: %s\n' "${input_file}" >&2 | |
return 1 | |
fi | |
tr '\n' ' ' < "${input_file}" | |
} | |
export -f blacklist_file_parse | |
function clean_version(){ | |
local version=${1} | |
version=${version#v} | |
version=${version%-*} | |
printf '%s' "${version}" | |
} | |
export -f clean_version | |
function compare_versions_semver(){ | |
local i | |
local IFS=. | |
local version_a=(${1//-/.}) version_b=(${2//-/.}) | |
while [[ ${#version_a[@]} -gt ${#version_b[@]} ]];do version_b+=('0');done | |
while [[ ${#version_a[@]} -lt ${#version_b[@]} ]];do version_a+=('0');done | |
for ((i=0; i<${#version_a[@]}; i++));do | |
if [[ ${version_a[i]} =~ ^[0-9]+$ && ${version_b[i]} =~ ^[0-9]+$ ]];then | |
((10#${version_a[i]} > 10#${version_b[i]})) && return 1 | |
((10#${version_a[i]} < 10#${version_b[i]})) && return 2 | |
else | |
[[ ${version_a[i]} > ${version_b[i]} ]] && return 1 | |
[[ ${version_a[i]} < ${version_b[i]} ]] && return 2 | |
fi | |
done | |
return 0 | |
} | |
export -f compare_versions_semver | |
function compare_versions(){ | |
local version_a=${1} version_b=${2} | |
get_version_type "${version_a}" | |
local version_type=${?} | |
case ${version_type} in | |
0) | |
[[ ${version_a} -gt ${version_b} ]] && return 1 | |
[[ ${version_a} -lt ${version_b} ]] && return 2 | |
return 0 | |
;; | |
[1-2]) | |
[[ "${version_a}" > "${version_b}" ]] && return 1 | |
[[ "${version_a}" < "${version_b}" ]] && return 2 | |
return 0 | |
;; | |
[3-7]) | |
compare_versions_semver "${version_a}" "${version_b}" | |
return ${?} | |
;; | |
255) | |
printf "Unknown version type: %s\n" "${version_a}" | |
return 255 | |
;; | |
*) | |
printf "Unknown version code: %d, %s\n" "${version_type}" "${version_a}" | |
return 255 | |
;; | |
esac | |
} | |
export -f compare_versions | |
function tags_url(){ | |
local image="${1}" | |
printf 'https://registry.hub.docker.com/v2/repositories/%s/tags?page_size=1024&architecture=amd64' "${image}" | |
} | |
export -f tags_url | |
function get_images(){ | |
local f=${1} | |
grep 'image:' "${f}" \ | |
|awk '{print $2}' | |
} | |
export -f get_images | |
function get_images_from_registry(){ | |
local url="${1}" | |
curl -sS "${url}" \ | |
|jq -r '.results[] | select(.images[] | select(.architecture == "amd64")) | .name' | |
} | |
export -f get_images_from_registry | |
function get_image_and_version(){ | |
local image_full="${1}" | |
echo "${image_full//:/ }" | |
} | |
export -f get_image_and_version | |
function get_version_type(){ | |
local version="${1}" | |
local count=0 | |
for t in "${VERSION_TYPES[@]}";do | |
[[ "${version}" =~ ${t} ]] && return ${count} | |
count=$((count+1)) | |
done | |
return 255 | |
} | |
function main(){ | |
local image version image_full | |
local url | |
while read -r image_full;do | |
read -r image version < <(get_image_and_version "${image_full}") | |
if [[ ${#IMAGES_ONLY[@]} -gt 0 && ${IMAGES_ONLY[${image}]} != 1 ]];then | |
printf 'Skipping image: %s\n' "${image}" >&2 | |
continue | |
fi | |
if [[ ${#IMAGES_EXCLUDE[@]} -gt 0 && ${IMAGES_EXCLUDE[${image}]} == 1 ]];then | |
printf 'Skipping excluded image: %s\n' "${image}" >&2 | |
continue | |
fi | |
url=$(tags_url "${image}") | |
local last="${version}" | |
get_version_type "${version}" | |
local version_type=${?} | |
if [[ ${version_type} -eq 255 ]];then | |
printf 'Version type not found for %s\n' "${image_full}" >&2 | |
continue | |
fi | |
while read -r registry_version;do | |
get_version_type "${registry_version}" | |
local registry_version_type=${?} | |
[[ ${version_type} -eq ${registry_version_type} ]] || continue | |
compare_suffix "${version}" "${registry_version}" || continue | |
local registry_image_version="${image}:${registry_version}" | |
if [[ ${#VERSION_BLACKLIST[@]} -gt 0 && ${VERSION_BLACKLIST[${registry_image_version}]} == 1 ]];then | |
printf 'Skipping blacklisted version: %s\n' "${registry_image_version}" >&2 | |
continue | |
fi | |
compare_versions "${version}" "${registry_version}" | |
local compare_result=${?} | |
[[ ${compare_result} -eq 2 ]] && last="${registry_version}" | |
[[ ${VERBOSE} -gt 0 ]] && printf '%s | %s\n' "${image_full}" "${registry_version}" | |
done < <(get_images_from_registry "${url}") | |
if [[ "${version}" == "${last}" ]];then | |
printf 'No newer version found for %s\n' "${image_full}" >&2 | |
continue | |
fi | |
printf '%s -> %s:%s\n' "${image_full}" "${image}" "${last}" | |
cat - > "${tmp_file}" <<EOF | |
s|${image_full}|${image}:${last}|g | |
EOF | |
if [[ "${DRY_RUN}" -gt 0 ]];then | |
echo ${SED_CMD} "${tmp_file}" "${INPUT_FILE}" | |
else | |
${SED_CMD} "${tmp_file}" "${INPUT_FILE}" | |
fi | |
done < <(get_images ${INPUT_FILE}|sort -u) | |
} | |
[[ "${BASH_SOURCE[0]}" == "${0}" ]] || return 1 | |
tmp_file=$(mktemp) | |
trap "rm -f ${tmp_file}" EXIT | |
while [[ "${#}" -gt 0 ]];do | |
case ${1} in | |
--verbose|-v) | |
VERBOSE=1 | |
;; | |
--dry-run|-n) | |
DRY_RUN=1 | |
;; | |
--exclude|-E) | |
[[ -n ${2} ]] || exit | |
for i in ${2//,/ };do | |
IMAGES_EXCLUDE[${i}]=1 | |
done | |
shift | |
;; | |
--only|-O) | |
[[ -n ${2} ]] || exit | |
for i in ${2//,/ };do | |
IMAGES_ONLY[${i}]=1 | |
done | |
shift | |
;; | |
--blacklist-file|-B) | |
[[ -n ${2} ]] || exit | |
if [[ ! -f "${2}" ]];then | |
printf 'No such file: %s\n' "${2}" >&2 | |
exit 1; | |
fi | |
while read -r version;do | |
VERSION_BLACKLIST[${version}]=1 | |
done < "${2}" | |
shift | |
;; | |
--help|-h) | |
printf 'Usage: %s [OPTIONS]\n' "${0}" | |
printf 'OPTIONS:\n' | |
printf ' --verbose, -v\tVerbose mode\n' | |
printf ' --dry-run, -n\tDry run mode\n' | |
printf ' --blacklist-file, -B\tBlacklist file\n' | |
exit | |
;; | |
*) | |
printf 'Unknown argument: %s\n' "${1}" | |
;; | |
esac | |
shift | |
done | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment