Alpine 3.23, Test vector, Clean up output, reduce messages #166
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: development | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - '!main' # excludes main | |
| - '!master' # excludes master | |
| env: | |
| TARGET_PLATFORMS: linux/amd64,linux/arm64 | |
| COMPOSE_DOCKER_CLI_BUILD: '1' | |
| DOCKER_BUILDKIT: '1' | |
| jobs: | |
| build_images: | |
| name: "Create runtime and buildroot OCI Images" | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| python: | |
| - '3.14' | |
| - '3.13' | |
| - '3.12' | |
| - '3.11' | |
| - '3.10' | |
| - '3.9' | |
| - '3.8' | |
| alpine: | |
| - '3.20' | |
| - '3.21' | |
| - '3.22' | |
| - '3.23' | |
| os: | |
| - 'ubuntu-latest' | |
| exclude: | |
| # No tag | |
| - python: '3.8' | |
| alpine: '3.21' | |
| - python: '3.8' | |
| alpine: '3.22' | |
| - python: '3.8' | |
| alpine: '3.23' | |
| - python: '3.9' | |
| alpine: '3.23' | |
| - python: '3.14' | |
| alpine: '3.20' | |
| - python: '3.15' | |
| alpine: '3.20' | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - | |
| name: Checkout | |
| uses: actions/checkout@v4 | |
| - | |
| id: setup | |
| name: Setup | |
| run: | | |
| DEBUG_OUTPUT=/dev/null | |
| if [ x"${DEBUG_DOCKERD:-}" = x1 ]; then | |
| DEBUG_OUTPUT="$GITHUB_STEP_SUMMARY" | |
| fi | |
| setup_registry () { | |
| >/dev/null docker pull -q docker.io/library/registry:3 | |
| registry_id=$(docker run \ | |
| -p5000:5000 \ | |
| --name registry \ | |
| --rm -d registry:3) | |
| echo 'REGISTRY_CONTAINER_ID='$registry_id | |
| } | |
| setup_containerd_snapshotter () { | |
| local _sudo=sudo | |
| if [ $(id -u) -eq 0 ]; then | |
| _sudo=sudo | |
| fi | |
| if ! [ -f /etc/docker/daemon.json ]; then | |
| eval $_sudo mkdir -p /etc/docker | |
| echo '{}' | >/dev/null $_sudo tee /etc/docker/daemon.json | |
| fi | |
| cat /etc/docker/daemon.json | jq '. | .+{"features": {"containerd-snapshotter": true}}' | >/dev/null eval $_sudo tee /etc/docker/daemon.json | |
| printf '# Docker config\n\n```\n' | |
| cat /etc/docker/daemon.json | |
| printf '\n```\n\n' | |
| eval $_sudo systemctl restart docker | |
| sleep 1 | |
| printf '# Docker Info\n\n```' | |
| docker info | |
| printf '\n```\n' | |
| } | |
| sudo apt-get -y install skopeo | |
| setup_containerd_snapshotter | tee -a "$DEBUG_OUTPUT" | |
| { | |
| printf '## Registry info:\n\n' | |
| setup_registry | tee -a "$GITHUB_OUTPUT" | |
| } | tee -a "$DEBUG_OUTPUT" | |
| - | |
| name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - | |
| name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| driver-opts: network=host | |
| buildkitd-flags: '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host' | |
| - | |
| id: image_env | |
| run: | | |
| . ./env.sh \ | |
| '${{ matrix.alpine }}' \ | |
| '${{ matrix.python }}' \ | |
| '${{ github.repository_owner }}' \ | |
| 'localhost:5000' | |
| docker pull -q "${SOURCE_IMAGE}" || true | |
| echo "IMAGE_HOME=$(mktemp -d)" >> "$GITHUB_OUTPUT" | |
| echo ALPINE_VERSION="${ALPINE_VERSION}"| tee -a "$GITHUB_OUTPUT" | |
| echo PYTHON_VERSION="${PYTHON_VERSION}"| tee -a "$GITHUB_OUTPUT" | |
| echo SOURCE_IMAGE="${SOURCE_IMAGE}"| tee -a "$GITHUB_OUTPUT" | |
| echo IMAGE_TAG="${IMAGE_TAG}"| tee -a "$GITHUB_OUTPUT" | |
| echo IMAGE_BUILDROOT_TAG="${IMAGE_TAG}-buildroot"| tee -a "$GITHUB_OUTPUT" | |
| echo IMAGE_TAG_SAFE="$(echo "$IMAGE_TAG" | base64 -w 0 )"| tee -a "$GITHUB_OUTPUT" | |
| echo IMAGE_BUILDROOT_TAG_SAFE="$(echo "${IMAGE_TAG}-buildroot" | base64 -w 0 )"| tee -a "$GITHUB_OUTPUT" | |
| echo 'EXAMPLE_IMAGE_TAG='"${IMAGE_TAG}-example1" | tee -a "$GITHUB_OUTPUT" | |
| echo EXAMPLE_IMAGE_TAG_SAFE="$(echo "${EXAMPLE_IMAGE_TAG}" | base64 -w 0 )"| tee -a "$GITHUB_OUTPUT" | |
| echo REPOSITORY="${REPOSITORY}"| tee -a "$GITHUB_OUTPUT" | |
| echo REPOSITORY_SAFE="$(echo "${REPOSITORY}" | base64 -w 0 )"| tee -a "$GITHUB_OUTPUT" | |
| echo BASE_IMAGE_DIGEST="$(digest_of "$SOURCE_IMAGE")"| tee -a "$GITHUB_OUTPUT" | |
| echo 'IMAGE_DESCRIPTION=${{ github.event.repository.description }}. See ${{ github.server_url }}/${{ github.repository }} for more info.' | tee -a "$GITHUB_OUTPUT" | |
| - | |
| name: Create Buildroot | |
| uses: docker/build-push-action@v6 | |
| id: buildroot | |
| env: | |
| DOCKER_BUILD_SUMMARY: false | |
| DOCKER_BUILD_RECORD_UPLOAD: false | |
| with: | |
| allow: network.host,security.insecure # this is for the bind mount | |
| push: true | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| context: "." | |
| file: buildroot/Dockerfile.alpine | |
| sbom: true | |
| provenance: mode=max | |
| target: buildroot | |
| cache-to: | | |
| type=gha,mode=max | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot | |
| type=registry,ref=${{ steps.image_env.outputs.SOURCE_IMAGE }}@${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| build-args: | | |
| ALPINE_VERSION=${{ steps.image_env.outputs.ALPINE_VERSION }} | |
| BASE_IMAGE_DIGEST=${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| PYTHON_VERSION=${{ steps.image_env.outputs.PYTHON_VERSION }} | |
| SOURCE_IMAGE=${{ steps.image_env.outputs.SOURCE_IMAGE }} | |
| BUILD_ROOT=/d | |
| tags: "${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot" | |
| - | |
| name: Create Image | |
| id: runtime | |
| uses: docker/build-push-action@v6 | |
| env: | |
| SOURCE_DATE_EPOCH: 0 | |
| DOCKER_BUILD_SUMMARY: false | |
| DOCKER_BUILD_RECORD_UPLOAD: false | |
| with: | |
| push: true | |
| context: "." | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| file: runtime/Dockerfile.alpine | |
| cache-to: | | |
| type=gha,mode=max | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=${{ steps.image_env.outputs.IMAGE_TAG }} | |
| type=registry,ref=${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot | |
| type=registry,ref=${{ steps.image_env.outputs.SOURCE_IMAGE }}@${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| build-args: | | |
| ALPINE_VERSION=${{ steps.image_env.outputs.ALPINE_VERSION }} | |
| BASE_IMAGE_DIGEST=${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| PYTHON_VERSION=${{ steps.image_env.outputs.PYTHON_VERSION }} | |
| SOURCE_IMAGE=${{ steps.image_env.outputs.SOURCE_IMAGE }} | |
| BUILDROOT_IMAGE=${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot@${{ steps.buildroot.outputs.digest }} | |
| BUILD_ROOT=/d | |
| tags: "${{ steps.image_env.outputs.IMAGE_TAG }}" | |
| labels: ${{steps.image_env.outputs.IMAGE_LABELS}} | |
| sbom: true | |
| provenance: mode=max | |
| annotations: | | |
| index,manifest:org.opencontainers.image.authors=distroless-python image developers <autumn.jolitz+distroless-python@gmail.com> | |
| index,manifest:org.opencontainers.image.source=https://github.com/autumnjolitz/distroless-python | |
| index,manifest:org.opencontainers.image.title=distroless-python${{ steps.image_env.outputs.PYTHON_VERSION }}-alpine${{ steps.image_env.outputs.ALPINE_VERSION }} | |
| index,manifest:org.opencontainers.image.description=${{ steps.image_env.outputs.IMAGE_DESCRIPTION }} | |
| index,manifest:org.opencontainers.image.base.digest=${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| index,manifest:org.opencontainers.image.base.name=${{ steps.image_env.outputs.SOURCE_IMAGE }} | |
| index,manifest:distroless.python-version=${{ steps.image_env.outputs.PYTHON_VERSION }} | |
| index,manifest:distroless.alpine-version=${{ steps.image_env.outputs.ALPINE_VERSION }} | |
| index,manifest:distroless.base-image=alpine${{ steps.image_env.outputs.ALPINE_VERSION }} | |
| - | |
| name: examples/simple-flask | |
| id: build_test | |
| uses: docker/build-push-action@v6 | |
| env: | |
| DOCKER_BUILD_SUMMARY: false | |
| DOCKER_BUILD_RECORD_UPLOAD: false | |
| with: | |
| push: true | |
| context: "examples/simple-flask" | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| cache-from: | | |
| type=gha | |
| type=registry,ref=${{ steps.image_env.outputs.IMAGE_TAG }} | |
| type=registry,ref=${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot | |
| type=registry,ref=${{ steps.image_env.outputs.SOURCE_IMAGE }}@${{ steps.image_env.outputs.BASE_IMAGE_DIGEST }} | |
| build-args: | | |
| SOURCE_IMAGE=${{ steps.image_env.outputs.IMAGE_TAG }} | |
| tags: "${{ steps.image_env.outputs.IMAGE_TAG }}-example1" | |
| - | |
| id: test | |
| name: Test Example | |
| env: | |
| IMAGE_URI: '${{ steps.image_env.outputs.EXAMPLE_IMAGE_TAG }}' | |
| IMAGE_DIGEST: '${{ steps.build_test.outputs.digest }}' | |
| test_command: curl -sLv 'http://localhost:8080/' | |
| run: | | |
| set -o pipefail | |
| run_test () { | |
| local rv=0 | |
| local T="$(mktemp)" | |
| trap 'rm -f '"$T" RETURN | |
| >"$T" 2>&1 eval "$@" || rv=$? | |
| if [ $rv -eq 0 ] && case "$(tail -1 $T)" in '<p>Hello, World!</p>') false ;; *) true ;; esac ; then | |
| >&2 echo 'failed! got "'"$(tail -1 $T)"'" !' | |
| rv=1 | |
| fi | |
| printf '`%s` (returned code: ' "$@" | |
| printf '%s)\n```\n' "$rv" | |
| cat "$T" | |
| printf '\n```\n\n' | |
| return "$rv" | |
| } | |
| rc=0 | |
| REMOTE_DIGEST="$(skopeo inspect --tls-verify=false --raw docker://"$IMAGE_URI" | skopeo manifest-digest /dev/stdin)" | |
| if [ "$REMOTE_DIGEST" != "${IMAGE_DIGEST}" ]; then | |
| echo 'generated a diferent manifest than '"${IMAGE_DIGEST}"': '"$REMOTE_DIGEST" | |
| exit 2 | |
| fi | |
| docker pull -q "$(echo "$IMAGE_URI" | rev | cut -d: -f2- | rev )@${IMAGE_DIGEST}" | |
| for platform in $(echo $TARGET_PLATFORMS | tr , '\n') | |
| do | |
| docker pull --platform $platform -q "$IMAGE_URI" | |
| >&2 printf 'pulled %s (%s)\n' "$IMAGE_TAG" "$platform" | |
| done | |
| { | |
| printf '# Test\n\n' | |
| printf '## Source Images\n\n```' | |
| docker images --tree $IMAGE_URI | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,2};?)?)?[mGK]//g" | |
| printf '\n```\n' | |
| } | tee -a "$GITHUB_STEP_SUMMARY" | |
| for platform in $(echo $TARGET_PLATFORMS | tr , '\n') | |
| do | |
| rc=0 | |
| container_id="$(docker run \ | |
| --platform $platform \ | |
| --detach -p 8080:8080 \ | |
| --rm $IMAGE_URI)" || rc=$? | |
| trap 'docker stop $container_id' EXIT | |
| if [ $rc -ne 0 ]; then | |
| printf 'docker run failed (return code '$rc')!\n' | >&2 tee \ | |
| -a "$GITHUB_OUTPUT" | |
| exit $rc | |
| fi | |
| start_ts=$(date +%s) | |
| until [ x$(curl \ | |
| --silent --output /dev/null \ | |
| -w '%{http_code}\n' --fail 'http://localhost:8080/_health') = x200 ] | |
| do | |
| sleep 1 | |
| if [ "$(expr "${start_ts}" \+ 30)" -lt "$(date +%s)" ]; then | |
| >&2 echo 'failed to get a good response in 30 seconds!' | |
| break | |
| fi | |
| done | |
| rc=0 | |
| printf '## Test (%s)\n\n' "$(curl 'http://localhost:8080/_arch')" | tee -a "$GITHUB_STEP_SUMMARY" | |
| (run_test "$test_command" | tee -a "$GITHUB_STEP_SUMMARY") || rc=$? | |
| if [ $rc -ne 0 ]; then | |
| exit $rc | |
| fi | |
| trap -- EXIT | |
| docker stop $container_id | |
| done | |
| - | |
| id: post_build | |
| name: 'download multiplatform images' | |
| run: | | |
| skopeo copy \ | |
| --src-no-creds \ | |
| --src-tls-verify=false \ | |
| --quiet \ | |
| --multi-arch all \ | |
| docker://${{ steps.image_env.outputs.IMAGE_TAG }} \ | |
| oci-archive://${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_TAG_SAFE }}.tar \ | |
| & | |
| skopeo copy \ | |
| --src-no-creds \ | |
| --src-tls-verify=false \ | |
| --quiet \ | |
| --multi-arch all \ | |
| docker://${{ steps.image_env.outputs.IMAGE_TAG }}-buildroot \ | |
| oci-archive://${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_BUILDROOT_TAG_SAFE }}.tar \ | |
| & | |
| skopeo copy \ | |
| --src-no-creds \ | |
| --src-tls-verify=false \ | |
| --quiet \ | |
| --multi-arch all \ | |
| docker://${{ steps.image_env.outputs.EXAMPLE_IMAGE_TAG }} \ | |
| oci-archive://${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.EXAMPLE_IMAGE_TAG_SAFE }}.tar \ | |
| & | |
| wait | |
| docker stop '${{ steps.setup.outputs.REGISTRY_CONTAINER_ID }}' | |
| IMAGE_DIGEST='${{steps.runtime.outputs.digest}}' | |
| IMAGE_BUILDROOT_DIGEST='${{steps.buildroot.outputs.digest}}' | |
| echo 'IMAGE_DIGEST='$IMAGE_DIGEST >> "GITHUB_OUTPUT" | |
| echo 'IMAGE_BUILDROOT_DIGEST='$IMAGE_BUILDROOT_DIGEST >> "GITHUB_OUTPUT" | |
| echo '${{ steps.image_env.outputs.IMAGE_TAG }}@'"$IMAGE_DIGEST" | tee -a ${{ steps.image_env.outputs.IMAGE_HOME }}/manifest.txt | |
| echo '${{ steps.image_env.outputs.IMAGE_BUILDROOT_TAG }}@'"$IMAGE_BUILDROOT_DIGEST" | tee -a ${{ steps.image_env.outputs.IMAGE_HOME }}/manifest.txt | |
| ls ${{ steps.image_env.outputs.IMAGE_HOME }} | |
| - | |
| name: upload build | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| if-no-files-found: error | |
| retention-days: 1 | |
| name: images-alpine${{steps.image_env.outputs.ALPINE_VERSION}}-python${{steps.image_env.outputs.PYTHON_VERSION}} | |
| path: | | |
| ${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_TAG_SAFE }}.tar | |
| ${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_BUILDROOT_TAG_SAFE }}.tar | |
| - | |
| name: upload build info | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| if-no-files-found: error | |
| retention-days: 1 | |
| name: metadata-alpine${{steps.image_env.outputs.ALPINE_VERSION}}-python${{steps.image_env.outputs.PYTHON_VERSION}} | |
| path: | | |
| ${{ steps.image_env.outputs.IMAGE_HOME }}/*.txt | |
| post-build: | |
| name: "Render metadata" | |
| needs: [build_images] | |
| runs-on: "ubuntu-latest" | |
| steps: | |
| - | |
| name: Checkout | |
| uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| cache: 'pip' | |
| - | |
| name: install dependencies | |
| run: | | |
| python -m pip install jinplate | |
| - | |
| name: fetch metadata | |
| uses: actions/download-artifact@v5 | |
| with: | |
| pattern: metadata-* | |
| path: dist-images | |
| - | |
| name: aggregate metadata | |
| run: | | |
| cat $(find dist-images -type f -name manifest.txt -print) > manifest.txt | |
| cat $(find dist-images -type f -name packages.txt -print) > packages.txt | |
| find dist-images -type f \( -name manifest.txt -o -name packages.txt \) -delete | |
| { | |
| printf '### manifest.txt\n\n' | |
| printf '```\n' | |
| cat manifest.txt | |
| printf '```\n' | |
| } >>"$GITHUB_STEP_SUMMARY" | |
| { | |
| printf '### packages.txt\n\n' | |
| printf '```\n' | |
| cat packages.txt | |
| printf '```\n' | |
| } >>"$GITHUB_STEP_SUMMARY" | |
| - | |
| name: fetch images | |
| uses: actions/download-artifact@v5 | |
| with: | |
| pattern: images-* | |
| path: dist-images | |
| merge-multiple: true | |
| - | |
| name: parse metadata for README | |
| run: | | |
| for filename in $(echo dist-images/image-*.tar) | |
| do | |
| IMAGE_URI="$(basename -s .tar $filename | sed 's/image-//g' | base64 -d)" | |
| mkdir -p "$(dirname "$IMAGE_URI")" | |
| mv $filename "${IMAGE_URI}.tar" | |
| filename="${IMAGE_URI}.tar" | |
| PACKAGE_URL="$(grep "${IMAGE_URI}@" packages.txt | cut -d@ -f2-)" | |
| echo '{ | |
| "filename": "'"$(basename $filename)"'", | |
| "url": "'"$IMAGE_URI"'", | |
| "tag": "'$(echo $IMAGE_URI | rev | cut -d: -f1 | rev)'", | |
| "path": "'$(echo $IMAGE_URI | cut -d/ -f2- | cut -d: -f1)'", | |
| "name": "'$(echo $IMAGE_URI | rev | cut -d: -f2- | cut -d/ -f1 | rev)'", | |
| "registry": "'"$(echo $IMAGE_URI | cut -d/ -f1)"'", | |
| "size": '"$(wc -c "$filename" | xargs | cut -f1 -d' ')"', | |
| "digest": "'$(grep "${IMAGE_URI}@" manifest.txt | cut -d@ -f2-)'", | |
| "package_url": "'$PACKAGE_URL'" | |
| }' | |
| done | >./values.json jq -s '. | {"images": .}' | |
| cat ./values.json | |
| >./README.rst python \ | |
| -m jinplate.cli \ | |
| docs/README.rst.tmpl file://$PWD/values.json | |
| - | |
| name: Convert README.rst to markdown | |
| uses: docker://pandoc/core:3.8 | |
| with: | |
| args: >- | |
| --wrap=none | |
| -t gfm | |
| -o README.md | |
| README.rst | |
| - | |
| name: Print out markdown | |
| run: | | |
| cat README.md >>"$GITHUB_STEP_SUMMARY" | |
| printf '\n\n# README.rst\n\n```\n' >> "$GITHUB_STEP_SUMMARY" | |
| cat README.rst >> "$GITHUB_STEP_SUMMARY" | |
| printf '\n```\n' >> "$GITHUB_STEP_SUMMARY" |