From c0bca5c12df85e77d4380d74afcd75143d28ccfa Mon Sep 17 00:00:00 2001 From: vikingowl Date: Mon, 18 May 2026 18:07:35 +0200 Subject: [PATCH] feat(deploy): add scripts/build-push.sh for the Zot registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit registry.itsh.dev runs Zot, which only accepts OCI image indexes with SLSA provenance attestations. A plain 'docker build && docker push' produces a bare manifest and is rejected with 'manifest invalid' — the same gotcha documented in self-hosted/ente. The script mirrors that working pattern: buildx with --provenance mode=max, --platform linux/amd64, integrated --push. Auto-detects the default tag from cronjob.yaml so build-push and the manifest can't drift; override with TAG= when releasing a new version. --- README.md | 14 +++++++-- scripts/build-push.sh | 69 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100755 scripts/build-push.sh diff --git a/README.md b/README.md index 1b580bc..f3c34f5 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,20 @@ Entrypoint: `python -m claude_matrix_bot.reset_watcher`. ### 1. Build and push the image +The target registry is `registry.itsh.dev` (Zot), which requires OCI image +indexes wrapped with SLSA provenance attestations. A plain `docker build` +followed by `docker push` produces a bare manifest and gets rejected with +`manifest invalid`. Use the helper script: + ``` -docker build -t /claude-matrix-bot: . -docker push /claude-matrix-bot: +./scripts/build-push.sh # builds the tag pinned in cronjob.yaml +TAG=0.2.0 ./scripts/build-push.sh # release a new version ``` -Edit `deploy/k8s/cronjob.yaml` and replace the `image:` ref. +The script auto-detects the current pinned tag from `deploy/k8s/cronjob.yaml`, +runs `docker buildx build --provenance mode=max --platform linux/amd64 --push`, +and verifies the manifest landed. When you bump versions, edit the `image:` +ref in `cronjob.yaml` after the push, then `kubectl apply -k deploy/k8s`. ### 2. Create the Matrix bot account diff --git a/scripts/build-push.sh b/scripts/build-push.sh new file mode 100755 index 0000000..8cd8611 --- /dev/null +++ b/scripts/build-push.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# build-push.sh — build the claude-matrix-bot image and push to registry.itsh.dev. +# +# registry.itsh.dev runs Zot, which only accepts images wrapped in an OCI image +# index with SLSA-style provenance attestations — exactly what BuildKit produces +# when `--provenance` is enabled. A plain `docker build && docker push` flow +# produces a bare image manifest (no index) and Zot rejects it as +# `manifest invalid`. Hence `--provenance mode=max` and the integrated `--push`. +# +# Env overrides: +# TAG image tag (default: matches deploy/k8s/cronjob.yaml's pinned tag) +# REGISTRY registry hostname (default: registry.itsh.dev) +# IMAGE_REPO full repo path (default: ${REGISTRY}/vikingowl/claude-matrix-bot) +# PLATFORM target platform (default: linux/amd64) +# +# Examples: +# ./scripts/build-push.sh # build current pinned tag +# TAG=0.2.0 ./scripts/build-push.sh # release a new version +# TAG=$(git rev-parse --short=8 HEAD) ./scripts/build-push.sh # SHA tag + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +REGISTRY="${REGISTRY:-registry.itsh.dev}" +IMAGE_REPO="${IMAGE_REPO:-${REGISTRY}/vikingowl/claude-matrix-bot}" +PLATFORM="${PLATFORM:-linux/amd64}" + +# Default TAG = whatever cronjob.yaml currently pins. Keeps build-push and the +# manifest in sync without forcing operators to remember a magic number. +DEFAULT_TAG="$(grep -E 'image:\s+registry\.itsh\.dev/' "${REPO_ROOT}/deploy/k8s/cronjob.yaml" \ + | sed -E 's|.*:([^:[:space:]]+)\s*$|\1|' \ + | head -n1)" +TAG="${TAG:-${DEFAULT_TAG:-0.1.0}}" + +IMAGE="${IMAGE_REPO}:${TAG}" + +if ! command -v docker >/dev/null 2>&1; then + echo "error: docker not installed" >&2 + exit 1 +fi + +echo ">> Building + pushing ${IMAGE}" +echo ">> platform=${PLATFORM} provenance=mode=max (Zot requires the OCI index wrapper)" + +docker buildx build \ + --platform "${PLATFORM}" \ + --provenance mode=max \ + --push \ + -t "${IMAGE}" \ + "${REPO_ROOT}" + +echo "" +echo ">> Verifying manifest landed in registry" +docker manifest inspect "${IMAGE}" >/dev/null + +echo "" +echo "Done. Image: ${IMAGE}" +echo "" +echo "Next steps:" +if [[ "${TAG}" != "${DEFAULT_TAG}" ]]; then + echo " 1. Bump the image tag in deploy/k8s/cronjob.yaml to :${TAG}" + echo " (currently pinned to :${DEFAULT_TAG})" + echo " 2. kubectl -n tenant-2 apply -k deploy/k8s" +else + echo " 1. kubectl -n tenant-2 apply -k deploy/k8s # already pinned to :${TAG}" +fi +echo " 3. kubectl -n tenant-2 create job --from=cronjob/claude-matrix-bot-reset-watcher manual-test-N" +echo " 4. kubectl -n tenant-2 logs -f job/manual-test-N"