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
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:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Executable
+69
@@ -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"
|
||||||
Reference in New Issue
Block a user