mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
fix(deploy): refuse to flatten a multi-arch tag to one architecture
`bin/deploy` builds with plain `docker build`, which produces an image for the architecture of the machine running it. Pushing that to a tag whose current manifest is a multi-arch list replaces the list outright, and every host on the other architecture fails the next pull with `exec format error`. This is not hypothetical — it happened to `:latest` on 2026-08-18 and was reported within hours as #427. The release workflow builds amd64 and arm64 and joins them into a manifest; a homelab deploy from an x86 workstation then silently flattened that to amd64-only. The registry has been repaired by re-pointing `:latest` at the `:1.28.1` manifest list. Two changes so it cannot recur: - `bin/deploy` inspects the target tag first and exits 65 if it already resolves to a manifest list, naming the private-tag fix and the pull-only alternative for deploying a released image. - `bin/deploy.env.example` shipped `IMAGE="akitaonrails/ai-memory:latest"`, so the documented setup pointed the operator's build at the public release tag by default. It now defaults to `:homelab`. Refs #427 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
acd9c0b369
commit
e40b9964b1
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- `bin/deploy` now refuses to push a single-architecture build over a tag that
|
||||
already resolves to a multi-architecture manifest (#427). It builds for the
|
||||
architecture of the machine it runs on, so deploying a homelab from an x86
|
||||
workstation to `IMAGE=akitaonrails/ai-memory:latest` replaced the
|
||||
amd64+arm64 manifest CI had published with an amd64-only image, and every
|
||||
arm64 host pulling `:latest` failed with `exec format error`. The shipped
|
||||
`bin/deploy.env.example` also defaulted `IMAGE` to `:latest`, so following
|
||||
the documented setup led straight into it; it now defaults to a private
|
||||
`:homelab` tag and explains that release tags are published by CI only.
|
||||
|
||||
### Added
|
||||
- New `strip_root_combinators` config flag (env `AI_MEMORY_STRIP_ROOT_COMBINATORS`,
|
||||
or `strip_root_combinators = true` in config.toml) strips root-level
|
||||
|
||||
+38
@@ -58,6 +58,44 @@ CONTAINER_NAME="${CONTAINER_NAME:-ai-memory}"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
# Fail closed if $IMAGE already carries a multi-architecture manifest.
|
||||
#
|
||||
# `docker build` below produces an image for THIS machine's architecture
|
||||
# only. Pushing that to a tag whose current manifest is a multi-arch list
|
||||
# replaces the list, and every host on the other architecture starts
|
||||
# failing with `exec format error` on the next pull.
|
||||
#
|
||||
# That is not hypothetical: it happened to `:latest` on 2026-08-18 (issue
|
||||
# #427). `bin/release` + the release workflow publish amd64 and arm64 and
|
||||
# join them into a manifest; a homelab deploy from an x86 workstation then
|
||||
# silently flattened it to amd64-only. Release tags are published by CI —
|
||||
# a deploy has no business overwriting one.
|
||||
if docker manifest inspect "$IMAGE" >/dev/null 2>&1 &&
|
||||
docker manifest inspect "$IMAGE" 2>/dev/null | grep -q '"manifests"'; then
|
||||
cat >&2 <<EOF
|
||||
==> REFUSING to overwrite a multi-architecture tag.
|
||||
|
||||
$IMAGE currently resolves to a multi-arch manifest (published by
|
||||
the release workflow). This script builds for $(uname -m) only, so
|
||||
pushing would drop every other architecture and break those hosts
|
||||
with "exec format error".
|
||||
|
||||
Deploy a private tag instead — set IMAGE in bin/deploy.env to
|
||||
something like:
|
||||
|
||||
IMAGE="akitaonrails/ai-memory:homelab"
|
||||
|
||||
and point docker-compose.yml on the server at that tag. Release
|
||||
tags (:latest, :X.Y.Z) are published by CI only.
|
||||
|
||||
To deploy the released image without rebuilding, skip this script
|
||||
and just pull it on the server:
|
||||
|
||||
ssh \$SERVER "cd \$DEPLOY_DIR && docker compose pull && docker compose up -d"
|
||||
EOF
|
||||
exit 65
|
||||
fi
|
||||
|
||||
echo "==> Building Docker image: $IMAGE"
|
||||
docker build -t "$IMAGE" -f docker/Dockerfile .
|
||||
|
||||
|
||||
+13
-3
@@ -12,9 +12,19 @@ DEPLOY_DIR="/var/opt/docker/utils/ai-memory"
|
||||
|
||||
# Image tag to build + push + pull. Make sure the Docker host can pull
|
||||
# from wherever you push to (Docker Hub, GHCR, your own registry).
|
||||
# Default: the official published image. Change to your own fork if
|
||||
# you're building from source.
|
||||
IMAGE="akitaonrails/ai-memory:latest"
|
||||
#
|
||||
# Use a tag of your OWN, not a release tag. `bin/deploy` builds for the
|
||||
# architecture of the machine you run it on; pushing that over `:latest`
|
||||
# or `:X.Y.Z` — which CI publishes as multi-arch manifests — drops every
|
||||
# other architecture and breaks those hosts with "exec format error"
|
||||
# (issue #427). bin/deploy refuses to do it, but pick a private tag here
|
||||
# and there is nothing to refuse.
|
||||
IMAGE="akitaonrails/ai-memory:homelab"
|
||||
|
||||
# If you only want to run the RELEASED image rather than build your own,
|
||||
# you do not need bin/deploy at all — point docker-compose.yml at
|
||||
# `akitaonrails/ai-memory:latest` on the server and run:
|
||||
# docker compose pull && docker compose up -d
|
||||
|
||||
# Optional: override the container name if your docker-compose.yml
|
||||
# uses something other than the default `ai-memory`.
|
||||
|
||||
Reference in New Issue
Block a user