Build/apply candidate images once, require digests on promote, and keep pinned env-service from rebuilding divergent images. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
3.1 KiB
Bash
Executable File
111 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build candidate app (+events) and pi-agent images once for a commit.
|
|
# Writes .doc/test-reports/assets/promotion-receipts/<sha>.images.json
|
|
#
|
|
# Usage:
|
|
# ./scripts/candidate-images-build.sh [--commit SHA] [--force]
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "${ROOT}"
|
|
|
|
COMMIT=""
|
|
FORCE=0
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--commit) COMMIT="${2:-}"; shift 2 ;;
|
|
--force) FORCE=1; shift ;;
|
|
-h|--help)
|
|
sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 0
|
|
;;
|
|
*) echo "unknown arg: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "${COMMIT}" ]]; then
|
|
COMMIT="$(git rev-parse HEAD)"
|
|
fi
|
|
COMMIT="$(git rev-parse --verify "${COMMIT}^{commit}")"
|
|
SHORT="${COMMIT:0:12}"
|
|
RECEIPT_DIR="${ROOT}/.doc/test-reports/assets/promotion-receipts"
|
|
OUT="${RECEIPT_DIR}/${COMMIT}.images.json"
|
|
mkdir -p "${RECEIPT_DIR}"
|
|
|
|
if [[ -f "${OUT}" && "${FORCE}" -ne 1 ]]; then
|
|
echo "images.json already exists (immutable): ${OUT}" >&2
|
|
echo "hint: use --force to rebuild local fixtures" >&2
|
|
cat "${OUT}"
|
|
exit 0
|
|
fi
|
|
|
|
APP_TAG="shuimu-agent-app:candidate-${SHORT}"
|
|
EVENTS_TAG="shuimu-agent-events:candidate-${SHORT}"
|
|
PI_TAG="shuimu-pi-agent:candidate-${SHORT}"
|
|
|
|
echo "==> building app/events image ${APP_TAG}..."
|
|
docker build -f deploy/app/Dockerfile -t "${APP_TAG}" .
|
|
# events binary lives in the same image
|
|
docker tag "${APP_TAG}" "${EVENTS_TAG}"
|
|
|
|
echo "==> building pi-agent image ${PI_TAG}..."
|
|
docker build -f deploy/pi-agent/Dockerfile -t "${PI_TAG}" .
|
|
|
|
image_id() {
|
|
docker inspect --format '{{.Id}}' "$1"
|
|
}
|
|
|
|
repo_digest_or_id() {
|
|
local name="$1"
|
|
local repo
|
|
repo="$(docker inspect --format '{{index .RepoDigests 0}}' "${name}" 2>/dev/null || true)"
|
|
if [[ -n "${repo}" && "${repo}" != "<no value>" ]]; then
|
|
printf '%s' "${repo}"
|
|
else
|
|
image_id "${name}"
|
|
fi
|
|
}
|
|
|
|
APP_ID="$(image_id "${APP_TAG}")"
|
|
EVENTS_ID="$(image_id "${EVENTS_TAG}")"
|
|
PI_ID="$(image_id "${PI_TAG}")"
|
|
APP_DIGEST="$(repo_digest_or_id "${APP_TAG}")"
|
|
EVENTS_DIGEST="$(repo_digest_or_id "${EVENTS_TAG}")"
|
|
PI_DIGEST="$(repo_digest_or_id "${PI_TAG}")"
|
|
|
|
RECORDED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
export COMMIT SHORT RECORDED_AT APP_TAG EVENTS_TAG PI_TAG
|
|
export APP_ID EVENTS_ID PI_ID APP_DIGEST EVENTS_DIGEST PI_DIGEST OUT
|
|
|
|
python3 -c '
|
|
import json, os
|
|
payload = {
|
|
"commit": os.environ["COMMIT"],
|
|
"recorded_at": os.environ["RECORDED_AT"],
|
|
"tags": {
|
|
"app": os.environ["APP_TAG"],
|
|
"events": os.environ["EVENTS_TAG"],
|
|
"pi-agent": os.environ["PI_TAG"],
|
|
},
|
|
"image_ids": {
|
|
"app": os.environ["APP_ID"],
|
|
"events": os.environ["EVENTS_ID"],
|
|
"pi-agent": os.environ["PI_ID"],
|
|
},
|
|
"image_digests": {
|
|
"app": os.environ["APP_DIGEST"],
|
|
"events": os.environ["EVENTS_DIGEST"],
|
|
"pi-agent": os.environ["PI_DIGEST"],
|
|
},
|
|
}
|
|
with open(os.environ["OUT"], "w", encoding="utf-8") as f:
|
|
json.dump(payload, f, indent=2, ensure_ascii=False)
|
|
f.write("\n")
|
|
print(os.environ["OUT"])
|
|
'
|
|
|
|
echo "wrote candidate images manifest: ${OUT}"
|
|
echo "app=${APP_DIGEST}"
|
|
echo "events=${EVENTS_DIGEST}"
|
|
echo "pi-agent=${PI_DIGEST}"
|