diff --git a/.github/workflows/verify-templating.yml b/.github/workflows/verify-templating.yml new file mode 100644 index 0000000..14497be --- /dev/null +++ b/.github/workflows/verify-templating.yml @@ -0,0 +1,22 @@ +name: Verify Templating + +on: + pull_request: + push: + +defaults: + run: + shell: 'bash -Eeuo pipefail -x {0}' + +jobs: + apply-templates: + name: Check For Uncomitted Changes + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Apply Templates + run: ./apply-templates.sh + - name: Check Git Status + run: | + status="$(git status --short)" + [ -z "$status" ] 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/1.6/alpine3.18/Dockerfile b/1.6/alpine3.18/Dockerfile new file mode 100644 index 0000000..8c496e9 --- /dev/null +++ b/1.6/alpine3.18/Dockerfile @@ -0,0 +1,76 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM alpine:3.18 + +# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added +RUN addgroup -g 11211 memcache && adduser -D -u 11211 -G memcache memcache + +# ensure SASL's "libplain.so" is installed as per https://github.com/memcached/memcached/wiki/SASLHowto +RUN apk add --no-cache libsasl + +ENV MEMCACHED_VERSION 1.6.22 +ENV MEMCACHED_DOWNLOAD_URL https://memcached.org/files/memcached-1.6.22.tar.gz +ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 + +RUN set -x \ + \ + && apk add --no-cache --virtual .build-deps \ + ca-certificates \ + coreutils \ + cyrus-sasl-dev \ + gcc \ + libc-dev \ + libevent-dev \ + linux-headers \ + make \ + openssl \ + openssl-dev \ + perl \ + perl-io-socket-ssl \ + perl-utils \ + \ + && wget -O memcached.tar.gz "$MEMCACHED_DOWNLOAD_URL" \ + && echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \ + && mkdir -p /usr/src/memcached \ + && tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \ + && rm memcached.tar.gz \ + \ + && cd /usr/src/memcached \ + \ + && ./configure \ + --build="$gnuArch" \ + --enable-extstore \ + --enable-sasl \ + --enable-sasl-pwdb \ + --enable-tls \ + && nproc="$(nproc)" \ + && make -j "$nproc" \ + \ + && make test PARALLEL="$nproc" \ + \ + && make install \ + \ + && cd / && rm -rf /usr/src/memcached \ + \ + && runDeps="$( \ + scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ + | tr ',' '\n' \ + | sort -u \ + | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ + )" \ + && apk add --no-network --virtual .memcached-rundeps $runDeps \ + && apk del --no-network .build-deps \ + \ + && memcached -V + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + +USER memcache +EXPOSE 11211 +CMD ["memcached"] diff --git a/alpine/docker-entrypoint.sh b/1.6/alpine3.18/docker-entrypoint.sh similarity index 100% rename from alpine/docker-entrypoint.sh rename to 1.6/alpine3.18/docker-entrypoint.sh diff --git a/1.6/bookworm/Dockerfile b/1.6/bookworm/Dockerfile new file mode 100644 index 0000000..5cb9571 --- /dev/null +++ b/1.6/bookworm/Dockerfile @@ -0,0 +1,96 @@ +# +# NOTE: THIS DOCKERFILE IS GENERATED VIA "apply-templates.sh" +# +# PLEASE DO NOT EDIT IT DIRECTLY. +# + +FROM debian:bookworm-slim + +# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added +RUN groupadd --system --gid 11211 memcache && useradd --system --gid memcache --uid 11211 memcache + +# ensure SASL's "libplain.so" is installed as per https://github.com/memcached/memcached/wiki/SASLHowto +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libsasl2-modules \ + ; \ + rm -rf /var/lib/apt/lists/* + +ENV MEMCACHED_VERSION 1.6.22 +ENV MEMCACHED_DOWNLOAD_URL https://memcached.org/files/memcached-1.6.22.tar.gz +ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 + +RUN set -x \ + \ + && savedAptMark="$(apt-mark showmanual)" \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + dpkg-dev \ + gcc \ + libc6-dev \ + libevent-dev \ + libio-socket-ssl-perl \ + libsasl2-dev \ + libssl-dev \ + make \ + perl \ + wget \ + && rm -rf /var/lib/apt/lists/* \ + \ + && wget -O memcached.tar.gz "$MEMCACHED_DOWNLOAD_URL" \ + && echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \ + && mkdir -p /usr/src/memcached \ + && tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \ + && rm memcached.tar.gz \ + \ + && cd /usr/src/memcached \ + \ + && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ + && enableExtstore="$( \ +# https://github.com/docker-library/memcached/pull/38 + case "$gnuArch" in \ +# https://github.com/memcached/memcached/issues/381 "--enable-extstore on s390x (IBM System Z mainframe architecture) fails tests" + s390x-*) ;; \ + *) echo '--enable-extstore' ;; \ + esac \ + )" \ + && ./configure \ + --build="$gnuArch" \ + --enable-sasl \ + --enable-sasl-pwdb \ + --enable-tls \ + $enableExtstore \ + && nproc="$(nproc)" \ + && make -j "$nproc" \ + \ +# see https://github.com/docker-library/memcached/pull/54#issuecomment-562797748 and https://bugs.debian.org/927461 for why we have to munge openssl.cnf + && sed -i.bak 's/SECLEVEL=2/SECLEVEL=1/g' /etc/ssl/openssl.cnf \ + && make test PARALLEL="$nproc" \ + && mv /etc/ssl/openssl.cnf.bak /etc/ssl/openssl.cnf \ + \ + && make install \ + \ + && cd / && rm -rf /usr/src/memcached \ + \ + && apt-mark auto '.*' > /dev/null \ + && apt-mark manual $savedAptMark > /dev/null \ + && find /usr/local -type f -executable -exec ldd '{}' ';' \ + | awk '/=>/ { so = $(NF-1); if (index(so, "/usr/local/") == 1) { next }; gsub("^/(usr/)?", "", so); print so }' \ + | sort -u \ + | xargs -r dpkg-query --search \ + | cut -d: -f1 \ + | sort -u \ + | xargs -r apt-mark manual \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ + \ + && memcached -V + +COPY docker-entrypoint.sh /usr/local/bin/ +RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat +ENTRYPOINT ["docker-entrypoint.sh"] + +USER memcache +EXPOSE 11211 +CMD ["memcached"] diff --git a/debian/docker-entrypoint.sh b/1.6/bookworm/docker-entrypoint.sh similarity index 100% rename from debian/docker-entrypoint.sh rename to 1.6/bookworm/docker-entrypoint.sh diff --git a/alpine/Dockerfile b/Dockerfile-alpine.template similarity index 88% rename from alpine/Dockerfile rename to Dockerfile-alpine.template index ebec1e5..580bfeb 100644 --- a/alpine/Dockerfile +++ b/Dockerfile-alpine.template @@ -1,4 +1,4 @@ -FROM alpine:3.18 +FROM alpine:{{ env.variant | ltrimstr("alpine") }} # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN addgroup -g 11211 memcache && adduser -D -u 11211 -G memcache memcache @@ -6,8 +6,9 @@ RUN addgroup -g 11211 memcache && adduser -D -u 11211 -G memcache memcache # ensure SASL's "libplain.so" is installed as per https://github.com/memcached/memcached/wiki/SASLHowto RUN apk add --no-cache libsasl -ENV MEMCACHED_VERSION 1.6.22 -ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 +ENV MEMCACHED_VERSION {{ .version }} +ENV MEMCACHED_DOWNLOAD_URL {{ .downloadUrl }} +ENV MEMCACHED_SHA1 {{ .sha1 }} RUN set -x \ \ @@ -26,7 +27,7 @@ RUN set -x \ perl-io-socket-ssl \ perl-utils \ \ - && wget -O memcached.tar.gz "https://memcached.org/files/memcached-$MEMCACHED_VERSION.tar.gz" \ + && wget -O memcached.tar.gz "$MEMCACHED_DOWNLOAD_URL" \ && echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \ && mkdir -p /usr/src/memcached \ && tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \ diff --git a/debian/Dockerfile b/Dockerfile-debian.template similarity index 92% rename from debian/Dockerfile rename to Dockerfile-debian.template index bd41ec1..2189824 100644 --- a/debian/Dockerfile +++ b/Dockerfile-debian.template @@ -1,4 +1,4 @@ -FROM debian:bookworm-slim +FROM debian:{{ env.variant }}-slim # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added RUN groupadd --system --gid 11211 memcache && useradd --system --gid memcache --uid 11211 memcache @@ -11,8 +11,9 @@ RUN set -eux; \ ; \ rm -rf /var/lib/apt/lists/* -ENV MEMCACHED_VERSION 1.6.22 -ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 +ENV MEMCACHED_VERSION {{ .version }} +ENV MEMCACHED_DOWNLOAD_URL {{ .downloadUrl }} +ENV MEMCACHED_SHA1 {{ .sha1 }} RUN set -x \ \ @@ -32,7 +33,7 @@ RUN set -x \ wget \ && rm -rf /var/lib/apt/lists/* \ \ - && wget -O memcached.tar.gz "https://memcached.org/files/memcached-$MEMCACHED_VERSION.tar.gz" \ + && wget -O memcached.tar.gz "$MEMCACHED_DOWNLOAD_URL" \ && echo "$MEMCACHED_SHA1 memcached.tar.gz" | sha1sum -c - \ && mkdir -p /usr/src/memcached \ && tar -xzf memcached.tar.gz -C /usr/src/memcached --strip-components=1 \ 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/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..35beb62 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +# first arg is `-f` or `--some-option` +if [ "${1#-}" != "$1" ]; then + set -- memcached "$@" +fi + +exec "$@" diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh index 42f33f2..0f32373 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -1,9 +1,18 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu +declare -A aliases=( + [1.6]='latest' +) + self="$(basename "$BASH_SOURCE")" cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" +if [ "$#" -eq 0 ]; then + versions="$(jq -r 'keys | map(@sh) | join(" ")' versions.json)" + eval "set -- $versions" +fi + # get the most recent commit which modified any of "$@" fileCommit() { git log -1 --format='format:%H' HEAD -- "$@" @@ -57,10 +66,16 @@ join() { echo "${out#$sep}" } -for variant in debian alpine; do - commit="$(dirCommit "$variant")" +for version; do + export version + + 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="$(git show "$commit":"$variant/Dockerfile" | awk '$1 == "ENV" && $2 == "MEMCACHED_VERSION" { print $3; exit }')" + fullVersion="$(jq -r '.[env.version].version' versions.json)" versionAliases=() while [ "${fullVersion%.*}" != "$fullVersion" ]; do @@ -69,40 +84,42 @@ for variant in debian alpine; do done versionAliases+=( $fullVersion - latest + ${aliases[$version]:-} ) - if [ "$variant" = 'debian' ]; then - variantAliases=( "${versionAliases[@]}" ) - else + for variant in "${variants[@]}"; do + dir="$version/$variant" + commit="$(dirCommit "$dir")" + + parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$dir/Dockerfile")" + arches="${parentRepoToArches[$parent]}" + variantAliases=( "${versionAliases[@]/%/-$variant}" ) variantAliases=( "${variantAliases[@]//latest-/}" ) - fi - - parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$variant/Dockerfile")" - - suite="${parent#*:}" # "bookworm-slim", "bookworm" - suite="${suite%-slim}" # "bookworm" - if [ "$variant" = 'alpine' ]; then - suite="alpine$suite" # "alpine3.14" - suiteAliases=( "${versionAliases[@]/%/-$suite}" ) - else - suiteAliases=( "${variantAliases[@]/%/-$suite}" ) - fi - suiteAliases=( "${suiteAliases[@]//latest-/}" ) - variantAliases+=( "${suiteAliases[@]}" ) - - arches="${parentRepoToArches[$parent]}" - - # https://github.com/memcached/memcached/issues/799 - # https://github.com/docker-library/memcached/issues/69 - arches="$(sed -r -e 's/ arm32v6 / /g' <<<" $arches ")" - - echo - cat <<-EOE - Tags: $(join ', ' "${variantAliases[@]}") - Architectures: $(join ', ' $arches) - GitCommit: $commit - Directory: $variant - EOE + + case "$variant" in + "$debian") + variantAliases=( + "${versionAliases[@]}" + "${variantAliases[@]}" + ) + ;; + alpine"$alpine") + variantAliases+=( "${versionAliases[@]/%/-alpine}" ) + variantAliases=( "${variantAliases[@]//latest-/}" ) + ;; + esac + + # https://github.com/memcached/memcached/issues/799 + # https://github.com/docker-library/memcached/issues/69 + arches="$(sed -r -e 's/ arm32v6 / /g' <<<" $arches ")" + + echo + cat <<-EOE + Tags: $(join ', ' "${variantAliases[@]}") + Architectures: $(join ', ' $arches) + GitCommit: $commit + Directory: $dir + EOE + done done diff --git a/update.sh b/update.sh index 72c1b69..bac2d75 100755 --- a/update.sh +++ b/update.sh @@ -1,31 +1,7 @@ #!/usr/bin/env bash set -Eeuo pipefail -versions="$( - git ls-remote --tags 'https://github.com/memcached/memcached.git' \ - | cut -d/ -f3- \ - | cut -d^ -f1 \ - | grep -E '^[0-9]+' \ - | grep -vE -- '-(beta|rc)' \ - | sort -urV -)" +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" -fullVersion= -sha1= -for version in $versions; do - if sha1="$(curl -fsSL "https://memcached.org/files/memcached-$version.tar.gz.sha1")" && [ -n "$sha1" ]; then - sha1="${sha1%% *}" - fullVersion="$version" - break - fi -done -if [ -z "$fullVersion" ] || [ -z "$sha1" ]; then - echo >&2 "error: could not determine latest release of memcached" - exit 1 -fi - -set -x -sed -ri \ - -e 's/^(ENV MEMCACHED_VERSION) .*/\1 '"$fullVersion"'/' \ - -e 's/^(ENV MEMCACHED_SHA1) .*/\1 '"$sha1"'/' \ - */Dockerfile +./versions.sh "$@" +./apply-templates.sh "$@" diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..b14da3c --- /dev/null +++ b/versions.json @@ -0,0 +1,13 @@ +{ + "1.6": { + "alpine": "3.18", + "debian": "bookworm", + "downloadUrl": "https://memcached.org/files/memcached-1.6.22.tar.gz", + "sha1": "7a691f390d59616dbebfc9e2e4942d499c39a338", + "variants": [ + "bookworm", + "alpine3.18" + ], + "version": "1.6.22" + } +} diff --git a/versions.sh b/versions.sh new file mode 100755 index 0000000..e326473 --- /dev/null +++ b/versions.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# we will support at most two entries in each of these lists, and both should be in descending order +supportedDebianSuites=( + bookworm +) +supportedAlpineVersions=( + 3.18 +) +defaultDebianSuite="${supportedDebianSuites[0]}" +declare -A debianSuites=( + #[7.2]='3.17' +) +defaultAlpineVersion="${supportedAlpineVersions[0]}" +declare -A alpineVersions=( + #[14]='3.16' +) + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +versions=( "$@" ) +if [ ${#versions[@]} -eq 0 ]; then + versions=( */ ) + json='{}' +else + json="$(< versions.json)" +fi +versions=( "${versions[@]%/}" ) + +packagesBase='https://github.com/memcached/memcached.git' + +packages=() + +fetch_package_list() { + local -; set +x # make sure running with "set -x" doesn't spam the terminal with the raw package lists + + if [ "${#packages[@]}" -le 0 ]; then + packages=( $( + git ls-remote --tags 'https://github.com/memcached/memcached.git' \ + | cut -d/ -f3- \ + | cut -d^ -f1 \ + | grep -E '^[0-9]+' \ + | grep -vE -- '-(beta|rc)' \ + | sort -urV + ) ) + fi +} + +get_version() { + local version="$1"; shift + + versionPattern="^${version/\./\\.}\.[0-9]*$" + filteredVersions=($(printf "%s\n" "${packages[@]}" | grep -E "${versionPattern}")) + fullVersion="${filteredVersions[0]}" + + downloadUrl="https://memcached.org/files/memcached-$fullVersion.tar.gz" + + shaHash="$(curl -fsSL "${downloadUrl}.sha1")" + + if [ -n "$shaHash" ]; then + shaHash="${shaHash%% *}" + fi +} + +for version in "${versions[@]}"; do + export version + + versionAlpineVersion="${alpineVersions[$version]:-$defaultAlpineVersion}" + versionDebianSuite="${debianSuites[$version]:-$defaultDebianSuite}" + export versionAlpineVersion versionDebianSuite + + doc="$(jq -nc '{ + alpine: env.versionAlpineVersion, + debian: env.versionDebianSuite, + }')" + + fetch_package_list + get_version "$version" + + if [ -z "$fullVersion" ] || [ -z "$shaHash" ]; then + echo >&2 "error: could not determine latest release of memcached" + exit 1 + fi + + 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 shaHash downloadUrl + json="$(jq <<<"$json" -c --argjson doc "$doc" ' + .[env.version] = ($doc + { + version: env.fullVersion, + downloadUrl: env.downloadUrl, + sha1: env.shaHash + }) + ')" +done + +jq <<<"$json" -S . > versions.json