diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d548f66 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.jq-template.awk diff --git a/5.0/alpine/Dockerfile b/5.0/alpine3.18/Dockerfile similarity index 97% rename from 5.0/alpine/Dockerfile rename to 5.0/alpine3.18/Dockerfile index c893212..a2a0f11 100644 --- a/5.0/alpine/Dockerfile +++ b/5.0/alpine3.18/Dockerfile @@ -1,3 +1,9 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + FROM ruby:3.1-alpine3.18 # explicitly set uid/gid to guarantee that it won't change in the future diff --git a/5.0/alpine/docker-entrypoint.sh b/5.0/alpine3.18/docker-entrypoint.sh similarity index 100% rename from 5.0/alpine/docker-entrypoint.sh rename to 5.0/alpine3.18/docker-entrypoint.sh diff --git a/5.0/Dockerfile b/5.0/bookworm/Dockerfile similarity index 97% rename from 5.0/Dockerfile rename to 5.0/bookworm/Dockerfile index bcb84fc..a35e84a 100644 --- a/5.0/Dockerfile +++ b/5.0/bookworm/Dockerfile @@ -1,3 +1,9 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + FROM ruby:3.1-slim-bookworm # explicitly set uid/gid to guarantee that it won't change in the future diff --git a/5.0/docker-entrypoint.sh b/5.0/bookworm/docker-entrypoint.sh similarity index 100% rename from 5.0/docker-entrypoint.sh rename to 5.0/bookworm/docker-entrypoint.sh diff --git a/Dockerfile-alpine.template b/Dockerfile-alpine.template index 3d755c0..76d8b4e 100644 --- a/Dockerfile-alpine.template +++ b/Dockerfile-alpine.template @@ -1,4 +1,4 @@ -FROM ruby:%%RUBY_VERSION%%-alpine3.18 +FROM ruby:{{ .ruby.version }}-{{ env.variant }} # explicitly set uid/gid to guarantee that it won't change in the future # the values 999:999 are identical to the current user/group id assigned @@ -40,9 +40,9 @@ RUN set -eux; \ chown redmine:redmine "$HOME"; \ chmod 1777 "$HOME" -ENV REDMINE_VERSION %%REDMINE_VERSION%% -ENV REDMINE_DOWNLOAD_URL %%REDMINE_DOWNLOAD_URL%% -ENV REDMINE_DOWNLOAD_SHA256 %%REDMINE_DOWNLOAD_SHA256%% +ENV REDMINE_VERSION {{ .version }} +ENV REDMINE_DOWNLOAD_URL {{ .downloadUrl }} +ENV REDMINE_DOWNLOAD_SHA256 {{ .sha256 }} RUN set -eux; \ wget -O redmine.tar.gz "$REDMINE_DOWNLOAD_URL"; \ diff --git a/Dockerfile-debian.template b/Dockerfile-debian.template index e19c9b0..0adb20d 100644 --- a/Dockerfile-debian.template +++ b/Dockerfile-debian.template @@ -1,4 +1,4 @@ -FROM ruby:%%RUBY_VERSION%%-slim-bookworm +FROM ruby:{{ .ruby.version }}-slim-{{ env.variant }} # explicitly set uid/gid to guarantee that it won't change in the future # the values 999:999 are identical to the current user/group id assigned @@ -43,9 +43,9 @@ RUN set -eux; \ chown redmine:redmine "$HOME"; \ chmod 1777 "$HOME" -ENV REDMINE_VERSION %%REDMINE_VERSION%% -ENV REDMINE_DOWNLOAD_URL %%REDMINE_DOWNLOAD_URL%% -ENV REDMINE_DOWNLOAD_SHA256 %%REDMINE_DOWNLOAD_SHA256%% +ENV REDMINE_VERSION {{ .version }} +ENV REDMINE_DOWNLOAD_URL {{ .downloadUrl }} +ENV REDMINE_DOWNLOAD_SHA256 {{ .sha256 }} RUN set -eux; \ # if we use wget here, we get certificate issues (https://github.com/docker-library/redmine/pull/249#issuecomment-984176479) diff --git a/apply-templates.sh b/apply-templates.sh new file mode 100755 index 0000000..41cb3bb --- /dev/null +++ b/apply-templates.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +[ -f versions.json ] # run "versions.sh" first + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +jqt='.jq-template.awk' +if [ -n "${BASHBREW_SCRIPTS:-}" ]; then + jqt="$BASHBREW_SCRIPTS/jq-template.awk" +elif [ "$BASH_SOURCE" -nt "$jqt" ]; then + # https://github.com/docker-library/bashbrew/blob/master/scripts/jq-template.awk + wget -qO "$jqt" 'https://github.com/docker-library/bashbrew/raw/9f6a35772ac863a0241f147c820354e4008edf38/scripts/jq-template.awk' +fi + +if [ "$#" -eq 0 ]; then + versions="$(jq -r 'keys | map(@sh) | join(" ")' versions.json)" + eval "set -- $versions" +fi + +generated_warning() { + cat <<-EOH + # + # NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" + # + # PLEASE DO NOT EDIT IT DIRECTLY. + # + + EOH +} + +for version; do + export version + + if [ -d "$version" ]; then + rm -rf "$version" + fi + + if jq -e '.[env.version] | not' versions.json > /dev/null; then + echo "skipping $version ..." + continue + fi + + variants="$(jq -r '.[env.version].variants | map(@sh) | join(" ")' versions.json)" + eval "variants=( $variants )" + + for variant in "${variants[@]}"; do + export variant + + echo "processing $version/$variant ..." + + dir="$version${variant:+/$variant}" + + mkdir -p "$dir" + + cp -f docker-entrypoint.sh "$dir/" + + case "$variant" in + alpine*) + template='Dockerfile-alpine.template' + sed -i -e 's/gosu/su-exec/g' "$dir/docker-entrypoint.sh" + ;; + *) + template='Dockerfile-debian.template' + ;; + esac + + { + generated_warning + gawk -f "$jqt" "$template" + } > "$dir/Dockerfile" + done +done diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index e0c950f..083c126 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -9,11 +9,13 @@ declare -A aliases=( self="$(basename "$BASH_SOURCE")" cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -versions=( */ ) -versions=( "${versions[@]%/}" ) +if [ "$#" -eq 0 ]; then + versions="$(jq -r 'keys | map(@sh) | join(" ")' versions.json)" + eval "set -- $versions" +fi # sort version numbers with highest first -IFS=$'\n'; versions=( $(echo "${versions[*]}" | sort -rV) ); unset IFS +IFS=$'\n'; set -- $(sort -rV <<<"$*"); unset IFS # get the most recent commit which modified any of "$@" fileCommit() { @@ -68,61 +70,49 @@ join() { echo "${out#$sep}" } -for version in "${versions[@]}"; do - # normally this would be down in the other loop, but "passenger" doesn't have it, so this is the simplest option (we just can't ever have "alpine" be out of sync, so we should remove it instead if it ever needs to be out of sync) - commit="$(dirCommit "$version")" - fullVersion="$(git show "$commit":"$version/Dockerfile" | awk '$1 == "ENV" && $2 == "REDMINE_VERSION" { print $3; exit }')" +for version; do + export version - versionAliases=( - $fullVersion + variants="$(jq -r '.[env.version].variants | map(@sh) | join(" ")' versions.json)" + eval "variants=( $variants )" + + alpine="$(jq -r '.[env.version].alpine' versions.json)" + debian="$(jq -r '.[env.version].debian' versions.json)" + + fullVersion="$(jq -r '.[env.version].version' versions.json)" + + versionAliases=() + while [ "$fullVersion" != "$version" -a "${fullVersion%[.]*}" != "$fullVersion" ]; do + versionAliases+=( $fullVersion ) + fullVersion="${fullVersion%[.]*}" + done + versionAliases+=( $version ${aliases[$version]:-} ) - for variant in '' passenger alpine; do - dir="$version${variant:+/$variant}" - [ -f "$dir/Dockerfile" ] || continue - + for variant in "${variants[@]}"; do + dir="$version/$variant" commit="$(dirCommit "$dir")" - if [ -n "$variant" ]; then - variantAliases=( "${versionAliases[@]/%/-$variant}" ) - variantAliases=( "${variantAliases[@]//latest-/}" ) - else - variantAliases=( "${versionAliases[@]}" ) - fi - variantParent="$(awk 'toupper($1) == "FROM" { print $2 }' "$dir/Dockerfile")" + variantArches="${parentRepoToArches[$variantParent]}" - suite="${variantParent#*:}" # "ruby:2.7-slim-bullseye", "2.7-alpine3.15" - suite="${suite##*-}" # "bullseye", "alpine3.15" - suite="${suite#alpine}" # "bullseye", "3.15" + variantAliases=( "${versionAliases[@]/%/-$variant}" ) + variantAliases=( "${variantAliases[@]//latest-/}" ) case "$variant" in - alpine) - suite="alpine$suite" # "alpine3.8" - suiteAliases=( "${versionAliases[@]/%/-$suite}" ) + "$debian") + variantAliases=( + "${versionAliases[@]}" + "${variantAliases[@]}" + ) ;; - passenger) - # the "passenger" variant doesn't get any extra aliases (sorry) - suiteAliases=() - ;; - *) - suiteAliases=( "${variantAliases[@]/%/-$suite}" ) + alpine"$alpine") + variantAliases+=( "${versionAliases[@]/%/-alpine}" ) + variantAliases=( "${variantAliases[@]//latest-/}" ) ;; esac - suiteAliases=( "${suiteAliases[@]//latest-/}" ) - variantAliases+=( "${suiteAliases[@]}" ) - - case "$variant" in - passenger) variantArches='amd64' ;; # https://github.com/docker-library/redmine/pull/87#issuecomment-323877678 - *) variantArches="${parentRepoToArches[$variantParent]}" ;; - esac - - if [ "$variant" != 'alpine' ]; then - # the "gosu" Debian package isn't available on mips64le - variantArches="$(sed <<<" $variantArches " -e 's/ mips64le / /g')" - fi echo cat <<-EOE diff --git a/update.sh b/update.sh index 717b989..bac2d75 100755 --- a/update.sh +++ b/update.sh @@ -1,71 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -# see https://www.redmine.org/projects/redmine/wiki/redmineinstall -defaultRubyVersion='3.1' -declare -A rubyVersions=( -) - cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -versions=( "$@" ) -if [ ${#versions[@]} -eq 0 ]; then - versions=( */ ) -fi -versions=( "${versions[@]%/}" ) - -# https://github.com/docker-library/redmine/issues/256 -downloadsPage="$(curl -fsSL 'https://redmine.org/projects/redmine/wiki/Download')" - -releasesUrl='https://www.redmine.org/releases' -versionsPage="$(curl -fsSL "$releasesUrl")" - -allVersions="$( - sed <<<"$versionsPage"$'\n'"$downloadsPage" \ - -rne 's/.*redmine-([0-9.]+)[.]tar[.]gz.*/\1/p' \ - | sort -ruV -)" - -for version in "${versions[@]}"; do - ourVersions="$(grep -E "^$version[.]" <<<"$allVersions")" - fullVersion= - for tryVersion in $ourVersions; do - url="$releasesUrl/redmine-$tryVersion.tar.gz" - if sha256="$(curl -fsSL "$url.sha256" 2>/dev/null)" && sha256="$(cut -d' ' -f1 <<<"$sha256")" && [ -n "$sha256" ]; then - fullVersion="$tryVersion" - break - fi - if urlLine="$(grep -oEm1 'href="https?://[^"]+/'"redmine-$tryVersion.tar.gz"'".*sha256:.*' <<<"$downloadsPage")" && url="$(cut -d'"' -f2 <<<"$urlLine")" && [ -n "$url" ] && sha256="$(grep -oEm1 'sha256:[[:space:]]*[a-f0-9]{64}' <<<"$urlLine")" && [ -n "$sha256" ] && sha256="${sha256: -64}"; then - fullVersion="$tryVersion" - break - fi - done - if [ -z "$fullVersion" ]; then - echo >&2 "error: failed to find full version for '$version'" - exit 1 - fi - - rubyVersion="${rubyVersions[$version]:-$defaultRubyVersion}" - - text="ruby $rubyVersion" - - echo "$version: $fullVersion ($text)" - - commonSedArgs=( - -r - -e 's/%%REDMINE_VERSION%%/'"$fullVersion"'/' - -e 's/%%RUBY_VERSION%%/'"$rubyVersion"'/' - -e 's!%%REDMINE_DOWNLOAD_URL%%!'"$url"'!' - -e 's/%%REDMINE_DOWNLOAD_SHA256%%/'"$sha256"'/' - -e 's/%%REDMINE%%/redmine:'"$version"'/' - ) - - mkdir -p "$version" - cp docker-entrypoint.sh "$version/" - sed "${commonSedArgs[@]}" Dockerfile-debian.template > "$version/Dockerfile" - - mkdir -p "$version/alpine" - cp docker-entrypoint.sh "$version/alpine/" - sed -i -e 's/gosu/su-exec/g' "$version/alpine/docker-entrypoint.sh" - sed "${commonSedArgs[@]}" Dockerfile-alpine.template > "$version/alpine/Dockerfile" -done +./versions.sh "$@" +./apply-templates.sh "$@" diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..26a6920 --- /dev/null +++ b/versions.json @@ -0,0 +1,16 @@ +{ + "5.0": { + "alpine": "3.18", + "debian": "bookworm", + "downloadUrl": "https://www.redmine.org/releases/redmine-5.0.6.tar.gz", + "ruby": { + "version": "3.1" + }, + "sha256": "488fe08f37a8eb1011415922a8ea743b7f38d8a7a5f8822950a34a375dcf08ee", + "variants": [ + "bookworm", + "alpine3.18" + ], + "version": "5.0.6" + } +} diff --git a/versions.sh b/versions.sh new file mode 100755 index 0000000..59ba63a --- /dev/null +++ b/versions.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +supportedDebianSuites=( + bookworm +) +supportedAlpineVersions=( + 3.18 +) + +defaultDebianSuite="${supportedDebianSuites[0]}" +declare -A debianSuites=( + #[5.0]='bookworm' +) +defaultAlpineVersion="${supportedAlpineVersions[0]}" +declare -A alpineVersions=( + #[5.0]='3.16' +) +# see https://www.redmine.org/projects/redmine/wiki/redmineinstall +defaultRubyVersion='3.1' +declare -A rubyVersions=( +) + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +versions=( "$@" ) +if [ ${#versions[@]} -eq 0 ]; then + versions=( */ ) + json='{}' +else + json="$(< versions.json)" +fi +versions=( "${versions[@]%/}" ) + +releasesUrl='https://www.redmine.org/releases' + +declare packages= + +fetch_package_list() { + local -; set +x # make sure running with "set -x" doesn't spam the terminal with the raw package lists + + if [ -z "${packages}" ]; then + packages="$(curl -fsSL "$releasesUrl")" + fi +} + +get_version() { + local version="$1"; shift + + fullVersion="$( + sed <<<"$packages" \ + -rne 's/.*redmine-([0-9.]+)[.]tar[.]gz.*/\1/p' \ + | cut -d/ -f3 \ + | cut -d^ -f1 \ + | grep -e "^$version" \ + | sort -urV \ + | head -1 + )" + + if [ -z "$fullVersion" ]; then + echo >&2 "error: failed to find full version for '$version'" + exit 1 + fi + + downloadUrl="$releasesUrl/redmine-$fullVersion.tar.gz" + sha256="$(curl -fsSL $downloadUrl.sha256 | awk '{print $1}')" +} + +for version in "${versions[@]}"; do + export version + + versionAlpineVersion="${alpineVersions[$version]:-$defaultAlpineVersion}" + versionDebianSuite="${debianSuites[$version]:-$defaultDebianSuite}" + versionRubyVersion="${rubyVersions[$version]:-$defaultRubyVersion}" + export versionAlpineVersion versionDebianSuite versionRubyVersion + + doc="$(jq -nc '{ + alpine: env.versionAlpineVersion, + debian: env.versionDebianSuite, + }')" + + fetch_package_list + get_version "$version" + + for suite in "${supportedDebianSuites[@]}"; do + export suite + doc="$(jq <<<"$doc" -c ' + .variants += [ env.suite ] + ')" + done + + for alpineVersion in "${supportedAlpineVersions[@]}"; do + doc="$(jq <<<"$doc" -c --arg v "$alpineVersion" ' + .variants += [ "alpine" + $v ] + ')" + done + + echo "$version: $fullVersion" + + export fullVersion downloadUrl sha256 + json="$(jq <<<"$json" -c --argjson doc "$doc" ' + .[env.version] = ($doc + { + version: env.fullVersion, + downloadUrl: env.downloadUrl, + sha256: env.sha256, + "ruby": { + version: env.versionRubyVersion + } + }) + ')" + +done + +jq <<<"$json" -S . > versions.json