Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
297 changes: 297 additions & 0 deletions .github/workflows/development.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
name: development

on:
pull_request:
push:
branches:
- '!main' # excludes main
- '!master' # excludes master


env:
TARGET_PLATFORMS: linux/amd64,linux/arm64

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
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
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 <[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)"
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 \
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"
Loading
Loading