set -euo pipefail release=/home/ps/token-usage-20260917 cd /opt/im-admin/deploy/im-admin test "$(docker image inspect zyt/wecom-admin:current --format '{{.Id}}')" = 'sha256:6e1ef31d8d419be0cdb15db49377aea66556379742442c5fd843ff455b5e8da7' python3 - <<'PY' from pathlib import Path import json,hashlib root=Path('/opt/im-admin');release=Path('/home/ps/token-usage-20260917') baseline=json.loads((release/'baseline.json').read_text())['expected'] for name, expected in baseline.items(): assert hashlib.sha256((root/'wechat_rpa'/name).read_bytes()).hexdigest()==expected,name+' changed during staging' PY rollback_on_error() { rc=$? trap - ERR docker compose logs --no-color --tail=120 > "$release/failed-startup.log" 2>&1 || true bash "$release/rollback.sh" exit "$rc" } trap rollback_on_error ERR python3 - <<'PY' from pathlib import Path import shutil,json,hashlib release=Path('/home/ps/token-usage-20260917');target=Path('/opt/im-admin') for row in json.loads((release/'manifest.json').read_text()): source=release/'payload'/row['path'];destination=(target/row['path']).resolve() assert target in destination.parents assert hashlib.sha256(source.read_bytes()).hexdigest()==row['sha256'] destination.parent.mkdir(parents=True,exist_ok=True) shutil.copyfile(source,destination) print('Token usage source and frontend installed.',flush=True) PY docker compose config --quiet docker compose stop -t 190 knowledge-worker docker compose stop -t 65 gateway docker tag zyt/wecom-admin:token-usage-20260917 zyt/wecom-admin:current docker compose up -d --no-build --wait --wait-timeout 180 api gateway knowledge-worker curl -fsS http://127.0.0.1:18766/api/v2/health curl -fsS http://127.0.0.1:18770/health docker compose ps bash "$release/04-verify.sh" trap - ERR echo 'Deployment verified successfully.'