Rename ai-tools-utils to python-tools
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8 context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; } name:ph… (push) Successful in 1m8s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18 POSTGIS_VERSION=3 VCHORD_VERSION=0.5.3 context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 1m54s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3 context:python-tools fingerprint_command:dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort name:python-tools primary_tag:git.ajpanton.se/ajp_anton/python-tools:3 tags:git.ajpa… (push) Successful in 1m39s

This commit is contained in:
ajp_anton
2026-07-16 01:00:05 +00:00
parent 19456c6467
commit 1319c71740
4 changed files with 103 additions and 18 deletions
+25 -11
View File
@@ -29,6 +29,8 @@ jobs:
image: image:
- name: postgres-custom - name: postgres-custom
context: postgres context: postgres
base_image: postgres:18
primary_tag: git.ajpanton.se/ajp_anton/postgres-custom:18
tags: | tags: |
git.ajpanton.se/ajp_anton/postgres-custom:18 git.ajpanton.se/ajp_anton/postgres-custom:18
git.ajpanton.se/ajp_anton/postgres-custom:18-postgis3-vchord0.5.3 git.ajpanton.se/ajp_anton/postgres-custom:18-postgis3-vchord0.5.3
@@ -37,21 +39,31 @@ jobs:
PG_VERSION=18 PG_VERSION=18
POSTGIS_VERSION=3 POSTGIS_VERSION=3
VCHORD_VERSION=0.5.3 VCHORD_VERSION=0.5.3
fingerprint_command: |
{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256sum {} + | LC_ALL=C sort; }
- name: php8-pgsql - name: php8-pgsql
context: php8-pgsql context: php8-pgsql
base_image: php:8-fpm-alpine
primary_tag: git.ajpanton.se/ajp_anton/php8-pgsql:8
tags: | tags: |
git.ajpanton.se/ajp_anton/php8-pgsql:8 git.ajpanton.se/ajp_anton/php8-pgsql:8
git.ajpanton.se/ajp_anton/php8-pgsql:8-fpm-alpine git.ajpanton.se/ajp_anton/php8-pgsql:8-fpm-alpine
git.ajpanton.se/ajp_anton/php8-pgsql:latest git.ajpanton.se/ajp_anton/php8-pgsql:latest
build_args: | build_args: |
PHP_VERSION=8 PHP_VERSION=8
- name: ai-tools-utils fingerprint_command: |
context: ai-tools-utils { apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; }
- name: python-tools
context: python-tools
base_image: python:3
primary_tag: git.ajpanton.se/ajp_anton/python-tools:3
tags: | tags: |
git.ajpanton.se/ajp_anton/ai-tools-utils:3 git.ajpanton.se/ajp_anton/python-tools:3
git.ajpanton.se/ajp_anton/ai-tools-utils:latest git.ajpanton.se/ajp_anton/python-tools:latest
build_args: | build_args: |
PYTHON_VERSION=3 PYTHON_VERSION=3
fingerprint_command: |
dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort
steps: steps:
- name: Check out repository - name: Check out repository
@@ -65,10 +77,12 @@ jobs:
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" --username "$REGISTRY_USERNAME" --password-stdin printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" --username "$REGISTRY_USERNAME" --password-stdin
- name: Build and push ${{ matrix.image.name }} - name: Build and push ${{ matrix.image.name }}
uses: docker/build-push-action@v6 env:
with: IMAGE_NAME: ${{ matrix.image.name }}
context: ./${{ matrix.image.context }} BUILD_CONTEXT: ${{ matrix.image.context }}
push: true BASE_IMAGE: ${{ matrix.image.base_image }}
tags: ${{ matrix.image.tags }} PRIMARY_TAG: ${{ matrix.image.primary_tag }}
build-args: ${{ matrix.image.build_args }} TAGS: ${{ matrix.image.tags }}
pull: true BUILD_ARGS: ${{ matrix.image.build_args }}
FINGERPRINT_COMMAND: ${{ matrix.image.fingerprint_command }}
run: ./scripts/build-image-if-changed.sh
+9 -7
View File
@@ -12,7 +12,7 @@ a few commonly useful database/runtime extensions.
| --- | --- | | --- | --- |
| `git.ajpanton.se/ajp_anton/postgres-custom:18` | PostgreSQL 18 with selected extension packages installed and `pg_cron`/VectorChord preloaded. | | `git.ajpanton.se/ajp_anton/postgres-custom:18` | PostgreSQL 18 with selected extension packages installed and `pg_cron`/VectorChord preloaded. |
| `git.ajpanton.se/ajp_anton/php8-pgsql:8` | PHP 8 FPM on Alpine with PostgreSQL client runtime support and the `pdo_pgsql` PHP extension enabled. | | `git.ajpanton.se/ajp_anton/php8-pgsql:8` | PHP 8 FPM on Alpine with PostgreSQL client runtime support and the `pdo_pgsql` PHP extension enabled. |
| `git.ajpanton.se/ajp_anton/ai-tools-utils:3` | Python 3 utility image with ExifTool for AI/helper workflows. | | `git.ajpanton.se/ajp_anton/python-tools:3` | Python 3 utility image with ExifTool for AI/helper workflows. |
## Included Components ## Included Components
@@ -65,7 +65,7 @@ Compiled PHP extension:
The compiled extension and PHP config are copied from a build stage into the The compiled extension and PHP config are copied from a build stage into the
final runtime image. final runtime image.
### `ai-tools-utils` ### `python-tools`
Base image: Base image:
@@ -90,7 +90,7 @@ Moving deployment tags:
- `git.ajpanton.se/ajp_anton/postgres-custom:18` - `git.ajpanton.se/ajp_anton/postgres-custom:18`
- `git.ajpanton.se/ajp_anton/php8-pgsql:8` - `git.ajpanton.se/ajp_anton/php8-pgsql:8`
- `git.ajpanton.se/ajp_anton/ai-tools-utils:3` - `git.ajpanton.se/ajp_anton/python-tools:3`
Descriptive tags: Descriptive tags:
@@ -101,15 +101,17 @@ Convenience tags:
- `git.ajpanton.se/ajp_anton/postgres-custom:latest` - `git.ajpanton.se/ajp_anton/postgres-custom:latest`
- `git.ajpanton.se/ajp_anton/php8-pgsql:latest` - `git.ajpanton.se/ajp_anton/php8-pgsql:latest`
- `git.ajpanton.se/ajp_anton/ai-tools-utils:latest` - `git.ajpanton.se/ajp_anton/python-tools:latest`
Tags are mutable. The digest behind a tag may change after a rebuild even when Tags are mutable. A weekly candidate build only repoints a deployment tag when
the tag name stays the same. its content fingerprint changed: base image, build context, build arguments,
installed packages, or image-specific generated files.
## Update Cadence ## Update Cadence
Images are rebuilt weekly. They may also be rebuilt after repository changes or Images are rebuilt weekly. They may also be rebuilt after repository changes or
manual runs. manual runs. Each run builds a candidate without cache, then publishes it only
when its content fingerprint differs from the current deployment image.
Each rebuild pulls the current upstream base image and installs the currently Each rebuild pulls the current upstream base image and installs the currently
available apt/apk packages. If the base image or repository packages changed, available apt/apk packages. If the base image or repository packages changed,
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
set -euo pipefail
: "${IMAGE_NAME:?}"
: "${BUILD_CONTEXT:?}"
: "${BASE_IMAGE:?}"
: "${PRIMARY_TAG:?}"
: "${TAGS:?}"
: "${BUILD_ARGS:?}"
: "${FINGERPRINT_COMMAND:?}"
candidate_tag="local/${IMAGE_NAME}:candidate"
cleanup() {
docker image rm -f "$candidate_tag" >/dev/null 2>&1 || true
}
trap cleanup EXIT
mapfile -t build_args < <(printf '%s\n' "$BUILD_ARGS" | sed '/^[[:space:]]*$/d')
build_flags=(--pull --no-cache --load --tag "$candidate_tag")
for build_arg in "${build_args[@]}"; do
build_flags+=(--build-arg "$build_arg")
done
context_hash="$(
cd "$BUILD_CONTEXT"
find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum | sha256sum | awk '{print $1}'
)"
docker pull "$BASE_IMAGE" >/dev/null
base_image_id="$(docker image inspect "$BASE_IMAGE" --format '{{.Id}}')"
docker buildx build "${build_flags[@]}" "$BUILD_CONTEXT"
fingerprint() {
{
printf 'base-image-id=%s\n' "$base_image_id"
printf 'context-hash=%s\n' "$context_hash"
printf 'build-args=%s\n' "$BUILD_ARGS"
docker run --rm --entrypoint /bin/sh "$1" -c "$FINGERPRINT_COMMAND"
} | sha256sum | awk '{print $1}'
}
candidate_fingerprint="$(fingerprint "$candidate_tag")"
docker pull "$PRIMARY_TAG" >/dev/null 2>&1 || true
current_fingerprint="$(docker image inspect "$PRIMARY_TAG" --format '{{ index .Config.Labels "org.ajpanton.content-fingerprint" }}' 2>/dev/null || true)"
if [[ -z "$current_fingerprint" ]] && docker image inspect "$PRIMARY_TAG" >/dev/null 2>&1; then
current_fingerprint="$(fingerprint "$PRIMARY_TAG")"
fi
if [[ -n "$current_fingerprint" && "$current_fingerprint" == "$candidate_fingerprint" ]]; then
echo "${IMAGE_NAME}: unchanged; not publishing"
exit 0
fi
docker buildx build "${build_flags[@]}" --label "org.ajpanton.content-fingerprint=${candidate_fingerprint}" "$BUILD_CONTEXT"
final_fingerprint="$(fingerprint "$candidate_tag")"
if [[ "$final_fingerprint" != "$candidate_fingerprint" ]]; then
echo "${IMAGE_NAME}: inputs changed while rebuilding; retry the workflow" >&2
exit 1
fi
while IFS= read -r tag; do
[[ -z "$tag" ]] && continue
docker tag "$candidate_tag" "$tag"
docker push "$tag"
done <<< "$TAGS"
echo "${IMAGE_NAME}: published"