diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..14a1122 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +/*/**/Dockerfile linguist-generated +/*/**/docker-entrypoint.sh linguist-generated +/Dockerfile*.template linguist-language=Dockerfile 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/alpine/Dockerfile b/1/alpine/Dockerfile similarity index 85% rename from alpine/Dockerfile rename to 1/alpine/Dockerfile index ebec1e5..51b4f69 100644 --- a/alpine/Dockerfile +++ b/1/alpine/Dockerfile @@ -1,3 +1,9 @@ +# +# 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 @@ -7,9 +13,10 @@ RUN addgroup -g 11211 memcache && adduser -D -u 11211 -G memcache memcache RUN apk add --no-cache libsasl ENV MEMCACHED_VERSION 1.6.22 +ENV MEMCACHED_URL https://memcached.org/files/memcached-1.6.22.tar.gz ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 -RUN set -x \ +RUN set -eux \ \ && apk add --no-cache --virtual .build-deps \ ca-certificates \ @@ -26,7 +33,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_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 \ @@ -47,7 +54,8 @@ RUN set -x \ \ && make install \ \ - && cd / && rm -rf /usr/src/memcached \ + && cd / \ + && rm -rf /usr/src/memcached \ \ && runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ diff --git a/alpine/docker-entrypoint.sh b/1/alpine/docker-entrypoint.sh similarity index 100% rename from alpine/docker-entrypoint.sh rename to 1/alpine/docker-entrypoint.sh diff --git a/debian/Dockerfile b/1/debian/Dockerfile similarity index 81% rename from debian/Dockerfile rename to 1/debian/Dockerfile index bd41ec1..4ebd57d 100644 --- a/debian/Dockerfile +++ b/1/debian/Dockerfile @@ -1,3 +1,9 @@ +# +# 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 @@ -12,9 +18,10 @@ RUN set -eux; \ rm -rf /var/lib/apt/lists/* ENV MEMCACHED_VERSION 1.6.22 +ENV MEMCACHED_URL https://memcached.org/files/memcached-1.6.22.tar.gz ENV MEMCACHED_SHA1 7a691f390d59616dbebfc9e2e4942d499c39a338 -RUN set -x \ +RUN set -eux \ \ && savedAptMark="$(apt-mark showmanual)" \ && apt-get update \ @@ -32,7 +39,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_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 \ @@ -40,21 +47,12 @@ RUN set -x \ \ && 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-extstore \ --enable-sasl \ --enable-sasl-pwdb \ --enable-tls \ - $enableExtstore \ && nproc="$(nproc)" \ && make -j "$nproc" \ \ @@ -65,7 +63,8 @@ RUN set -x \ \ && make install \ \ - && cd / && rm -rf /usr/src/memcached \ + && cd / \ + && rm -rf /usr/src/memcached \ \ && apt-mark auto '.*' > /dev/null \ && apt-mark manual $savedAptMark > /dev/null \ diff --git a/debian/docker-entrypoint.sh b/1/debian/docker-entrypoint.sh similarity index 100% rename from debian/docker-entrypoint.sh rename to 1/debian/docker-entrypoint.sh diff --git a/Dockerfile.template b/Dockerfile.template new file mode 100644 index 0000000..b1ee7c4 --- /dev/null +++ b/Dockerfile.template @@ -0,0 +1,126 @@ +{{ if env.variant == "alpine" then ( -}} +FROM alpine:{{ .alpine.version }} +{{ ) else ( -}} +FROM debian:{{ .debian.version }}-slim +{{ ) end -}} + +# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added +{{ if env.variant == "alpine" then ( -}} +RUN addgroup -g 11211 memcache && adduser -D -u 11211 -G memcache memcache +{{ ) else ( -}} +RUN groupadd --system --gid 11211 memcache && useradd --system --gid memcache --uid 11211 memcache +{{ ) end -}} + +# ensure SASL's "libplain.so" is installed as per https://github.com/memcached/memcached/wiki/SASLHowto +{{ if env.variant == "alpine" then ( -}} +RUN apk add --no-cache libsasl +{{ ) else ( -}} +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libsasl2-modules \ + ; \ + rm -rf /var/lib/apt/lists/* +{{ ) end -}} + +ENV MEMCACHED_VERSION {{ .version }} +ENV MEMCACHED_URL {{ .url }} +ENV MEMCACHED_SHA1 {{ .sha1 }} + +RUN set -eux \ + \ +{{ if env.variant == "alpine" then ( -}} + && 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 \ +{{ ) else ( -}} + && 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/* \ +{{ ) end -}} + \ + && wget -O memcached.tar.gz "$MEMCACHED_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" \ + \ +{{ if env.variant == "debian" then ( -}} +# 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 \ +{{ ) else "" end -}} + && make test PARALLEL="$nproc" \ +{{ if env.variant == "debian" then ( -}} + && mv /etc/ssl/openssl.cnf.bak /etc/ssl/openssl.cnf \ +{{ ) else "" end -}} + \ + && make install \ + \ + && cd / \ + && rm -rf /usr/src/memcached \ + \ +{{ if env.variant == "alpine" then ( -}} + && 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 \ +{{ ) else ( -}} + && 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 \ +{{ ) end -}} + \ + && 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/apply-templates.sh b/apply-templates.sh new file mode 100755 index 0000000..2af30c7 --- /dev/null +++ b/apply-templates.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +[ -f versions.json ] # run "versions.sh" first + +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 + rm -rf "$version" + + for variant in debian alpine; do + export version variant + + dir="$version/$variant" + + echo "processing $dir ..." + + mkdir -p "$dir" + + { + generated_warning + gawk -f "$jqt" Dockerfile.template + } > "$dir/Dockerfile" + + cp -a docker-entrypoint.sh "$dir/" + 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..3df0b30 100755 --- a/generate-stackbrew-library.sh +++ b/generate-stackbrew-library.sh @@ -1,9 +1,18 @@ -#!/bin/bash -set -eu +#!/usr/bin/env bash +set -Eeuo pipefail + +declare -A aliases=( + [1]='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,10 @@ join() { echo "${out#$sep}" } -for variant in debian alpine; do - commit="$(dirCommit "$variant")" +for version; do + export version - 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 +78,44 @@ for variant in debian alpine; do done versionAliases+=( $fullVersion - latest + ${aliases[$version]:-} ) - if [ "$variant" = 'debian' ]; then - variantAliases=( "${versionAliases[@]}" ) - else - variantAliases=( "${versionAliases[@]/%/-$variant}" ) - variantAliases=( "${variantAliases[@]//latest-/}" ) - fi + for variant in debian alpine; do + export variant + dir="$version/$variant" + + commit="$(dirCommit "$dir")" - parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$variant/Dockerfile")" + if [ "$variant" = 'debian' ]; then + variantAliases=( "${versionAliases[@]}" ) + else + variantAliases=( "${versionAliases[@]/%/-$variant}" ) + variantAliases=( "${variantAliases[@]//latest-/}" ) + fi - suite="${parent#*:}" # "bookworm-slim", "bookworm" - suite="${suite%-slim}" # "bookworm" - if [ "$variant" = 'alpine' ]; then - suite="alpine$suite" # "alpine3.14" + parent="$(awk 'toupper($1) == "FROM" { print $2 }' "$dir/Dockerfile")" + arches="${parentRepoToArches[$parent]}" + + suite="${parent#*:}" # "bookworm-slim", "bookworm" + suite="${suite%-slim}" # "bookworm" + if [ "$variant" = 'alpine' ]; then + suite="alpine$suite" # "alpine3.18" + fi 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 + suiteAliases=( "${suiteAliases[@]//latest-/}" ) + variantAliases+=( "${suiteAliases[@]}" ) + + # 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..8f5d5cb --- /dev/null +++ b/versions.json @@ -0,0 +1,13 @@ +{ + "1": { + "version": "1.6.22", + "url": "https://memcached.org/files/memcached-1.6.22.tar.gz", + "sha1": "7a691f390d59616dbebfc9e2e4942d499c39a338", + "alpine": { + "version": "3.18" + }, + "debian": { + "version": "bookworm" + } + } +} diff --git a/versions.sh b/versions.sh new file mode 100755 index 0000000..915398d --- /dev/null +++ b/versions.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +alpine="$( + bashbrew cat --format '{{ .TagEntry.Tags | join "\n" }}' https://github.com/docker-library/official-images/raw/HEAD/library/alpine:latest \ + | grep -E '^[0-9]+[.][0-9]+$' +)" +[ "$(wc -l <<<"$alpine")" = 1 ] +export alpine + +debian="$( + bashbrew cat --format '{{ .TagEntry.Tags | join "\n" }}' https://github.com/docker-library/official-images/raw/HEAD/library/debian:latest \ + | grep -vE '^latest$|[0-9.-]' \ + | head -1 +)" +[ "$(wc -l <<<"$debian")" = 1 ] +export debian + +cd "$(dirname "$(readlink -f "$BASH_SOURCE")")" + +versions=( "$@" ) +if [ ${#versions[@]} -eq 0 ]; then + versions=( */ ) + json='{}' +else + json="$(< versions.json)" +fi +versions=( "${versions[@]%/}" ) + +possibles="$( + 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 +)" + +for version in "${versions[@]}"; do + export version + + versionPossibles="$(grep <<<"$possibles" -E "^$version([.-]|\$)")" + + fullVersion= + sha1= + url= + for possible in $versionPossibles; do + url="https://memcached.org/files/memcached-$possible.tar.gz" + if sha1="$(curl -fsSL "$url.sha1")" && [ -n "$sha1" ]; then + sha1="${sha1%% *}" + fullVersion="$possible" + break + fi + done + if [ -z "$fullVersion" ]; then + echo >&2 "error: could not determine latest release for $version" + exit 1 + fi + [ -n "$sha1" ] + [ -n "$url" ] + + echo "$version: $fullVersion" + + export fullVersion sha1 url + json="$(jq <<<"$json" -c ' + .[env.version] = { + version: env.fullVersion, + url: env.url, + sha1: env.sha1, + alpine: { version: env.alpine }, + debian: { version: env.debian }, + } + ')" +done + +jq <<<"$json" . > versions.json