Quality gates / quality (pull_request) Successful in 3m29s
A worker that answers /v1/models has not necessarily loaded its weights, so the app was offering an AI control that could only fail after the upload. Readiness now passes only once the pinned model is loaded, and the not-loaded state reaches manual Bristol entry in one tap. Verified against the real pinned llama.cpp build: it serves 503 while loading and the gate reports the distinct not-loaded state. Adds the reproducible container recipe (digest-pinned bases, pinned llama.cpp commit, hash-verified read-only weights, unprivileged, loopback-only publish), one shared post-load readiness gate used by the host bootstrap and the image healthcheck, and a target-GPU benchmark that refuses to write a receipt it did not measure. Refs #15
86 lines
3.3 KiB
Bash
Executable File
86 lines
3.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build the pinned Timmy worker image (issue #15).
|
|
#
|
|
# Every pin is checked before docker is invoked, so an unpinned or floating reference fails here
|
|
# rather than producing an image nobody can reproduce.
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
RECEIPTS="$REPO_ROOT/deploy/worker/receipts.json"
|
|
IMAGE="${TIMMY_WORKER_IMAGE:-timmy-vision-worker}"
|
|
TAG="${TIMMY_WORKER_TAG:-$(date -u +%Y%m%d)}"
|
|
PUBLISH_HOST="${TIMMY_WORKER_PUBLISH_HOST:-127.0.0.1}"
|
|
PORT="${TIMMY_MODEL_PORT:-8080}"
|
|
|
|
receipt_field() {
|
|
python3 - "$RECEIPTS" "$1" <<'PY'
|
|
import json
|
|
import sys
|
|
document = json.load(open(sys.argv[1]))
|
|
value = document
|
|
for part in sys.argv[2].split('.'):
|
|
value = value[int(part)] if part.isdigit() else value[part]
|
|
print(value)
|
|
PY
|
|
}
|
|
|
|
# An explicit value still wins, but an empty one is treated as an invalid pin rather than a default.
|
|
LLAMA_CPP_COMMIT="${TIMMY_LLAMA_CPP_COMMIT-$(receipt_field llamaCpp.commit)}"
|
|
MODEL_SHA256="${TIMMY_MODEL_SHA256-$(receipt_field models.0.sha256)}"
|
|
MMPROJ_SHA256="${TIMMY_MMPROJ_SHA256-$(receipt_field models.1.sha256)}"
|
|
MODEL_FILE="$(receipt_field models.0.file)"
|
|
MMPROJ_FILE="$(receipt_field models.1.file)"
|
|
BUILD_BASE="$(receipt_field baseImages.build.reference)@$(receipt_field baseImages.build.digest)"
|
|
RUNTIME_BASE="$(receipt_field baseImages.runtime.reference)@$(receipt_field baseImages.runtime.digest)"
|
|
|
|
if [[ ! "$LLAMA_CPP_COMMIT" =~ ^[0-9a-f]{40}$ ]]; then
|
|
printf 'Refusing to build: the llama.cpp pin must be a 40-hex commit, got %s (floating refs are not reproducible).\n' "${LLAMA_CPP_COMMIT:-<empty>}" >&2
|
|
exit 64
|
|
fi
|
|
for pin in "$MODEL_SHA256" "$MMPROJ_SHA256"; do
|
|
if [[ ! "$pin" =~ ^[0-9a-f]{64}$ ]]; then
|
|
printf 'Refusing to build: every model hash must be a pinned sha256, got %s.\n' "${pin:-<empty>}" >&2
|
|
exit 64
|
|
fi
|
|
done
|
|
for base in "$BUILD_BASE" "$RUNTIME_BASE"; do
|
|
if [[ ! "$base" =~ @sha256:[0-9a-f]{64}$ ]]; then
|
|
printf 'Refusing to build: base images must be digest-pinned, got %s.\n' "$base" >&2
|
|
exit 64
|
|
fi
|
|
done
|
|
|
|
if [[ ! "$PUBLISH_HOST" =~ ^(127\.0\.0\.1|::1|localhost)$ ]]; then
|
|
printf 'Refusing to publish the worker outside loopback (127.0.0.1): got %s.\n' "$PUBLISH_HOST" >&2
|
|
exit 64
|
|
fi
|
|
|
|
print_plan() {
|
|
printf 'image: %s:%s\n' "$IMAGE" "$TAG"
|
|
printf 'llama.cpp commit: %s\n' "$LLAMA_CPP_COMMIT"
|
|
printf '%s sha256: %s\n' "$MODEL_FILE" "$MODEL_SHA256"
|
|
printf '%s sha256: %s\n' "$MMPROJ_FILE" "$MMPROJ_SHA256"
|
|
printf 'base image (build): %s\n' "$BUILD_BASE"
|
|
printf 'base image (runtime): %s\n' "$RUNTIME_BASE"
|
|
printf 'bind address: %s:%s\n' "$PUBLISH_HOST" "$PORT"
|
|
printf 'publish: %s:%s\n' "$PUBLISH_HOST" "$PORT"
|
|
printf 'docker build --file deploy/worker/Dockerfile --build-arg LLAMA_CPP_COMMIT=%s --build-arg MODEL_SHA256=%s --build-arg MMPROJ_SHA256=%s --tag %s:%s .\n' \
|
|
"$LLAMA_CPP_COMMIT" "$MODEL_SHA256" "$MMPROJ_SHA256" "$IMAGE" "$TAG"
|
|
}
|
|
|
|
if [[ "${1:-}" == '--print-plan' ]]; then
|
|
print_plan
|
|
exit 0
|
|
fi
|
|
|
|
print_plan
|
|
cd "$REPO_ROOT"
|
|
docker build \
|
|
--file deploy/worker/Dockerfile \
|
|
--build-arg "LLAMA_CPP_COMMIT=$LLAMA_CPP_COMMIT" \
|
|
--build-arg "MODEL_SHA256=$MODEL_SHA256" \
|
|
--build-arg "MMPROJ_SHA256=$MMPROJ_SHA256" \
|
|
--tag "$IMAGE:$TAG" \
|
|
.
|
|
printf 'Built %s:%s from pinned receipts. Run it with deploy/worker/run_worker_container.sh\n' "$IMAGE" "$TAG"
|