#!/usr/bin/env bash set -euo pipefail test "${PATIENT_RELEASE_APPROVED:-}" = patient-session-20260918 release=/home/ps/patient-session-20260918 cd "$release" test -f preflight-complete test ! -e candidate-passed python3 patient_server_guard.py baseline test "$(docker image inspect zyt/wecom-admin:before-patient-session-20260918 --format '{{.Id}}')" = "$(cat image-before.txt)" docker exec -i im-admin-api-1 python - <<'PY' > backup-patient.json from pathlib import Path import sqlite3,shutil,os,json folder=Path('/data/backups/patient-session-20260918');folder.mkdir(mode=0o700) source=sqlite3.connect('file:/data/backend.db?mode=ro',uri=True,timeout=30) backup=sqlite3.connect(folder/'backend.db') try:source.backup(backup,pages=4096,sleep=0.05) finally:backup.close();source.close() with sqlite3.connect('file:'+str(folder/'backend.db')+'?mode=ro',uri=True) as check: assert check.execute('PRAGMA quick_check').fetchone()[0]=='ok' os.chmod(folder/'backend.db',0o600) keys=list(Path('/data').glob('*.key'));assert keys,'Encryption key backup missing' for p in keys:shutil.copy2(p,folder/p.name);os.chmod(folder/p.name,0o600) print(json.dumps({'backup':str(folder),'quick_check':'ok','bytes':(folder/'backend.db').stat().st_size,'keyCount':len(keys)})) PY docker build --network=none --pull=false -f Dockerfile.patient-overlay -t zyt/wecom-admin:patient-session-20260918 . > candidate-build.log 2>&1 mapfile -t modules < <(python3 -c 'import json;print("\n".join(json.load(open("patient-release-plan.json"))["serverTestModules"]))') test "${#modules[@]}" -gt 0 docker run --rm --network=none --read-only --tmpfs /tmp:size=512m,mode=1777 --tmpfs /data:size=256m,mode=1777 -v "$release/tests:/patient-tests:ro" -e PYTHONPATH=/patient-tests:/app/wechat_rpa zyt/wecom-admin:patient-session-20260918 python -X utf8 -B -m unittest "${modules[@]}" -q > candidate-tests.log 2>&1 python3 patient_server_guard.py candidate printf '%s\n' passed > candidate-passed