0d986f60bd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
集成服务器托管模型、自然语言移动与有界抓放、内置 LeKiwi URL 导入和双摄像头;同步部署契约与指定域名 iframe 白名单,保留原有物理安全、会话及调用预算防护。 更新 npm 包及锁文件版本、CHANGELOG 与发布文档。提交前 typecheck、120 项定向前端测试和 44 项后端测试通过(3 项可选跳过);真实 v2 云模型抓放仍待单独验收,不包含运行密钥或构建产物。
71 lines
3.8 KiB
Bash
71 lines
3.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Run on the target host after baseline backup, image imports and TLS bootstrap.
|
|
set -euo pipefail
|
|
release=${1:?usage: publish.sh RELEASE}
|
|
[[ "$release" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$ ]] || exit 2
|
|
root=/opt/cadworld-sim
|
|
www=/opt/1panel/apps/openresty/openresty/www
|
|
site=$www/sites/cadworld-sim.robotquan.com
|
|
nginx=1Panel-openresty-m72w
|
|
stage=$root/releases/$release
|
|
[[ -f "$stage/manifest.json" ]] || { echo 'missing verified release'; exit 1; }
|
|
python3 - "$stage" <<'PY'
|
|
import hashlib,json,pathlib,sys
|
|
root=pathlib.Path(sys.argv[1])
|
|
manifest=json.loads((root/'manifest.json').read_text())
|
|
for name,digest in manifest['files'].items():
|
|
path=root/name
|
|
if '..' in pathlib.Path(name).parts or pathlib.Path(name).is_absolute() or path.is_symlink():
|
|
raise RuntimeError('invalid manifest path')
|
|
if hashlib.sha256(path.read_bytes()).hexdigest()!=digest:
|
|
raise RuntimeError('artifact checksum mismatch: '+name)
|
|
print('Release manifest verified')
|
|
PY
|
|
old=''
|
|
[[ ! -f $root/current.txt ]] || read -r old < "$root/current.txt"
|
|
docker build --network none --pull=false -t "cadworld-decision:$release" "$stage/app"
|
|
# Fail before replacing the live service if a runtime contract/module was omitted.
|
|
# No server keys, network, volumes or inference are available to this check.
|
|
docker run --rm --network none --read-only --cap-drop ALL \
|
|
--security-opt no-new-privileges --entrypoint python "cadworld-decision:$release" \
|
|
-c 'import decision_server.web_server; from decision_server.language_tasks import resolve_target; assert resolve_target("B", [])[1] == "table"; print("Runtime imports and v2 contracts verified")'
|
|
# No current traffic is changed before the build and static manifest succeed.
|
|
mkdir -p "$site/releases/$release"
|
|
cp -a "$stage/static/." "$site/releases/$release/"
|
|
chmod -R a+rX "$site/releases/$release"
|
|
restore() {
|
|
trap - ERR
|
|
if [[ -n "$old" ]]; then
|
|
cp "$root/releases/$old/deploy/compose.yaml" "$root/compose.yaml"
|
|
cp "$root/releases/$old/deploy/openresty.conf" /opt/1panel/apps/openresty/openresty/conf/conf.d/cadworld-sim.robotquan.com.conf
|
|
printf 'CADWORLD_RELEASE=%s\n' "$old" > "$root/release.env"
|
|
docker compose --env-file "$root/release.env" -f "$root/compose.yaml" up -d --wait decision || true
|
|
ln -sfn "releases/$old" "$site/current.next"
|
|
mv -Tf "$site/current.next" "$site/current"
|
|
docker exec "$nginx" nginx -t && docker exec "$nginx" nginx -s reload || true
|
|
else
|
|
docker compose --env-file "$root/release.env" -f "$root/compose.yaml" stop decision || true
|
|
conf=/opt/1panel/apps/openresty/openresty/conf/conf.d/cadworld-sim.robotquan.com.conf
|
|
[[ ! -f "$conf" ]] || mv "$conf" "$root/failed-first-vhost.conf"
|
|
docker exec "$nginx" nginx -t && docker exec "$nginx" nginx -s reload || true
|
|
fi
|
|
echo 'Publish failed; previous deployment retained/restored. Inspect logs before retry.' >&2
|
|
exit 1
|
|
}
|
|
trap restore ERR
|
|
cp "$stage/deploy/compose.yaml" "$root/compose.yaml"
|
|
printf 'CADWORLD_RELEASE=%s\n' "$release" > "$root/release.env"
|
|
docker compose --env-file "$root/release.env" -f "$root/compose.yaml" up -d --wait --wait-timeout 75 decision
|
|
curl --fail --silent --show-error --max-time 5 -H 'Host: cadworld-sim.robotquan.com' http://127.0.0.1:8768/healthz
|
|
ln -sfn "releases/$release" "$site/current.next"
|
|
mv -Tf "$site/current.next" "$site/current"
|
|
cp "$stage/deploy/openresty.conf" /opt/1panel/apps/openresty/openresty/conf/conf.d/cadworld-sim.robotquan.com.conf
|
|
docker exec "$nginx" nginx -t
|
|
docker exec "$nginx" nginx -s reload
|
|
curl --fail --silent --show-error --max-time 15 https://cadworld-sim.robotquan.com/ -o /dev/null
|
|
printf '%s\n' "$old" > "$root/previous.txt"
|
|
printf '%s\n' "$release" > "$root/current.txt"
|
|
docker image inspect "cadworld-decision:$release" --format '{{.Id}}' > "$stage/image-id.txt"
|
|
trap - ERR
|
|
echo "Published $release; previous=$old"
|