fix(deploy): 修复组件版本解析和安装升级验收流程
This commit is contained in:
@@ -22,22 +22,28 @@ selected = ['easyai-server', 'easyai-web', 'model-conversion-worker']
|
||||
|
||||
|
||||
def run(args, cwd, env=None):
|
||||
return subprocess.run(args, cwd=cwd, env=env, text=True, encoding='utf-8', errors='backslashreplace', capture_output=True, timeout=35)
|
||||
return subprocess.run(args, cwd=cwd, env=env, text=True, encoding='utf-8', errors='backslashreplace', capture_output=True, timeout=90)
|
||||
|
||||
|
||||
def fixture(base):
|
||||
root = base / 'easyai'
|
||||
root.mkdir()
|
||||
for name in ['scripts', 'update.sh', 'update.ps1', '.env.sample', 'docker-compose.yml']:
|
||||
for name in ['scripts', 'system-update', 'update.sh', 'update.ps1', '.env.sample', 'docker-compose.yml']:
|
||||
path = source / name
|
||||
if path.is_dir():
|
||||
shutil.copytree(path, root / name)
|
||||
else:
|
||||
shutil.copy2(path, root / name)
|
||||
# Synthetic immutable product records keep these transport-only cases
|
||||
# independent from the current official release and its actual registry.
|
||||
for version in ['3.23.0.20261003', '3.25.0.20261009.2', '3.25.0.20261009.3']:
|
||||
components = [{'key': key, 'image': 'registry.cn-shanghai.aliyuncs.com/'+image, 'tag': version, 'digest': 'sha256:'+'a'*64}
|
||||
for key,image in [('server-main','comfy-ai/comfy-server'),('web','comfy-ai/one-ai'),('ws-gateway','easyaigc/wsgateway'),('model-conversion-worker','easyaigc/model-conversion-worker')]]
|
||||
(root / 'system-update/releases' / (version+'.json')).write_text(json.dumps({'schemaVersion':1,'channel':'versioned','productVersion':version,'platform':'linux/amd64','components':components}))
|
||||
(root / 'scripts/install-infrastructure-update-task.sh').write_text('#!/bin/sh\nexit 0\n')
|
||||
(root / 'scripts/Install-InfrastructureUpdateTask.ps1').write_text('# Scheduler transport is verified separately.\n')
|
||||
(root / 'data').mkdir()
|
||||
(root / '.env').write_text('VERSION=3.23.0.20261003\nMODEL_CONVERSION_WORKER_VERSION=3.23.0.20261003\nSYSTEM_UPDATE_MODE=legacy_watchtower\nNUXT_PUBLIC_BASE_APIURL=http://127.0.0.1:3001\nCONFIG_JWT_SECRET=existing-jwt-secret-that-is-long-enough\nWS_AUTH_WS_TICKET_SECRET=existing-ticket-secret-that-is-long-enough\n')
|
||||
(root / '.env').write_text('UPDATE_MANIFEST_SOURCE=local\nVERSION=3.23.0.20261003\nMODEL_CONVERSION_WORKER_VERSION=3.23.0.20261003\nSYSTEM_UPDATE_MODE=legacy_watchtower\nNUXT_PUBLIC_BASE_APIURL=http://127.0.0.1:3001\nCONFIG_JWT_SECRET=existing-jwt-secret-that-is-long-enough\nWS_AUTH_WS_TICKET_SECRET=existing-ticket-secret-that-is-long-enough\n')
|
||||
(root / '.gitignore').write_text('.env\n*.log\n')
|
||||
commands = base / 'commands.jsonl'
|
||||
commands.touch()
|
||||
@@ -48,7 +54,18 @@ def fixture(base):
|
||||
import json, os, sys
|
||||
args = sys.argv[1:]
|
||||
with open(os.environ['UPDATE_TEST_COMMANDS'], 'a') as output: output.write(json.dumps(args)+'\\n')
|
||||
if args[:2] == ['container', 'inspect']: sys.exit(0 if os.environ.get('UPDATE_TEST_LEGACY_REDIS') == '1' else 1)
|
||||
if args[:1] == ['--config']: args = args[2:]
|
||||
values = dict(line.split('=',1) for line in open('.env').read().splitlines() if '=' in line and not line.startswith('#'))
|
||||
expected = {'comfy-ai/comfy-server':'SERVER_STABLE_DIGEST','comfy-ai/one-ai':'WEB_STABLE_DIGEST','easyaigc/wsgateway':'WS_GATEWAY_STABLE_DIGEST'}
|
||||
if args[:2] == ['manifest', 'inspect']:
|
||||
digest = next((values.get(key,'') for repo,key in expected.items() if '/'+repo+':' in args[-1]), '')
|
||||
if os.environ.get('UPDATE_TEST_STABLE_CHANGED') == 'before': digest='sha256:'+'b'*64
|
||||
print(json.dumps({'Descriptor':{'digest':digest,'platform':{'os':'linux','architecture':'amd64'}}})); sys.exit(0)
|
||||
if args[:2] == ['image','inspect']:
|
||||
ref=args[-1]; digest=next((values.get(key,'') for repo,key in expected.items() if '/'+repo+':' in ref), '')
|
||||
if os.environ.get('UPDATE_TEST_STABLE_CHANGED') == 'after': digest='sha256:'+'b'*64
|
||||
print(json.dumps([{'RepoDigests':[ref.rsplit(':',1)[0]+'@'+digest]}]));sys.exit(0)
|
||||
if args[:2] == ['container', 'inspect']: sys.exit(0 if args[-1] == 'redis' and os.environ.get('UPDATE_TEST_LEGACY_REDIS') == '1' else 1)
|
||||
if args[:1] == ['inspect']:
|
||||
if '--format' in args:
|
||||
if args[-1] == 'redis': print('volume|legacy-redis-data'); sys.exit(0)
|
||||
@@ -60,9 +77,14 @@ if args[:3] == ['compose', 'config', '--services']: print('easyai-server\\neasya
|
||||
elif args[:4] == ['compose', 'config', '--format', 'json']:
|
||||
names = {'easyai-server':('SERVER_VERSION','comfy-ai/comfy-server'), 'easyai-web':('WEB_VERSION','comfy-ai/one-ai'), 'ws-gateway':('WS_GATEWAY_VERSION','easyaigc/wsgateway'), 'easyai-updater':('UPDATER_VERSION','easyaigc/easyai-updater'), 'model-conversion-worker':('MODEL_CONVERSION_WORKER_VERSION','easyaigc/model-conversion-worker')}
|
||||
values = dict(line.split('=',1) for line in open('.env').read().splitlines() if '=' in line and not line.startswith('#'))
|
||||
key,image = names[args[-1]]
|
||||
actual = 'registry.cn-shanghai.aliyuncs.com/'+image+':'+('latest' if os.environ.get('UPDATE_TEST_IMAGE_OVERRIDE') == '1' else values[key])
|
||||
print(json.dumps({'services': {args[-1]: {'image':actual}, 'dependency': {'image':'redis:7-alpine'}}}))
|
||||
wanted = [args[-1]] if args[-1] in names else list(names)
|
||||
services = {}
|
||||
for name in wanted:
|
||||
key,image = names[name]
|
||||
actual = 'registry.cn-shanghai.aliyuncs.com/'+image+':'+('local-override' if os.environ.get('UPDATE_TEST_IMAGE_OVERRIDE') == '1' else values.get(key, 'latest'))
|
||||
services[name] = {'image':actual}
|
||||
services['dependency'] = {'image':'redis:7-alpine'}
|
||||
print(json.dumps({'services': services,'x-easyai-release':{name:values.get(key,'') for name,key in [('easyai-server','SERVER_STABLE_DIGEST'),('easyai-web','WEB_STABLE_DIGEST'),('ws-gateway','WS_GATEWAY_STABLE_DIGEST')]}}))
|
||||
elif args[:3] == ['compose', 'ps', '-q']: print('container-' + args[-1])
|
||||
sys.exit(0)
|
||||
''')
|
||||
@@ -71,7 +93,7 @@ sys.exit(0)
|
||||
p = binaries / name
|
||||
p.write_text(code)
|
||||
p.chmod(0o755)
|
||||
env = {**os.environ, 'PATH': str(binaries) + os.pathsep + os.environ['PATH'], 'CI': 'true', 'UPDATE_NO_WAIT': '1', 'UPDATE_MODE': 'image', 'UPDATE_SERVICES': ' '.join(selected), 'UPDATE_TEST_COMMANDS': str(commands), 'UPDATE_TEST_LEGACY_REDIS': '1'}
|
||||
env = {**{k:v for k,v in os.environ.items() if not k.startswith(('UPDATE_', 'DEPLOY_'))}, 'PATH': str(binaries) + os.pathsep + os.environ['PATH'], 'CI': 'true', 'UPDATE_NO_WAIT': '1', 'UPDATE_MODE': 'image', 'UPDATE_SERVICES': ' '.join(selected), 'UPDATE_TEST_COMMANDS': str(commands), 'UPDATE_TEST_LEGACY_REDIS': '1'}
|
||||
return root, commands, env
|
||||
|
||||
|
||||
@@ -105,6 +127,14 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar
|
||||
result = run(command, root, env)
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
assert_selected(root, log, result.stdout)
|
||||
for phase in ('before','after'):
|
||||
log.write_text('')
|
||||
result = run(command, root, {**env, 'UPDATE_TEST_STABLE_CHANGED':phase})
|
||||
assert result.returncode != 0, result.stdout + result.stderr
|
||||
assert not any(c[:2] == ['compose','up'] for c in calls(log))
|
||||
if phase == 'before': assert not any(c[:2] == ['compose','pull'] for c in calls(log))
|
||||
else: assert ['compose','pull',*selected] in calls(log)
|
||||
log.write_text('')
|
||||
original = (root / '.env').read_bytes()
|
||||
result = run(command, root, env)
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
@@ -114,9 +144,10 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar
|
||||
(root / '.env').write_text((root / '.env').read_text().replace('VERSION=3.23.0.20261003', 'VERSION=latest'))
|
||||
rejected_env = (root / '.env').read_bytes()
|
||||
result = run(command, root, env)
|
||||
assert result.returncode != 0 and '固定版本' in result.stdout + result.stderr
|
||||
assert rejected_env == (root / '.env').read_bytes()
|
||||
assert not any(c[:2] in [['compose','pull'],['compose','up']] for c in calls(log))
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
assert_selected(root, log, result.stdout)
|
||||
migrated = dict(line.split('=',1) for line in (root / '.env').read_text().splitlines() if '=' in line and not line.startswith('#'))
|
||||
assert migrated['SERVER_VERSION'] == 'latest' and migrated['SERVER_STABLE_DIGEST'].startswith('sha256:')
|
||||
(root / '.env').write_bytes(original)
|
||||
log.write_text('')
|
||||
result = run(command, root, {**env, 'UPDATE_TEST_IMAGE_OVERRIDE':'1'})
|
||||
@@ -131,7 +162,8 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar
|
||||
result = run(command, root, {**env, 'UPDATE_RELEASE_VERSION': '3.23.0.20261003'})
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
values = dict(line.split('=', 1) for line in (root / '.env').read_text().splitlines() if '=' in line and not line.startswith('#'))
|
||||
assert values['VERSION'] == values['MODEL_CONVERSION_WORKER_VERSION'] == '3.23.0.20261003'
|
||||
assert values['VERSION'] == '3.23.0.20261003'
|
||||
assert values['MODEL_CONVERSION_WORKER_VERSION'].startswith('3.23.0.20261003@sha256:')
|
||||
log.write_text('')
|
||||
result = run(command, root, {**env, 'UPDATE_SERVICES': 'easyai-server missing-service'})
|
||||
assert result.returncode != 0
|
||||
@@ -146,7 +178,7 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar
|
||||
result = run(command, root, {**env, 'UPDATE_SERVICES':'easyai-server', 'UPDATE_RELEASE_VERSION':'3.25.0.20261009.2'})
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
after = dict(line.split('=',1) for line in (root / '.env').read_text().splitlines() if '=' in line and not line.startswith('#'))
|
||||
assert after['SERVER_VERSION'] == '3.25.0.20261009.2'
|
||||
assert after['SERVER_VERSION'].startswith('3.25.0.20261009.2@sha256:')
|
||||
assert after['VERSION'] == before['VERSION'] and after['WEB_VERSION'] == before['WEB_VERSION']
|
||||
assert after['MODEL_CONVERSION_WORKER_VERSION'] == before['MODEL_CONVERSION_WORKER_VERSION']
|
||||
assert ['compose','up','-d','--no-deps','easyai-server'] in calls(log)
|
||||
@@ -155,7 +187,7 @@ with tempfile.TemporaryDirectory(prefix='easyai-conversion-update-') as temporar
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
core_values = dict(line.split('=', 1) for line in (root / '.env').read_text().splitlines() if '=' in line and not line.startswith('#'))
|
||||
assert core_values['VERSION'] == '3.25.0.20261009.3'
|
||||
assert core_values['MODEL_CONVERSION_WORKER_VERSION'] == values['MODEL_CONVERSION_WORKER_VERSION']
|
||||
assert core_values['MODEL_CONVERSION_WORKER_VERSION'] == after['MODEL_CONVERSION_WORKER_VERSION']
|
||||
assert ['compose', 'pull', 'easyai-server', 'easyai-web'] in calls(log)
|
||||
assert ['compose', 'up', '-d', '--no-deps', 'easyai-server', 'easyai-web'] in calls(log)
|
||||
assert not any('model-conversion-worker' in call for call in calls(log) if call[:2] in [['compose', 'pull'], ['compose', 'up']])
|
||||
|
||||
Reference in New Issue
Block a user