Alpine 3.23, Test vector, Clean up output, reduce messages #56
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" | |
| services: | |
| registry: | |
| image: registry:3 | |
| ports: | |
| - 5000:5000 | |
| 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' | |
| os: | |
| - 'ubuntu-latest' | |
| exclude: | |
| # No tag | |
| - python: '3.8' | |
| alpine: '3.21' | |
| - python: '3.8' | |
| alpine: '3.22' | |
| - python: '3.14' | |
| alpine: '3.20' | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - | |
| name: Checkout | |
| uses: actions/checkout@v4 | |
| - | |
| name: Setup | |
| run: | | |
| setup_containerd_snapshotter () { | |
| local _sudo=sudo | |
| if [ $(id -u) -eq 0 ]; then | |
| _sudo= | |
| fi | |
| if ! [ -f /etc/docker/daemon.json ]; then | |
| eval $_sudo mkdir -p /etc/docker | |
| echo '{}' | $_sudo tee /etc/docker/daemon.json | |
| fi | |
| cat /etc/docker/daemon.json | jq '. | .+{"features": {"containerd-snapshotter": true}}' | 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 "$GITHUB_STEP_SUMMARY" | |
| - | |
| 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 | |
| - | |
| 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 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: | |
| push: true | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| context: "." | |
| file: 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" | |
| outputs: type=oci,dest=${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_BUILDROOT_TAG_SAFE }}.tar | |
| - | |
| 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: 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 }} | |
| BUILD_ROOT=/d | |
| tags: "${{ steps.image_env.outputs.IMAGE_TAG }}" | |
| outputs: type=oci,dest=${{ steps.image_env.outputs.IMAGE_HOME }}/image-${{ steps.image_env.outputs.IMAGE_TAG_SAFE }}.tar | |
| 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" | |
| outputs: type=oci,dest=${{ steps.image_env.outputs.IMAGE_HOME }}/example1-${{ steps.image_env.outputs.IMAGE_TAG_SAFE }}.tar | |
| - | |
| 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 () { | |
| printf '## Test\n\n' | |
| 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### 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' | |
| return "$rv" | |
| } | |
| rc=0 | |
| REMOTE_DIGEST="$(skopeo inspect --raw "$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 | |
| container_id="$(docker run --detach -p 8080:8080 --rm $IMAGE_URI)" || rc=$? | |
| if [ $rc -ne 0 ]; then | |
| printf 'docker run failed (return code '$rc')!\n' | >&2 tee \ | |
| -a "$GITHUB_OUTPUT" | |
| exit $rc | |
| fi | |
| trap 'docker stop $container_id' EXIT | |
| start_ts=$(date +%s) | |
| until [ x$(curl \ | |
| --silent --output /dev/null \ | |
| -w '%{http_code}\n' --fail 'http://localhost:8080/') = 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 | |
| (run_test "$test_command" | tee -a "$GITHUB_STEP_SUMMARY") || rc=$? | |
| if [ $rc -ne 0 ]; then | |
| exit $rc | |
| fi | |
| - | |
| id: post_build | |
| run: | | |
| 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' | |
| - | |
| 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: >- | |
| -s | |
| --wrap=none | |
| -t gfm | |
| -o README.md | |
| README.rst | |
| - | |
| name: Print out markdown | |
| run: | | |
| cat README.md >>"$GITHUB_STEP_SUMMARY" |