deploy(prod): 生产切换脚本(门控字面量+回滚标签+env 0600 备份)

This commit is contained in:
姚章浩
2026-09-17 14:25:30 +08:00
parent 5f1206b545
commit 06d11135e1
@@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Prod-server switch to the combined release:
# reasoning-body boundary refresh + offset-0 body boundary + card structural diagnostics
# + wide-screen (autoLayout) card payload
# RUN ON THE PRODUCTION HOST (120.76.218.137) as root. Do not wrap in ssh.
#
# Usage: bash prod_switch_reasoning_widescreen.sh <new-image-ref>
#
# Safety: requires the new image locally; tags the running image as a rollback tag;
# backs up runtime.env with its original mode; refuses unless DOBOTAI_IMAGE appears
# exactly once before and once after the edit and the line count is unchanged;
# refuses unless the new binary carries every release literal.
# Prints only image refs / variable names / literal names - never env values.
set -euo pipefail
NEW_IMAGE="${1:-}"
[ -n "$NEW_IMAGE" ] || { echo "ABORT: pass the new image ref as \$1" >&2; exit 1; }
RUNTIME_ENV="/opt/dobotai/container/runtime.env"
BACKUP_DIR="/opt/dobotai/container/backups"
COMPOSE_SERVICE="dobotai-dingtalk"
RELEASE_SLUG="reasoning-widescreen"
OLD_IMAGE="$(grep -m1 '^DOBOTAI_IMAGE=' "$RUNTIME_ENV" | cut -d= -f2-)"
OLD_SHA="$(echo "$OLD_IMAGE" | sed 's/.*://')"
ROLLBACK_TAG="dobotai-dingtalk:rollback-${OLD_SHA}-before-${RELEASE_SLUG}"
STAMP="$(date +%Y%m%dT%H%M%S)"
BACKUP="$BACKUP_DIR/runtime.env.before-${RELEASE_SLUG}.${STAMP}"
echo "== preflight =="
docker image inspect "$NEW_IMAGE" >/dev/null || { echo "ABORT: $NEW_IMAGE not present locally (load it first)" >&2; exit 1; }
[ "$OLD_IMAGE" != "$NEW_IMAGE" ] || { echo "ABORT: runtime.env already points at $NEW_IMAGE" >&2; exit 1; }
before_count="$(grep -c '^DOBOTAI_IMAGE=' "$RUNTIME_ENV")"
[ "$before_count" = "1" ] || { echo "ABORT: $before_count DOBOTAI_IMAGE lines before edit" >&2; exit 1; }
for lit in sys_full_json_obj autoLayout body_prefix_runes banner_pos PartialFeed; do
if docker run --rm --entrypoint sh "$NEW_IMAGE" -c "grep -aqF '$lit' /usr/local/bin/dobotai"; then
echo "literal $lit: PRESENT"
else
echo "ABORT: $NEW_IMAGE lacks literal '$lit' (wrong artifact)" >&2
exit 1
fi
done
echo "old_image=$OLD_IMAGE"
echo "new_image=$NEW_IMAGE"
echo "rollback_tag=$ROLLBACK_TAG"
echo "== tag rollback target =="
if docker image inspect "$ROLLBACK_TAG" >/dev/null 2>&1; then
echo "rollback tag already exists, keeping it (no overwrite)"
else
docker tag "$OLD_IMAGE" "$ROLLBACK_TAG"
echo "tagged $OLD_IMAGE -> $ROLLBACK_TAG"
fi
echo "== backup runtime.env =="
mkdir -p "$BACKUP_DIR"
ORIG_MODE="$(stat -c '%a' "$RUNTIME_ENV")"
install -m "$ORIG_MODE" "$RUNTIME_ENV" "$BACKUP"
[ "$ORIG_MODE" = "600" ] || chmod 0600 "$RUNTIME_ENV"
echo "backup=$BACKUP mode=$ORIG_MODE"
echo "== rewrite DOBOTAI_IMAGE =="
tmp="$(mktemp /opt/dobotai/container/.runtime.env.XXXXXX)"
chmod "$ORIG_MODE" "$tmp"
awk -v new="$NEW_IMAGE" '/^DOBOTAI_IMAGE=/ { sub(/^DOBOTAI_IMAGE=.*/, "DOBOTAI_IMAGE=" new) } { print }' "$RUNTIME_ENV" >"$tmp"
after_count="$(grep -c "^DOBOTAI_IMAGE=$NEW_IMAGE\$" "$tmp")"
if [ "$after_count" != "1" ] || [ "$(wc -l <"$tmp")" != "$(wc -l <"$RUNTIME_ENV")" ]; then
rm -f "$tmp"
echo "ABORT: post-edit verification failed (match=$after_count)" >&2
exit 1
fi
mv "$tmp" "$RUNTIME_ENV"
grep -n '^DOBOTAI_IMAGE=' "$RUNTIME_ENV"
echo "== recreate container =="
cd /opt/dobotai/container
docker compose --env-file runtime.env -f compose.yaml up -d --no-deps --force-recreate "$COMPOSE_SERVICE"
echo "== readback =="
sleep 4
curl -fsS -m 5 http://127.0.0.1:8090/healthz; echo
docker inspect "$COMPOSE_SERVICE" --format 'image={{.Config.Image}} status={{.State.Status}} started={{.State.StartedAt}}'
for lit in sys_full_json_obj autoLayout body_prefix_runes banner_pos PartialFeed; do
if docker exec "$COMPOSE_SERVICE" grep -aqF "$lit" /proc/1/root/usr/local/bin/dobotai; then
echo "running literal $lit: PRESENT"
else
echo "running literal $lit: ABSENT"
fi
done
echo "== log scan (since 90s) =="
docker logs "$COMPOSE_SERVICE" --since 90s 2>&1 | grep -icE 'panic|fatal|level=error| ERROR ' || echo "0 error/panic lines"
docker logs "$COMPOSE_SERVICE" --since 90s 2>&1 | tail -6
echo "== rollback (one command, run on this host) =="
echo "sed -i 's|^DOBOTAI_IMAGE=.*|DOBOTAI_IMAGE=$OLD_IMAGE|' $RUNTIME_ENV && cd /opt/dobotai/container && docker compose --env-file runtime.env -f compose.yaml up -d --no-deps --force-recreate $COMPOSE_SERVICE"
echo "or restore: install -m $ORIG_MODE $BACKUP $RUNTIME_ENV (then the compose line above)"