diff --git a/.github/workflows/build-ruby.yml b/.github/workflows/build-ruby.yml index de58bbc..57f9fee 100644 --- a/.github/workflows/build-ruby.yml +++ b/.github/workflows/build-ruby.yml @@ -22,71 +22,186 @@ jobs: fail-fast: false matrix: include: + # gnu - engine: ruby version: "2.1" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.2" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.3" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.4" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.5" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.6" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "2.7" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "3.0" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "3.1" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "3.2" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "3.3" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: ruby version: "3.4" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: jruby version: "9.2" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: jruby version: "9.3" + libc: gnu + arch: ["x86_64", "aarch64"] - engine: jruby version: "9.4" + libc: gnu + arch: ["x86_64", "aarch64"] + # musl + - engine: ruby + version: "2.1" + libc: musl + arch: ["x86_64"] + - engine: ruby + version: "2.2" + libc: musl + arch: ["x86_64"] + - engine: ruby + version: "2.3" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "2.4" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "2.5" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "2.6" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "2.7" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "3.0" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "3.1" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "3.2" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "3.3" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: ruby + version: "3.4" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: jruby + version: "9.2" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: jruby + version: "9.3" + libc: musl + arch: ["x86_64", "aarch64"] + - engine: jruby + version: "9.4" + libc: musl + arch: ["x86_64", "aarch64"] runs-on: ubuntu-latest - name: Build (${{ matrix.engine }} ${{ matrix.version }}) + name: Build (${{ matrix.engine }} ${{ matrix.version }} ${{ matrix.libc }}) steps: + - name: Set up Docker + uses: crazy-max/ghaction-setup-docker@v3 + with: + daemon-config: | + { + "features": { + "containerd-snapshotter": true + } + } - name: Set variables id: vars run: | echo "SRC=src/engines/${{ matrix.engine }}/${{ matrix.version }}" >> $GITHUB_OUTPUT echo "IMAGE=${{ env.REGISTRY }}/${{ env.REPO }}/engines/${{ matrix.engine }}" >> $GITHUB_OUTPUT - echo "TAG=${{ matrix.version }}" >> $GITHUB_OUTPUT - echo "DOCKERFILE=src/engines/${{ matrix.engine }}/${{ matrix.version }}/Dockerfile" >> $GITHUB_OUTPUT + echo "RELEASE_TAG=${{ matrix.version }}-${{ matrix.libc }}" >> $GITHUB_OUTPUT + echo "TAG=${{ matrix.version }}-${{ matrix.libc }}-gha${{ github.run_id }}-g${{ github.sha }}" >> $GITHUB_OUTPUT + echo "DOCKERFILE=src/engines/${{ matrix.engine }}/${{ matrix.version }}/Dockerfile.${{ matrix.libc }}" >> $GITHUB_OUTPUT + echo "DOCKER_PLATFORMS=$(echo ${{ join(matrix.arch) }} | tr ',' '\n' | sed 's/^/linux\//' | paste -s -d, -)" >> $GITHUB_OUTPUT - name: Checkout uses: actions/checkout@v4 - # Using docker-container engine enables advanced buildx features - - name: Set up Docker container engine - run: | - docker buildx create --name=container --driver=docker-container --use --bootstrap - # First, build image for x86_64 as it will fail fast # # Tagging is necessary to reference the image for the testing step # Tagging is done separately to avoid interfrence with caching - name: Build single-arch image (x86_64) + if: ${{ contains(matrix.arch, 'x86_64') }} run: | - docker buildx build ${{ steps.vars.outputs.SRC }} --builder=container --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} --output=type=image,push=false --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }} + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --output=type=image,push=false --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }} - name: Tag single-arch image (x86_64) + if: ${{ contains(matrix.arch, 'x86_64') }} run: | - docker buildx build ${{ steps.vars.outputs.SRC }} --builder=container --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} --load --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --load --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} - name: Test single-arch image (x86_64) + if: ${{ contains(matrix.arch, 'x86_64') }} run: | docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'true' docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} ruby -e 'puts RUBY_DESCRIPTION' docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} gem --version docker run --platform linux/x86_64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} bundle --version - docker run --platform linux/x86_64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" -e BUNDLE_GEMFILE=gemfiles/${{ matrix.engine }}-${{ matrix.version }}.gemfile ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && bundle exec rake test' + docker run --platform linux/x86_64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" -e BUNDLE_GEMFILE=gemfiles/${{ matrix.engine }}-${{ matrix.version }}.gemfile ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install --with="test" --without="check ide" && bundle exec rake test' + + # TODO: hardcoded musl, unify gnu instead + # TODO: hardcoded tags, use proper tag building + - name: Tag single-arch image (x86_64) + if: ${{ contains(matrix.arch, 'x86_64') }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --load --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} + docker image list + - name: Build single-arch compiler image (x86_64) + if: ${{ contains(matrix.arch, 'x86_64') && matrix.libc == 'musl' }} + run: | + docker image list # image not in builder=container! + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc --output=type=image,push=false --platform linux/x86_64 -f ${{ steps.vars.outputs.DOCKERFILE }}.gcc + # TODO: add CI tag and test # Then, build image for aarch64 which, being emulated under qemu, is slower # @@ -94,30 +209,66 @@ jobs: # Tagging is done separately to avoid interfrence with caching # Start by enabling qemu for aarch64 - name: Enable aarch64 emulation (x86_64) + if: ${{ contains(matrix.arch, 'aarch64') }} run: | docker run --privileged --rm tonistiigi/binfmt --install arm64 - name: Build single-arch image (aarch64) + if: ${{ contains(matrix.arch, 'aarch64') }} run: | - docker buildx build ${{ steps.vars.outputs.SRC }} --builder=container --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} --output=type=image,push=false --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --output=type=image,push=false --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} - name: Tag single-arch image (aarch64) + if: ${{ contains(matrix.arch, 'aarch64') }} run: | - docker buildx build ${{ steps.vars.outputs.SRC }} --builder=container --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} --load --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --load --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} - name: Test single-arch image (aarch64) + if: ${{ contains(matrix.arch, 'aarch64') }} run: | docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'true' docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} ruby -e 'puts RUBY_DESCRIPTION' docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} gem --version docker run --platform linux/aarch64 --rm ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} bundle --version - docker run --platform linux/aarch64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" -e BUNDLE_GEMFILE=gemfiles/${{ matrix.engine }}-${{ matrix.version }}.gemfile ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install && bundle exec rake test' + docker run --platform linux/aarch64 --rm -v "${PWD}":"${PWD}" -w "${PWD}" -e BUNDLE_GEMFILE=gemfiles/${{ matrix.engine }}-${{ matrix.version }}.gemfile ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} /bin/sh -c 'bundle install --with="test" --without="check ide" && bundle exec rake test' + + # TODO: hardcoded musl, unify gnu instead + # TODO: hardcoded tags, use proper tag building + - name: Tag single-arch image (aarch64) + if: ${{ contains(matrix.arch, 'aarch64') }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --load --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} + - name: Build single-arch compiler image (aarch64) + if: ${{ contains(matrix.arch, 'aarch64') && matrix.libc == 'musl' }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc --output=type=image,push=false --platform linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }}.gcc + # TODO: add CI tag and test # Finally, assemble multi-arch image for a combined push to the registry # # This reruns docker build but layers are in the cache, so it's fast - name: Log in to the Container Registry - if: ${{ inputs.push }} run: | echo ${{ secrets.GITHUB_TOKEN }} | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin - - name: Build multi-arch image (x86_64, aarch64) + - name: Push CI run image (${{ join(matrix.arch, ', ') }}) + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform ${{ steps.vars.outputs.DOCKER_PLATFORMS }} -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} + - name: Push release image (${{ join(matrix.arch, ', ') }}) if: ${{ inputs.push }} run: | - docker buildx build ${{ steps.vars.outputs.SRC }} --builder=container --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform linux/x86_64,linux/aarch64 -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.TAG }} + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform ${{ steps.vars.outputs.DOCKER_PLATFORMS }} -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} + + # TODO: hardcoded, reuse strip-tags directive instead (or better, unify gnu) + - name: Push unqualified release image (${{ join(matrix.arch, ', ') }}) + if: ${{ inputs.push && matrix.libc == 'gnu' }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }} --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform ${{ steps.vars.outputs.DOCKER_PLATFORMS }} -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ matrix.version }} + + # TODO: hardcoded, reuse append-tags directive instead (or better, unify gnu) + - name: Push release compiler image (${{ join(matrix.arch, ', ') }}) + if: ${{ inputs.push && matrix.libc == 'gnu' }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform ${{ steps.vars.outputs.DOCKER_PLATFORMS }} -f ${{ steps.vars.outputs.DOCKERFILE }} --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc + + # TODO: hardcoded musl, unify gnu instead + - name: Push release compiler image (${{ join(matrix.arch, ', ') }}) + if: ${{ inputs.push && matrix.libc == 'musl' }} + run: | + docker buildx build ${{ steps.vars.outputs.SRC }} --cache-from=type=registry,ref=${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc --output=type=image,push=true --build-arg BUILDKIT_INLINE_CACHE=1 --platform ${{ steps.vars.outputs.DOCKER_PLATFORMS }} -f ${{ steps.vars.outputs.DOCKERFILE }}.gcc --tag ${{ steps.vars.outputs.IMAGE }}:${{ steps.vars.outputs.RELEASE_TAG }}-gcc diff --git a/src/engines/jruby/9.2/Dockerfile b/src/engines/jruby/9.2/Dockerfile.gnu similarity index 98% rename from src/engines/jruby/9.2/Dockerfile rename to src/engines/jruby/9.2/Dockerfile.gnu index 30dc4e8..ba7cf3c 100644 --- a/src/engines/jruby/9.2/Dockerfile +++ b/src/engines/jruby/9.2/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM eclipse-temurin:8-jammy AS jruby-9.2.21.0-jre8 # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/jruby/9.2/Dockerfile.musl b/src/engines/jruby/9.2/Dockerfile.musl new file mode 100644 index 0000000..159c00d --- /dev/null +++ b/src/engines/jruby/9.2/Dockerfile.musl @@ -0,0 +1,58 @@ +FROM eclipse-temurin:22-jdk-alpine AS jruby-9.2.21.0-jre22 + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +# Add a few packages for consistency +RUN apk add curl bash + +# Install JRuby, pinned for reproducibility +ENV JRUBY_VERSION 9.2.21.0 +ENV JRUBY_SHA256 dbf05fca4f61bd7d5131d9b83c5f4d1a249213c474b82def37e82013969c8b8a +RUN mkdir /opt/jruby \ + && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ + && echo "$JRUBY_SHA256 /tmp/jruby.tar.gz" | sha256sum -c - \ + && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ + && rm /tmp/jruby.tar.gz \ + && ln -sf /opt/jruby/bin/jruby /usr/local/bin/ruby +ENV PATH /opt/jruby/bin:$PATH + +# Skip installing gem documentation +RUN mkdir -p /opt/jruby/etc \ + && echo -e 'install: --no-document\nupdate: --no-document' >> /opt/jruby/etc/gemrc + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 + +# Start IRB as a default +CMD [ "irb" ] diff --git a/src/engines/jruby/9.2/Dockerfile.musl.clang b/src/engines/jruby/9.2/Dockerfile.musl.clang new file mode 100644 index 0000000..1e5b383 --- /dev/null +++ b/src/engines/jruby/9.2/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.2-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/jruby/9.2/Dockerfile.musl.gcc b/src/engines/jruby/9.2/Dockerfile.musl.gcc new file mode 100644 index 0000000..7072c2d --- /dev/null +++ b/src/engines/jruby/9.2/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.2-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/jruby/9.3/Dockerfile b/src/engines/jruby/9.3/Dockerfile.gnu similarity index 98% rename from src/engines/jruby/9.3/Dockerfile rename to src/engines/jruby/9.3/Dockerfile.gnu index dafe37c..6e6a6ad 100644 --- a/src/engines/jruby/9.3/Dockerfile +++ b/src/engines/jruby/9.3/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM eclipse-temurin:11-jammy AS jruby-9.3.9.0-jre11 # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/jruby/9.3/Dockerfile.musl b/src/engines/jruby/9.3/Dockerfile.musl new file mode 100644 index 0000000..191658d --- /dev/null +++ b/src/engines/jruby/9.3/Dockerfile.musl @@ -0,0 +1,58 @@ +FROM eclipse-temurin:22-jdk-alpine AS jruby-9.3.9.0-jre22 + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +# Add a few packages for consistency +RUN apk add curl bash + +# Install JRuby, pinned for reproducibility +ENV JRUBY_VERSION 9.3.9.0 +ENV JRUBY_SHA256 251e6dd8d1d2f82922c8c778d7857e1bef82fe5ca2cf77bc09356421d0b05ab8 +RUN mkdir /opt/jruby \ + && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ + && echo "$JRUBY_SHA256 /tmp/jruby.tar.gz" | sha256sum -c - \ + && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ + && rm /tmp/jruby.tar.gz \ + && ln -sf /opt/jruby/bin/jruby /usr/local/bin/ruby +ENV PATH /opt/jruby/bin:$PATH + +# Skip installing gem documentation +RUN mkdir -p /opt/jruby/etc \ + && echo -e 'install: --no-document\nupdate: --no-document' >> /opt/jruby/etc/gemrc + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 + +# Start IRB as a default +CMD [ "irb" ] diff --git a/src/engines/jruby/9.3/Dockerfile.musl.clang b/src/engines/jruby/9.3/Dockerfile.musl.clang new file mode 100644 index 0000000..586cbcd --- /dev/null +++ b/src/engines/jruby/9.3/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.3-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/jruby/9.3/Dockerfile.musl.gcc b/src/engines/jruby/9.3/Dockerfile.musl.gcc new file mode 100644 index 0000000..6a609df --- /dev/null +++ b/src/engines/jruby/9.3/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.3-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/jruby/9.4/Dockerfile b/src/engines/jruby/9.4/Dockerfile.gnu similarity index 98% rename from src/engines/jruby/9.4/Dockerfile rename to src/engines/jruby/9.4/Dockerfile.gnu index 31210db..ca87f82 100644 --- a/src/engines/jruby/9.4/Dockerfile +++ b/src/engines/jruby/9.4/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM eclipse-temurin:11-jammy AS jruby-9.4.7.0-jre11 # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/jruby/9.4/Dockerfile.musl b/src/engines/jruby/9.4/Dockerfile.musl new file mode 100644 index 0000000..bc5eca5 --- /dev/null +++ b/src/engines/jruby/9.4/Dockerfile.musl @@ -0,0 +1,58 @@ +FROM eclipse-temurin:22-jdk-alpine AS jruby-9.4.7.0-jre22 + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +# Add a few packages for consistency +RUN apk add curl bash + +# Install JRuby, pinned for reproducibility +ENV JRUBY_VERSION 9.4.7.0 +ENV JRUBY_SHA256 f1c39f8257505300a528ff83fe4721fbe61a855abb25e3d27d52d43ac97a4d80 +RUN mkdir /opt/jruby \ + && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ + && echo "$JRUBY_SHA256 /tmp/jruby.tar.gz" | sha256sum -c - \ + && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ + && rm /tmp/jruby.tar.gz \ + && ln -sf /opt/jruby/bin/jruby /usr/local/bin/ruby +ENV PATH /opt/jruby/bin:$PATH + +# Skip installing gem documentation +RUN mkdir -p /opt/jruby/etc \ + && echo -e 'install: --no-document\nupdate: --no-document' >> /opt/jruby/etc/gemrc + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 + +# Start IRB as a default +CMD [ "irb" ] diff --git a/src/engines/jruby/9.4/Dockerfile.musl.clang b/src/engines/jruby/9.4/Dockerfile.musl.clang new file mode 100644 index 0000000..4e928fb --- /dev/null +++ b/src/engines/jruby/9.4/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.4-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/jruby/9.4/Dockerfile.musl.gcc b/src/engines/jruby/9.4/Dockerfile.musl.gcc new file mode 100644 index 0000000..7fe1bfa --- /dev/null +++ b/src/engines/jruby/9.4/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/jruby:9.4-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.1/Dockerfile b/src/engines/ruby/2.1/Dockerfile.gnu similarity index 99% rename from src/engines/ruby/2.1/Dockerfile rename to src/engines/ruby/2.1/Dockerfile.gnu index 3efe869..fd941ac 100644 --- a/src/engines/ruby/2.1/Dockerfile +++ b/src/engines/ruby/2.1/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + # ruby:2.1.10 was based on jessie, but never got a aarch64 image # aarch64 used to be supported upon release but is not part of LTS, so archive.debian.org does not contain aarch64: https://www.debian.org/releases/jessie/ diff --git a/src/engines/ruby/2.1/Dockerfile.musl b/src/engines/ruby/2.1/Dockerfile.musl new file mode 100644 index 0000000..b658f96 --- /dev/null +++ b/src/engines/ruby/2.1/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.1-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 2.7.11 +RUN gem install bundler --version 1.17.3 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:12.3.3 diff --git a/src/engines/ruby/2.1/Dockerfile.musl.clang b/src/engines/ruby/2.1/Dockerfile.musl.clang new file mode 100644 index 0000000..cc33491 --- /dev/null +++ b/src/engines/ruby/2.1/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.1-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.1/Dockerfile.musl.gcc b/src/engines/ruby/2.1/Dockerfile.musl.gcc new file mode 100644 index 0000000..ecda630 --- /dev/null +++ b/src/engines/ruby/2.1/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.1-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.2/Dockerfile b/src/engines/ruby/2.2/Dockerfile.gnu similarity index 99% rename from src/engines/ruby/2.2/Dockerfile rename to src/engines/ruby/2.2/Dockerfile.gnu index a4d9711..96b10e0 100644 --- a/src/engines/ruby/2.2/Dockerfile +++ b/src/engines/ruby/2.2/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + # ruby:2.2.10-jessie was based on jessie, and had a aarch64 image # aarch64 used to be supported upon release but is not part of LTS, # so archive.debian.org does not contain aarch64: https://www.debian.org/releases/jessie/ diff --git a/src/engines/ruby/2.2/Dockerfile.musl b/src/engines/ruby/2.2/Dockerfile.musl new file mode 100644 index 0000000..be263cd --- /dev/null +++ b/src/engines/ruby/2.2/Dockerfile.musl @@ -0,0 +1,38 @@ +# platforms: linux/x86_64 +FROM ruby:2.2-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 2.7.11 +RUN gem install bundler --version 1.17.3 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.0.6 diff --git a/src/engines/ruby/2.2/Dockerfile.musl.clang b/src/engines/ruby/2.2/Dockerfile.musl.clang new file mode 100644 index 0000000..b06cb61 --- /dev/null +++ b/src/engines/ruby/2.2/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.2-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.2/Dockerfile.musl.gcc b/src/engines/ruby/2.2/Dockerfile.musl.gcc new file mode 100644 index 0000000..a249bef --- /dev/null +++ b/src/engines/ruby/2.2/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.2-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.3/Dockerfile b/src/engines/ruby/2.3/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/2.3/Dockerfile rename to src/engines/ruby/2.3/Dockerfile.gnu index 5333e1e..86514e0 100644 --- a/src/engines/ruby/2.3/Dockerfile +++ b/src/engines/ruby/2.3/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + # Last version: https://github.com/docker-library/ruby/blob/31f66490fdb837ddcc5896e3275f2188f2b7b6dd/2.3/stretch/Dockerfile FROM ruby:2.3.8-stretch diff --git a/src/engines/ruby/2.3/Dockerfile.musl b/src/engines/ruby/2.3/Dockerfile.musl new file mode 100644 index 0000000..0aadd04 --- /dev/null +++ b/src/engines/ruby/2.3/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.3-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/2.3/Dockerfile.musl.clang b/src/engines/ruby/2.3/Dockerfile.musl.clang new file mode 100644 index 0000000..feef3ac --- /dev/null +++ b/src/engines/ruby/2.3/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.3-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.3/Dockerfile.musl.gcc b/src/engines/ruby/2.3/Dockerfile.musl.gcc new file mode 100644 index 0000000..f02ff9f --- /dev/null +++ b/src/engines/ruby/2.3/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.3-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.4/Dockerfile b/src/engines/ruby/2.4/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/2.4/Dockerfile rename to src/engines/ruby/2.4/Dockerfile.gnu index 3aa0e8f..d3d7625 100644 --- a/src/engines/ruby/2.4/Dockerfile +++ b/src/engines/ruby/2.4/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:2.4.10-buster # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/2.4/Dockerfile.musl b/src/engines/ruby/2.4/Dockerfile.musl new file mode 100644 index 0000000..ee2da59 --- /dev/null +++ b/src/engines/ruby/2.4/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.4-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/2.4/Dockerfile.musl.clang b/src/engines/ruby/2.4/Dockerfile.musl.clang new file mode 100644 index 0000000..352dcc9 --- /dev/null +++ b/src/engines/ruby/2.4/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.4-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.4/Dockerfile.musl.gcc b/src/engines/ruby/2.4/Dockerfile.musl.gcc new file mode 100644 index 0000000..f7a8cf6 --- /dev/null +++ b/src/engines/ruby/2.4/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.4-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.5/Dockerfile b/src/engines/ruby/2.5/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/2.5/Dockerfile rename to src/engines/ruby/2.5/Dockerfile.gnu index 7eb4e5d..c2ca8fa 100644 --- a/src/engines/ruby/2.5/Dockerfile +++ b/src/engines/ruby/2.5/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:2.5.9-buster # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/2.5/Dockerfile.musl b/src/engines/ruby/2.5/Dockerfile.musl new file mode 100644 index 0000000..8edb08f --- /dev/null +++ b/src/engines/ruby/2.5/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.5-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/2.5/Dockerfile.musl.clang b/src/engines/ruby/2.5/Dockerfile.musl.clang new file mode 100644 index 0000000..40499d9 --- /dev/null +++ b/src/engines/ruby/2.5/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.5-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.5/Dockerfile.musl.gcc b/src/engines/ruby/2.5/Dockerfile.musl.gcc new file mode 100644 index 0000000..40c7d56 --- /dev/null +++ b/src/engines/ruby/2.5/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.5-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.6/Dockerfile b/src/engines/ruby/2.6/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/2.6/Dockerfile rename to src/engines/ruby/2.6/Dockerfile.gnu index 7c8dcae..473f5e6 100644 --- a/src/engines/ruby/2.6/Dockerfile +++ b/src/engines/ruby/2.6/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:2.6.10-bullseye # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/2.6/Dockerfile.musl b/src/engines/ruby/2.6/Dockerfile.musl new file mode 100644 index 0000000..f5856bd --- /dev/null +++ b/src/engines/ruby/2.6/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.6-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/2.6/Dockerfile.musl.clang b/src/engines/ruby/2.6/Dockerfile.musl.clang new file mode 100644 index 0000000..fa2a143 --- /dev/null +++ b/src/engines/ruby/2.6/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.6-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.6/Dockerfile.musl.gcc b/src/engines/ruby/2.6/Dockerfile.musl.gcc new file mode 100644 index 0000000..f1296ab --- /dev/null +++ b/src/engines/ruby/2.6/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.6-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/2.7/Dockerfile b/src/engines/ruby/2.7/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/2.7/Dockerfile rename to src/engines/ruby/2.7/Dockerfile.gnu index 90d3f25..4c53a73 100644 --- a/src/engines/ruby/2.7/Dockerfile +++ b/src/engines/ruby/2.7/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:2.7.8-bullseye # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/2.7/Dockerfile.musl b/src/engines/ruby/2.7/Dockerfile.musl new file mode 100644 index 0000000..7521b59 --- /dev/null +++ b/src/engines/ruby/2.7/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:2.7-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.3.26 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/2.7/Dockerfile.musl.clang b/src/engines/ruby/2.7/Dockerfile.musl.clang new file mode 100644 index 0000000..4f78e4f --- /dev/null +++ b/src/engines/ruby/2.7/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.7-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/2.7/Dockerfile.musl.gcc b/src/engines/ruby/2.7/Dockerfile.musl.gcc new file mode 100644 index 0000000..1aa6b48 --- /dev/null +++ b/src/engines/ruby/2.7/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:2.7-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/3.0/Dockerfile b/src/engines/ruby/3.0/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/3.0/Dockerfile rename to src/engines/ruby/3.0/Dockerfile.gnu index 81c693d..b809f51 100644 --- a/src/engines/ruby/3.0/Dockerfile +++ b/src/engines/ruby/3.0/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:3.0.7-bullseye # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/3.0/Dockerfile.musl b/src/engines/ruby/3.0/Dockerfile.musl new file mode 100644 index 0000000..3a87a09 --- /dev/null +++ b/src/engines/ruby/3.0/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:3.0-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/3.0/Dockerfile.musl.clang b/src/engines/ruby/3.0/Dockerfile.musl.clang new file mode 100644 index 0000000..00d5be5 --- /dev/null +++ b/src/engines/ruby/3.0/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.0-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/3.0/Dockerfile.musl.gcc b/src/engines/ruby/3.0/Dockerfile.musl.gcc new file mode 100644 index 0000000..0ecca67 --- /dev/null +++ b/src/engines/ruby/3.0/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.0-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/3.1/Dockerfile b/src/engines/ruby/3.1/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/3.1/Dockerfile rename to src/engines/ruby/3.1/Dockerfile.gnu index 9cb9fea..8987611 100644 --- a/src/engines/ruby/3.1/Dockerfile +++ b/src/engines/ruby/3.1/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:3.1.6-bookworm # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/3.1/Dockerfile.musl b/src/engines/ruby/3.1/Dockerfile.musl new file mode 100644 index 0000000..5f248f1 --- /dev/null +++ b/src/engines/ruby/3.1/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:3.1-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/3.1/Dockerfile.musl.clang b/src/engines/ruby/3.1/Dockerfile.musl.clang new file mode 100644 index 0000000..87544ff --- /dev/null +++ b/src/engines/ruby/3.1/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.1-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/3.1/Dockerfile.musl.gcc b/src/engines/ruby/3.1/Dockerfile.musl.gcc new file mode 100644 index 0000000..a8c7f44 --- /dev/null +++ b/src/engines/ruby/3.1/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.1-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/3.2/Dockerfile b/src/engines/ruby/3.2/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/3.2/Dockerfile rename to src/engines/ruby/3.2/Dockerfile.gnu index cb26d59..367630e 100644 --- a/src/engines/ruby/3.2/Dockerfile +++ b/src/engines/ruby/3.2/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:3.2.4-bookworm # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/3.2/Dockerfile.musl b/src/engines/ruby/3.2/Dockerfile.musl new file mode 100644 index 0000000..f370a7b --- /dev/null +++ b/src/engines/ruby/3.2/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:3.2-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/3.2/Dockerfile.musl.clang b/src/engines/ruby/3.2/Dockerfile.musl.clang new file mode 100644 index 0000000..293c136 --- /dev/null +++ b/src/engines/ruby/3.2/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.2-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/3.2/Dockerfile.musl.gcc b/src/engines/ruby/3.2/Dockerfile.musl.gcc new file mode 100644 index 0000000..bdf2960 --- /dev/null +++ b/src/engines/ruby/3.2/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.2-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/3.3/Dockerfile b/src/engines/ruby/3.3/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/3.3/Dockerfile rename to src/engines/ruby/3.3/Dockerfile.gnu index 67bea33..7c78e22 100644 --- a/src/engines/ruby/3.3/Dockerfile +++ b/src/engines/ruby/3.3/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:3.3.2-bookworm # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/3.3/Dockerfile.musl b/src/engines/ruby/3.3/Dockerfile.musl new file mode 100644 index 0000000..9fe7520 --- /dev/null +++ b/src/engines/ruby/3.3/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:3.3-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/3.3/Dockerfile.musl.clang b/src/engines/ruby/3.3/Dockerfile.musl.clang new file mode 100644 index 0000000..10f6a34 --- /dev/null +++ b/src/engines/ruby/3.3/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.3-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/3.3/Dockerfile.musl.gcc b/src/engines/ruby/3.3/Dockerfile.musl.gcc new file mode 100644 index 0000000..3462b66 --- /dev/null +++ b/src/engines/ruby/3.3/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.3-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/src/engines/ruby/3.4/Dockerfile b/src/engines/ruby/3.4/Dockerfile.gnu similarity index 98% rename from src/engines/ruby/3.4/Dockerfile rename to src/engines/ruby/3.4/Dockerfile.gnu index d65c3e0..67cea9b 100644 --- a/src/engines/ruby/3.4/Dockerfile +++ b/src/engines/ruby/3.4/Dockerfile.gnu @@ -1,3 +1,6 @@ +# strip-tags: gnu +# append-tags: gcc + FROM ruby:3.4.0-preview2 # A few RUN actions in Dockerfiles are subject to uncontrollable outside diff --git a/src/engines/ruby/3.4/Dockerfile.musl b/src/engines/ruby/3.4/Dockerfile.musl new file mode 100644 index 0000000..81c4e08 --- /dev/null +++ b/src/engines/ruby/3.4/Dockerfile.musl @@ -0,0 +1,37 @@ +FROM ruby:3.4-rc-alpine + +# A few RUN actions in Dockerfiles are subject to uncontrollable outside +# variability: an identical command would be the same from `docker build`'s +# point of view but does not indicate the result would be identical at +# different points in time. +# +# This causes two possible issues: +# +# - one wants to capture a new state and so wants the identical +# non-reproducible command to produce a new result. This could be achieved +# with --no-cache but this affects every single operation in a Dockerfile +# - one wants to identify a specific state and leverage caching at that +# specific state. +# +# To that end a BUILD_ARG is introduced to capture an arbitrary identifier of +# that state (typically time) that is introduced in non-reproducible commands +# to make them appear different to Docker. +# +# Of course it only works when caching data is available: two independent +# builds with the same value and no cache shared would produce different +# results. +ARG REPRO_RUN_KEY=0 + +# `apk update` is uncontrolled and fetches whatever is today's index. +# For the sake of reproducibility subsequent steps (including in dependent +# images) should not do `apk update`, instead this base image should be +# updated by changing the `REPRO_RUN_KEY`. +RUN true "${REPRO_RUN_KEY}" && apk update + +## Install a pinned RubyGems and Bundler +RUN gem update --system 3.5.21 +RUN gem install bundler:2.3.26 + +# Install additional gems that are in CRuby but missing from the above +# JRuby install distribution. These are version-pinned for reproducibility. +RUN gem install rake:13.2.1 diff --git a/src/engines/ruby/3.4/Dockerfile.musl.clang b/src/engines/ruby/3.4/Dockerfile.musl.clang new file mode 100644 index 0000000..0bd515a --- /dev/null +++ b/src/engines/ruby/3.4/Dockerfile.musl.clang @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.4-musl + +RUN apk add binutils file fortify-headers make musl-dev clang diff --git a/src/engines/ruby/3.4/Dockerfile.musl.gcc b/src/engines/ruby/3.4/Dockerfile.musl.gcc new file mode 100644 index 0000000..a93e457 --- /dev/null +++ b/src/engines/ruby/3.4/Dockerfile.musl.gcc @@ -0,0 +1,3 @@ +FROM ghcr.io/datadog/images-rb/engines/ruby:3.4-musl + +RUN apk add binutils file fortify-headers make musl-dev gcc g++ diff --git a/tasks/docker.rake b/tasks/docker.rake new file mode 100644 index 0000000..f9fcd8a --- /dev/null +++ b/tasks/docker.rake @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +# @type self: Rake::DSL + +# TODO: use rake dependency + satisfaction mechanism via `needs?` +# See: https://github.com/ruby/rake/blob/03cb03474b4eb008b2d62ad96d07de0d6239c7ab/lib/rake/file_task.rb#L16 + +namespace :docker do + def repository # TODO: rename to registry/registry host/user/path + "ghcr.io/datadog/images-rb" + end + + def targets + @targets ||= Dir.glob("src/**/Dockerfile*").map do |f| + dockerfile = f + context = File.dirname(dockerfile) + + image = "#{repository}/#{File.dirname(f.sub(/^src\//, "").sub(/\/Dockerfile(.*)/, ""))}" + tag = f.sub(/.*\/(\d+(?:\.\d+))+\//, "\\1").sub(/Dockerfile(.*)$/) { |m| m.sub("Dockerfile", "").tr(".", "-") } + + targets = [ + { + dockerfile: dockerfile, + context: context, + image: image, # TODO: rename to repository + tag: tag + # TODO: rename to image/tag/tagged_image/name/alias: "#{repo}:#{tag}" + } + ] + + strip_tags = File.read(dockerfile).lines.select { |l| l =~ /^\s*#\s*strip-tags:/ }.map { |l| l =~ /strip-tags: (.*)/ && $1 } + if strip_tags.any? + stripped_tag = strip_tags.each_with_object(tag.dup) { |t, r| r.gsub!(/-#{t}/, "") } + targets << { + dockerfile: dockerfile, + context: context, + image: image, + tag: stripped_tag + } + end + + append_tags = File.read(dockerfile).lines.select { |l| l =~ /^\s*#\s*append-tags:/ }.map { |l| l =~ /append-tags: (.*)/ && $1 } + if append_tags.any? + append_tags.each do |t| + targets << { + dockerfile: dockerfile, + context: context, + image: image, + tag: "#{tag}-#{t}" + } + end + end + + targets + end.flatten + end + + def dependencies + @dependencies ||= Dir.glob("src/**/Dockerfile*").each_with_object({}) do |path, h| + h[path] = File.read(path).each_line.with_object([]) { |l, a| l =~ /^FROM (\S+)(?:\s+AS|\s*$)/ && a << $1 } + end + end + + def local_dependencies + @local_dependencies ||= dependencies.each_with_object({}) { |(k, v), h| h[k] = v.select { |from| from.start_with?(repository) } } + end + + def target_for(args) + targets_for(args).tap { |a| a.size > 1 and fail "multiple args passed to task" }.first + end + + def glob_match?(pattern, str) + re = Regexp.new("^#{Regexp.escape(pattern).gsub("\\*\\*", "[^:]*?").gsub("\\*", "[^/:]*?")}$") + + !!(str =~ re) + end + + def targets_for(args) + images = args.to_a + + images.map do |image| + image = "#{repository}/#{image}" unless image.start_with?(repository) + + found = targets.select { |e| glob_match?(image, "#{e[:image]}:#{e[:tag]}") } + + fail "#{image} not found" if found.nil? + + found + end.flatten + end + + def dockerfiles_for(*images) + images.map do |image| + targets.each_with_object([]) { |t, a| a << t[:dockerfile] if "#{t[:image]}:#{t[:tag]}" == image } + end.flatten + end + + def satisfied?(result, deps = []) + result_time = case result + when String + File.ctime(result).to_datetime + when Proc + result.call + else + raise ArgumentError, "invalid type: #{dep.class}" + end + + return false if result_time.nil? + return true if deps.empty? + + deps.map do |dep| + dep_time = case dep + when String + File.ctime(dep).to_datetime + when Proc + dep.call + else + raise ArgumentError, "invalid type: #{dep.class}" + end + + result_time > dep_time + end.reduce(:&) + end + + def docker_platform + if RUBY_PLATFORM =~ /^(?:universal\.|)(x86_64|aarch64|arm64)/ + cpu = $1.sub(/arm64(:?e|)/, "aarch64") + else + raise ArgumentError, "unsupported platform: #{RUBY_PLATFORM}" + end + + os = "linux" + + "#{os}/#{cpu}" + end + + def image_time(image) + require "time" + + last_tag_time = `docker image inspect -f '{{ .Metadata.LastTagTime }}' '#{image}'`.chomp + + if $?.to_i == 0 + # "0001-01-01 00:00:00 +0000 UTC" + last_tag_time.sub!(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})(\s+)/, "\\1.0\\2") + + DateTime.strptime(last_tag_time, "%Y-%m-%d %H:%M:%S.%N %z") + end + end + + def volume_time(volume) + require "time" + + volume_creation_time = `docker volume inspect -f '{{ .CreatedAt }}' '#{volume}'`.chomp + + if $?.to_i == 0 + volume_creation_time.sub!(/^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})(\s+)/, "\\1.0\\2") + + DateTime.strptime(volume_creation_time, "%Y-%m-%dT%H:%M:%S.%N %z") + end + end + + desc "List image targets." + task :list do + targets.each do |image| + puts "#{image[:image]}:#{image[:tag]}" + end + end + + desc "Pull image." + task :pull do |_, args| + targets = targets_for(args) + + targets.each do |target| + image = target[:image] + tag = target[:tag] + platform = docker_platform + + sh "docker pull --platform #{platform} #{image}:#{tag} || true" + end + end + + desc "Build image." + task :build do |_, args| + targets = targets_for(args) + + targets.each do |target| + dockerfile = target[:dockerfile] + context = target[:context] + image = target[:image] + tag = target[:tag] + platform = docker_platform + + deps = [ + dockerfile + ] + dockerfiles_for(*local_dependencies[dockerfile]) + + compatible_platforms = deps.map do |dep| + File.read(dep).lines.select { |l| l =~ /^\s*#\s*platforms:/ }.map { |l| l =~ /platforms: (.*)/ && $1 } + end.flatten + + if compatible_platforms.any? && !compatible_platforms.include?(platform) + warn "skip build: dockerfile: #{dockerfile.inspect}, incompatible platform: #{platform.inspect}, compatible platforms: #{compatible_platforms.inspect}" + next + end + + local_dependencies[dockerfile].each { |dep| Rake::Task[:"docker:build"].execute(Rake::TaskArguments.new([], [dep])) } + + next if satisfied?(-> { image_time("#{image}:#{tag}") }, deps) + + sh "docker buildx build --platform #{platform} --cache-from=type=registry,ref=#{image}:#{tag} -f #{dockerfile} -t #{image}:#{tag} #{context}" + end + end + + desc "Run container with default CMD." + task cmd: :build do |_, args| + target = target_for(args) + + image = target[:image] + tag = target[:tag] + platform = docker_platform + + exec "docker run --rm -it --platform #{platform} -v #{Dir.pwd}:#{Dir.pwd} -w #{Dir.pwd} #{image}:#{tag}" + end + + desc "Run container with shell." + task shell: :build do |_, args| + target = target_for(args) + + image = target[:image] + tag = target[:tag] + platform = docker_platform + + exec "docker run --rm -it --platform #{platform} -v #{Dir.pwd}:#{Dir.pwd} -w #{Dir.pwd} #{image}:#{tag} /bin/sh" + end + + desc "Run container with irb." + task irb: :build do |_, args| + target = target_for(args) + + image = target[:image] + tag = target[:tag] + platform = docker_platform + + exec "docker run --rm -it --platform #{platform} -v #{Dir.pwd}:#{Dir.pwd} -w #{Dir.pwd} #{image}:#{tag} irb" + end +end