set -euo pipefail release=/home/ps/knowledge-20260916-100045 cd /opt/im-admin/deploy/im-admin test "$(docker image inspect zyt/wecom-admin:current --format '{{.Id}}')" = "$(cat "$release/image-before.txt")" rollback() { rc=$? trap - ERR echo 'Deployment failed; restoring previous services.' docker compose stop knowledge-worker || true docker compose rm -f knowledge-worker || true python3 - <<'PY' from pathlib import Path import tarfile,shutil root=Path('/opt/im-admin') with tarfile.open('/home/ps/knowledge-20260916-100045/source-before.tar.gz') as tf: for item in tf.getmembers(): if item.isfile(): target=(root/item.name).resolve() assert root in target.parents target.parent.mkdir(parents=True,exist_ok=True) with tf.extractfile(item) as source, target.open('wb') as output: shutil.copyfileobj(source,output) PY docker tag zyt/wecom-admin:before-knowledge-20260916-100045 zyt/wecom-admin:current docker compose up -d --no-build api gateway exit "$rc" } trap rollback ERR python3 - <<'PY' from pathlib import Path import shutil source=Path('/home/ps/knowledge-20260916-100045/payload') target=Path('/opt/im-admin') for path in source.rglob('*'): if path.is_file(): destination=target/path.relative_to(source) destination.parent.mkdir(parents=True,exist_ok=True) shutil.copyfile(path,destination) print('Installed release files',flush=True) PY docker compose config --quiet docker tag zyt/wecom-admin:knowledge-20260916-100045 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 trap - ERR echo 'Services switched successfully.'