Pull the server image on the Pi instead of building it

The Pi now runs registry.mc02.dev/schulcloud-mcp. Its compose file `!reset`s
the build section docker-compose.yml declares, so no `docker compose` command
there can fall back to building (Compose 2.24 or later). SCHULCLOUD_MCP_TAG
pins a commit's image; unset, the Pi follows `latest`.

`npm run publish-image` publishes from a development machine. It builds only a
clean working tree, so a commit tag names exactly that commit's code, for amd64
and arm64, tagged `latest` and with the short commit id. On an x86 machine
arm64 builds under QEMU, which the script checks for and explains rather than
registering unasked.

PI.md logs in to the registry, pulls and starts instead of building, and
updates by publishing and pulling. Rehearsed in a scratch compose project: the
image came from the registry, `up -d --build` built nothing, and the server
came up healthy with its database and no published ports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-09-16 21:26:44 +02:00
parent bac91303bf
commit bfccb3f343
7 changed files with 141 additions and 19 deletions

48
scripts/publish-image.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# Builds the server image for the Pi (arm64) and x86 hosts (amd64) and pushes it
# to the registry, tagged `latest` and with the commit it was built from.
#
# The Pi never builds: deploy/docker-compose.pi.yml pulls what this pushes.
# Only a clean working tree is built, so a commit tag names exactly the code in
# that commit — a tag built from uncommitted edits would name nothing.
#
# npm run publish-image # registry.mc02.dev/schulcloud-mcp
# IMAGE=registry.example/other npm run publish-image
set -euo pipefail
IMAGE=${IMAGE:-registry.mc02.dev/schulcloud-mcp}
PLATFORMS=${PLATFORMS:-linux/amd64,linux/arm64}
cd "$(dirname "$0")/.."
if [ -n "$(git status --porcelain)" ]; then
echo "The working tree has uncommitted changes. Commit them first: the image's tag must name exactly one commit." >&2
exit 1
fi
# An x86 machine builds arm64 under QEMU emulation, which has to be registered
# once per boot. Registering needs a privileged container, so say how rather
# than doing it unasked.
if [[ "$PLATFORMS" == *linux/arm64* ]] && ! docker buildx inspect | grep -q 'linux/arm64'; then
echo "This builder cannot build linux/arm64. Register emulation (lasts until the next reboot) with:" >&2
echo " docker run --privileged --rm tonistiigi/binfmt --install arm64" >&2
exit 1
fi
revision=$(git rev-parse HEAD)
tag=$(git rev-parse --short HEAD)
# One multi-platform push needs either Docker's containerd image store or a
# docker-container builder (`docker buildx create --use`); buildx says which
# is missing if neither is there.
docker buildx build \
--platform "$PLATFORMS" \
--tag "$IMAGE:latest" \
--tag "$IMAGE:$tag" \
--label org.opencontainers.image.title=schulcloud-mcp \
--label org.opencontainers.image.source=https://git.mc02.dev/fabi/Schulcloud-MCP \
--label "org.opencontainers.image.revision=$revision" \
--label "org.opencontainers.image.created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--push .
echo "Pushed $IMAGE:$tag and $IMAGE:latest for $PLATFORMS."