ci: publish distroless variants and rebuild images weekly

Extract the release build/push and manifest jobs into a reusable
build-images.yml workflow, extend it with a distroless variant, and add
a weekly rebuild-images.yml so digest-pinned bases pick up security
updates without a manual re-pin.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Sharon Hart
2026-09-10 00:28:02 +03:00
co-authored by Copilot App
parent a04e702875
commit a3f351261a
3 changed files with 243 additions and 97 deletions
+166
View File
@@ -0,0 +1,166 @@
name: Build and Push Images
# Reusable image build. Called by release.yml when cutting a release and by
# rebuild-images.yml on a schedule, so that published images pick up base image
# security updates without waiting for the next release.
on:
workflow_call:
inputs:
version:
description: Tag applied to the analyzer, anonymizer and CLI-adjacent images.
required: true
type: string
image-version:
description: Tag applied to the image-redactor image.
required: true
type: string
date-tag:
description: >
Optional immutable suffix, e.g. 20260909. When set, each image is also
pushed as <version>-<date-tag> so a specific rebuild can be pinned or
rolled back to.
required: false
default: ''
type: string
permissions: read-all
env:
IMAGE_REGISTRY: ghcr.io
IMAGE_NAMESPACE: data-privacy-stack
jobs:
build-and-push-containers:
name: Build and Push ${{ matrix.image }}${{ matrix.tag_suffix }} (${{ matrix.platform }})
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
permissions:
contents: read
packages: write
id-token: write
strategy:
fail-fast: false
matrix:
image: [presidio-anonymizer, presidio-analyzer, presidio-image-redactor]
platform: [linux/amd64, linux/arm64]
variant: [default, distroless]
exclude:
# No distroless variant is published for the image-redactor: it
# depends on the Tesseract OCR system packages, which are not
# available in a distroless base image.
- image: presidio-image-redactor
variant: distroless
include:
- platform: linux/amd64
platform_tag: linux-amd64
- platform: linux/arm64
platform_tag: linux-arm64
- variant: default
dockerfile: Dockerfile
tag_suffix: ''
- variant: distroless
dockerfile: Dockerfile.distroless
tag_suffix: -distroless
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.0
with:
persist-credentials: false
- name: Log in to GitHub Container Registry
env:
GHCR_TOKEN: ${{ github.repository_owner == 'data-privacy-stack' && github.token || secrets.GHCR_TOKEN }}
GHCR_USERNAME: ${{ github.repository_owner == 'data-privacy-stack' && github.actor || vars.GHCR_USERNAME || github.actor }}
run: echo "${GHCR_TOKEN}" | docker login ${{ env.IMAGE_REGISTRY }} -u "${GHCR_USERNAME}" --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Build and Push ${{ matrix.image }}${{ matrix.tag_suffix }} for ${{ matrix.platform }}
run: |
set -eu
repo="${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${{ matrix.image }}"
version_tag="${VERSION}"
if [ "${{ matrix.image }}" = "presidio-image-redactor" ]; then
version_tag="${IMAGE_VERSION}"
fi
suffix="${{ matrix.tag_suffix }}"
platform_tag="${{ matrix.platform_tag }}"
tag_args="--tag ${repo}:latest${suffix}-${platform_tag}"
tag_args="${tag_args} --tag ${repo}:${version_tag}${suffix}-${platform_tag}"
if [ -n "${DATE_TAG}" ]; then
tag_args="${tag_args} --tag ${repo}:${version_tag}-${DATE_TAG}${suffix}-${platform_tag}"
fi
# shellcheck disable=SC2086
docker buildx build \
--platform ${{ matrix.platform }} \
--file ./${{ matrix.image }}/${{ matrix.dockerfile }} \
--cache-from type=registry,ref=${repo}:latest${suffix} \
--cache-to type=inline \
${tag_args} \
--attest type=sbom \
--attest type=provenance,mode=max \
--push \
./${{ matrix.image }}
env:
VERSION: ${{ inputs.version }}
IMAGE_VERSION: ${{ inputs.image-version }}
DATE_TAG: ${{ inputs.date-tag }}
create-container-manifests:
name: Create Multi-Platform Container Manifests
runs-on: ubuntu-latest
needs: build-and-push-containers
permissions:
contents: read
packages: write
steps:
- name: Log in to GitHub Container Registry
env:
GHCR_TOKEN: ${{ github.repository_owner == 'data-privacy-stack' && github.token || secrets.GHCR_TOKEN }}
GHCR_USERNAME: ${{ github.repository_owner == 'data-privacy-stack' && github.actor || vars.GHCR_USERNAME || github.actor }}
run: echo "${GHCR_TOKEN}" | docker login ${{ env.IMAGE_REGISTRY }} -u "${GHCR_USERNAME}" --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Create multi-platform manifests
run: |
set -eu
images=(presidio-anonymizer presidio-analyzer presidio-image-redactor)
platform_tags=(linux-amd64 linux-arm64)
for image in "${images[@]}"; do
repo="${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${image}"
version_tag="${VERSION}"
suffixes=("" "-distroless")
if [ "${image}" = "presidio-image-redactor" ]; then
version_tag="${IMAGE_VERSION}"
suffixes=("")
fi
for suffix in "${suffixes[@]}"; do
tags=("latest${suffix}" "${version_tag}${suffix}")
if [ -n "${DATE_TAG}" ]; then
tags+=("${version_tag}-${DATE_TAG}${suffix}")
fi
for tag in "${tags[@]}"; do
platform_refs=()
for platform_tag in "${platform_tags[@]}"; do
platform_refs+=("${repo}:${tag}-${platform_tag}")
done
docker buildx imagetools create \
--tag "${repo}:${tag}" \
"${platform_refs[@]}"
done
done
done
env:
VERSION: ${{ inputs.version }}
IMAGE_VERSION: ${{ inputs.image-version }}
DATE_TAG: ${{ inputs.date-tag }}
+71
View File
@@ -0,0 +1,71 @@
name: Rebuild Images
# Published images pin their base image by digest, so they never pick up
# operating system security updates on their own. Rebuilding on a schedule
# republishes the same versions on top of a freshly patched base, which is what
# container compliance scanners expect ("redeploy the image", "update the
# vulnerable image reference").
#
# Rebuilds do not change any application code: the workflow builds the current
# state of main and republishes the `latest` and current version tags, plus an
# immutable `<version>-<date>` tag so a specific rebuild can be pinned.
on:
schedule:
# Every Monday at 05:00 UTC.
- cron: '0 5 * * 1'
workflow_dispatch:
permissions: read-all
jobs:
get-version:
name: Get Version Numbers
runs-on: ubuntu-slim
outputs:
version: ${{ steps.set-version.outputs.version }}
image-version: ${{ steps.set-image-version.outputs.image-version }}
date-tag: ${{ steps.set-date-tag.outputs.date-tag }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.0
with:
persist-credentials: false
- name: Extract main version
id: set-version
run: |
set -eu
ver=$(grep -m 1 version presidio-analyzer/pyproject.toml | tr -s ' ' | tr -d '"' | tr -d "'" | cut -d' ' -f3)
echo "version=$ver" >> $GITHUB_OUTPUT
echo "Main version: $ver"
- name: Extract image-redactor version
id: set-image-version
run: |
set -eu
imageVer=$(grep -m 1 version presidio-image-redactor/pyproject.toml | tr -s ' ' | tr -d '"' | tr -d "'" | cut -d' ' -f3)
echo "image-version=$imageVer" >> $GITHUB_OUTPUT
echo "Image-redactor version: $imageVer"
- name: Set date tag
id: set-date-tag
run: |
set -eu
dateTag=$(date -u +'%Y%m%d')
echo "date-tag=$dateTag" >> $GITHUB_OUTPUT
echo "Date tag: $dateTag"
rebuild-and-push-containers:
name: Rebuild and Push Container Images
needs: get-version
permissions:
contents: read
packages: write
id-token: write
uses: ./.github/workflows/build-images.yml
with:
version: ${{ needs.get-version.outputs.version }}
image-version: ${{ needs.get-version.outputs.image-version }}
date-tag: ${{ needs.get-version.outputs.date-tag }}
secrets: inherit
+6 -97
View File
@@ -5,10 +5,6 @@ on:
permissions: read-all
env:
IMAGE_REGISTRY: ghcr.io
IMAGE_NAMESPACE: data-privacy-stack
jobs:
get-version:
name: Get Version Numbers
@@ -114,101 +110,14 @@ jobs:
skip-existing: true
build-and-push-containers:
name: Build and Push ${{ matrix.image }} (${{ matrix.platform }})
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
name: Build and Push Container Images
needs: get-version
permissions:
contents: read
packages: write
id-token: write
strategy:
fail-fast: false
matrix:
image: [presidio-anonymizer, presidio-analyzer, presidio-image-redactor]
platform: [linux/amd64, linux/arm64]
include:
- platform: linux/amd64
platform_tag: linux-amd64
- platform: linux/arm64
platform_tag: linux-arm64
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.0
with:
persist-credentials: false
- name: Log in to GitHub Container Registry
env:
GHCR_TOKEN: ${{ github.repository_owner == 'data-privacy-stack' && github.token || secrets.GHCR_TOKEN }}
GHCR_USERNAME: ${{ github.repository_owner == 'data-privacy-stack' && github.actor || vars.GHCR_USERNAME || github.actor }}
run: echo "${GHCR_TOKEN}" | docker login ${{ env.IMAGE_REGISTRY }} -u "${GHCR_USERNAME}" --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Build and Push ${{ matrix.image }} for ${{ matrix.platform }}
run: |
repo="${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${{ matrix.image }}"
version_tag="${VERSION}"
if [ "${{ matrix.image }}" = "presidio-image-redactor" ]; then
version_tag="${IMAGE_VERSION}"
fi
docker buildx build \
--platform ${{ matrix.platform }} \
--cache-from type=registry,ref=${repo}:latest \
--cache-to type=inline \
--tag "${repo}:latest-${{ matrix.platform_tag }}" \
--tag "${repo}:${version_tag}-${{ matrix.platform_tag }}" \
--attest type=sbom \
--attest type=provenance,mode=max \
--push \
./${{ matrix.image }}
env:
VERSION: ${{ needs.get-version.outputs.version }}
IMAGE_VERSION: ${{ needs.get-version.outputs.image-version }}
create-container-manifests:
name: Create Multi-Platform Container Manifests
runs-on: ubuntu-latest
needs: [get-version, build-and-push-containers]
permissions:
contents: read
packages: write
steps:
- name: Log in to GitHub Container Registry
env:
GHCR_TOKEN: ${{ github.repository_owner == 'data-privacy-stack' && github.token || secrets.GHCR_TOKEN }}
GHCR_USERNAME: ${{ github.repository_owner == 'data-privacy-stack' && github.actor || vars.GHCR_USERNAME || github.actor }}
run: echo "${GHCR_TOKEN}" | docker login ${{ env.IMAGE_REGISTRY }} -u "${GHCR_USERNAME}" --password-stdin
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
- name: Create multi-platform manifests
run: |
images=(presidio-anonymizer presidio-analyzer presidio-image-redactor)
platform_tags=(linux-amd64 linux-arm64)
for image in "${images[@]}"; do
repo="${IMAGE_REGISTRY}/${IMAGE_NAMESPACE}/${image}"
version_tag="${VERSION}"
if [ "${image}" = "presidio-image-redactor" ]; then
version_tag="${IMAGE_VERSION}"
fi
for tag in latest "${version_tag}"; do
platform_refs=()
for platform_tag in "${platform_tags[@]}"; do
platform_refs+=("${repo}:${tag}-${platform_tag}")
done
docker buildx imagetools create \
--tag "${repo}:${tag}" \
"${platform_refs[@]}"
done
done
env:
VERSION: ${{ needs.get-version.outputs.version }}
IMAGE_VERSION: ${{ needs.get-version.outputs.image-version }}
uses: ./.github/workflows/build-images.yml
with:
version: ${{ needs.get-version.outputs.version }}
image-version: ${{ needs.get-version.outputs.image-version }}
secrets: inherit