diff --git a/docker-compose.yml b/docker-compose.yml index 3122cbc..19596c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -224,7 +224,7 @@ services: ws-gateway: container_name: easyai-wsgateway # websocket网关 # 阿里云镜像地址 - image: registry.cn-shanghai.aliyuncs.com/easyaigc/wsgateway:latest # 阿里云 + image: registry.cn-shanghai.aliyuncs.com/easyaigc/wsgateway:${WS_GATEWAY_VERSION:-latest} # 阿里云 labels: - "com.centurylinklabs.watchtower.enable=${WATCHTOWER_CORE_UPDATE_ENABLED:-true}" - "com.centurylinklabs.watchtower.scope=${WATCHTOWER_SCOPE:-}" @@ -391,7 +391,7 @@ services: max-size: "100m" max-file: "10" easyai-updater: - image: registry.cn-shanghai.aliyuncs.com/easyaigc/easyai-updater:latest + image: registry.cn-shanghai.aliyuncs.com/easyaigc/easyai-updater:${UPDATER_VERSION:-latest} container_name: easyai-updater profiles: - managed-update diff --git a/scripts/test-model-conversion-update.py b/scripts/test-model-conversion-update.py index 073f7f8..cfe5ab7 100644 --- a/scripts/test-model-conversion-update.py +++ b/scripts/test-model-conversion-update.py @@ -56,7 +56,7 @@ if args[:1] == ['inspect']: print('true|unhealthy' if unhealthy else 'true|healthy') sys.exit(0) sys.exit(1) -if args[:3] == ['compose', 'config', '--services']: print('easyai-server\\neasyai-web\\nws-gateway\\nmodel-conversion-worker\\nmongo\\nredis') +if args[:3] == ['compose', 'config', '--services']: print('easyai-server\\neasyai-web\\nws-gateway\\neasyai-updater\\nmodel-conversion-worker\\nmongo\\nredis') elif args[:3] == ['compose', 'ps', '-q']: print('container-' + args[-1]) sys.exit(0) ''') @@ -121,6 +121,29 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar result = run(command, root, {**env, 'UPDATE_TEST_UNHEALTHY': '1', 'UPDATE_HEALTH_TIMEOUT_SECONDS': '1'}) assert result.returncode != 0 assert '应用更新成功' not in result.stdout + if name == 'bash': + companion_env = {**env, 'UPDATE_SERVICES': 'ws-gateway easyai-updater', + 'UPDATE_GATEWAY_VERSION': '3.25.0.20261009', + 'UPDATE_UPDATER_VERSION': '3.25.0.20261009'} + original = (root / '.env').read_bytes() + for overrides in ({'UPDATE_GATEWAY_VERSION': 'latest;unsafe'}, + {'UPDATE_SERVICES': 'ws-gateway'}): + log.write_text('') + result = run(command, root, {**companion_env, **overrides}) + assert result.returncode != 0 + assert original == (root / '.env').read_bytes() + assert not any(c[:2] in [['compose', 'pull'], ['compose', 'up']] for c in calls(log)) + log.write_text('') + result = run(command, root, companion_env) + assert result.returncode == 0, result.stdout + result.stderr + assert ['compose', 'pull', 'ws-gateway', 'easyai-updater'] in calls(log) + assert ['compose', 'up', '-d', '--no-deps', 'ws-gateway', 'easyai-updater'] in calls(log) + new_values = dict(line.split('=', 1) for line in (root / '.env').read_text().splitlines() if '=' in line and not line.startswith('#')) + assert new_values['WS_GATEWAY_VERSION'] == new_values['UPDATER_VERSION'] == '3.25.0.20261009' + assert new_values['VERSION'] == values['VERSION'] + assert new_values['MODEL_CONVERSION_WORKER_VERSION'] == values['MODEL_CONVERSION_WORKER_VERSION'] + assert new_values['CONFIG_JWT_SECRET'] == values['CONFIG_JWT_SECRET'] + print('bash: independent companion pins preserve non-target versions and reject invalid scope passed') print(f'{name}: selected services, stable secret, invalid service and unhealthy Worker passed') case = base / 'self-update' diff --git a/system-update/release-manifest.json b/system-update/release-manifest.json index ea85b47..32978e6 100644 --- a/system-update/release-manifest.json +++ b/system-update/release-manifest.json @@ -1,10 +1,10 @@ { "schemaVersion": 1, "channel": "stable", - "releaseId": "3.24.0.20261008.2-3abdf832363c", + "releaseId": "3.24.0.20261008.2-e5c1df43a084", "productVersion": "3.24.0.20261008.2", "platform": "linux/amd64", - "publishedAt": "2026-10-08T12:47:02.542869+00:00", + "publishedAt": "2026-10-08T19:12:50.599329+00:00", "managedUpdateAllowed": true, "scheduledUpdateAllowed": true, "rollbackSafe": true, @@ -22,9 +22,9 @@ { "key": "ws-gateway", "image": "registry.cn-shanghai.aliyuncs.com/easyaigc/wsgateway", - "digest": "sha256:a2b5fb67384c7f0eca7c7a768b5deacb4c0d2195dfbeb6e16ef40d6dc097bf25", - "buildId": "retained-formal-image", - "imageSizeBytes": 751972019 + "digest": "sha256:1dd4247cf6a8a21d336876b5d97ecdc3e4ce43199b2f31aff18e0fab47bb4dca", + "buildId": "7eea56adc8d77fc76bdc895421c364f66dd59d19", + "imageSizeBytes": 751983343 }, { "key": "web", diff --git a/update.sh b/update.sh index 6b2ad14..70f197a 100755 --- a/update.sh +++ b/update.sh @@ -21,6 +21,8 @@ if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then echo " 仅拉取并重建指定服务;不重建其依赖。留空保持整组更新。" echo " UPDATE_RELEASE_VERSION=固定版本" echo " 通过脚本固定核心服务和转换 Worker 的镜像版本,不提升 latest。" + echo " UPDATE_GATEWAY_VERSION=固定版本 / UPDATE_UPDATER_VERSION=固定版本" + echo " 仅对已选中的网关或更新服务固定已核验镜像。" echo " UPDATE_ADOPT_LEGACY_CONFIG=1" echo " 备份旧部署配置并将本地差异迁移为独立覆盖文件。" echo " UPDATE_RESTORE_REPOSITORY_IMAGES='easyai-web'" @@ -109,6 +111,22 @@ if [ -n "${UPDATE_RELEASE_VERSION:-}" ] && ! [[ "$UPDATE_RELEASE_VERSION" =~ ^[0 exit 1 fi +# 配套服务可独立更新;固定版本不得改变未选中服务的环境。 +for option in UPDATE_GATEWAY_VERSION UPDATE_UPDATER_VERSION; do + value="${!option:-}" + [ -n "$value" ] || continue + if ! [[ "$value" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]{8}(\.[0-9]+)?$ ]]; then + echo "❌ $option 格式不正确;尚未修改配置或容器" >&2 + exit 1 + fi + service=ws-gateway + [ "$option" != UPDATE_UPDATER_VERSION ] || service=easyai-updater + if ! [[ " ${UPDATE_SERVICES:-} " == *" $service "* ]]; then + echo "❌ $option 要求在 UPDATE_SERVICES 中指定 $service;尚未修改配置或容器" >&2 + exit 1 + fi +done + # 拉取整个仓库更新 if [ "$SKIP_REPO_UPDATE" = false ]; then echo "" @@ -188,6 +206,13 @@ if [ -n "${UPDATE_RELEASE_VERSION:-}" ]; then echo "📌 已固定本次发布版本: $UPDATE_RELEASE_VERSION" fi +if [ -n "${UPDATE_GATEWAY_VERSION:-}" ]; then + write_env_value .env WS_GATEWAY_VERSION "$UPDATE_GATEWAY_VERSION" +fi +if [ -n "${UPDATE_UPDATER_VERSION:-}" ]; then + write_env_value .env UPDATER_VERSION "$UPDATE_UPDATER_VERSION" +fi + # 在更新镜像和重建服务前迁移并校验后端公开 API 地址。 # shellcheck source=scripts/init-public-api-base-url.sh . ./scripts/init-public-api-base-url.sh