build: add development flow #25
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/v8 | |
| jobs: | |
| docker: | |
| services: | |
| registry: | |
| image: registry:3 | |
| ports: | |
| - 5000:5000 | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| repository: | |
| - 'localhost:5000' | |
| 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: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - | |
| name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| driver-opts: network=host | |
| - | |
| id: image_env | |
| run: | | |
| . ./env.sh \ | |
| '${{ matrix.alpine }}' \ | |
| '${{ matrix.python }}' \ | |
| '${{ github.repository_owner }}' \ | |
| '${{ matrix.repository }}' | |
| docker pull "${SOURCE_IMAGE}" || true | |
| echo "IMAGE_HOME=$(mktemp -d)" >> "$GITHUB_OUTPUT" | |
| echo ALPINE_VERSION="${ALPINE_VERSION}" >> "$GITHUB_OUTPUT" | |
| echo PYTHON_VERSION="${PYTHON_VERSION}" >> "$GITHUB_OUTPUT" | |
| echo SOURCE_IMAGE="${SOURCE_IMAGE}" >> "$GITHUB_OUTPUT" | |
| echo IMAGE_TAG="${IMAGE_TAG}" >> "$GITHUB_OUTPUT" | |
| echo IMAGE_BUILDROOT_TAG="${IMAGE_TAG}-buildroot" >> "$GITHUB_OUTPUT" | |
| echo IMAGE_TAG_SAFE="$(echo "$IMAGE_TAG" | base64 -w 0 )" >> "$GITHUB_OUTPUT" | |
| echo IMAGE_BUILDROOT_TAG_SAFE="$(echo "${IMAGE_TAG}-buildroot" | base64 -w 0 )" >> "$GITHUB_OUTPUT" | |
| echo REPOSITORY="${REPOSITORY}" >> "$GITHUB_OUTPUT" | |
| echo REPOSITORY_SAFE="$(echo "${REPOSITORY}" | base64 -w 0 )" >> "$GITHUB_OUTPUT" | |
| echo BASE_IMAGE_DIGEST="$(digest_of "$SOURCE_IMAGE")" >> "$GITHUB_OUTPUT" | |
| echo 'IMAGE_DESCRIPTION=${{ github.event.repository.description }}. See ${{ github.server_url }}/${{ github.repository }} for more info.' >> "$GITHUB_OUTPUT" | |
| - | |
| name: Create Buildroot | |
| uses: docker/build-push-action@v6 | |
| id: buildroot | |
| with: | |
| push: true | |
| platforms: ${{ env.TARGET_PLATFORMS }} | |
| context: "." | |
| file: Dockerfile.alpine | |
| 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 | |
| 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 | |
| annotations: | | |
| index,manifest:org.opencontainers.image.authors=distroless-python image developers <[email protected]> | |
| 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 | |
| uses: docker/build-push-action@v6 | |
| with: | |
| 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-amd64" | |
| outputs: type=oci,dest=${{ steps.image_env.outputs.IMAGE_HOME }}/example1-${{ steps.image_env.outputs.IMAGE_TAG_SAFE }}.tar | |
| - | |
| 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: Fetch packages version ID | |
| if: ${{ matrix.repository == 'ghcr.io' }} | |
| id: fetch_version_ids | |
| run: | | |
| tag=$(echo ${{ steps.image_env.outputs.IMAGE_TAG }} | rev | cut -d: -f1 | rev) | |
| package_name=$(echo ${{ steps.image_env.outputs.IMAGE_TAG }} | rev | cut -d: -f2- | cut -d/ -f1 | rev) | |
| repo_name=${{ github.repository }} | |
| owner=${{ github.repository_owner }} | |
| version_id=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://api.github.com/orgs/${owner}/packages/container/${package_name}/versions" | \ | |
| jq -r --arg tag "$tag" '.[] | select(.metadata.container.tags[] == $tag) | .id') | |
| package_url="https://github.com/${repo_name}/pkgs/container/${package_name}/${version_id}?tag=${tag}" | |
| echo "${{steps.image_env.outputs.IMAGE_TAG}}@$package_url" | tee -a ${{ steps.image_env.outputs.IMAGE_HOME }}/packages.txt | |
| - name: upload build | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| if-no-files-found: error | |
| retention-days: 1 | |
| name: images-${{steps.image_env.outputs.REPOSITORY_SAFE}}-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-${{steps.image_env.outputs.REPOSITORY_SAFE}}-alpine${{steps.image_env.outputs.ALPINE_VERSION}}-python${{steps.image_env.outputs.PYTHON_VERSION}} | |
| path: | | |
| ${{ steps.image_env.outputs.IMAGE_HOME }}/*.txt | |
| render-dockerhub-desc: | |
| needs: [docker] | |
| 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)" | |
| 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 \ | |
| 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" |