fix(deploy): 修复组件版本解析和安装升级验收流程
Test deployment public URL (Linux) / test-linux (push) Waiting to run
Test start.ps1 (Windows) / test-windows (push) Waiting to run

This commit is contained in:
2026-10-18 00:18:00 +08:00
parent 895d92317e
commit b99eb849b4
30 changed files with 1579 additions and 270 deletions
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Adopt a real non-Git directory and resolve the resulting Compose with Docker CLI."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import yaml
source = Path(__file__).resolve().parent.parent
def run(arguments, root, check=True):
return subprocess.run(arguments, cwd=root, capture_output=True, text=True, check=check, timeout=40)
with tempfile.TemporaryDirectory(prefix='easyai-package-adoption-') as temporary:
base = Path(temporary)
template = base / 'tool'
template.mkdir()
for name in ['docker-compose.yml', '.gitignore']:
shutil.copy2(source / name, template / name)
for sample in source.glob('.env*.sample'):
shutil.copy2(sample, template / sample.name)
for command in [['git','init','-q'], ['git','config','user.name','Deployment fixture'], ['git','config','user.email','fixture@example.invalid'], ['git','add','.'], ['git','commit','-qm','test: 部署模板'], ['git','remote','add','origin','https://example.invalid/deployment.git']]:
run(command, template)
for kind in ('compatible', 'conflicting-port'):
project = base / kind
project.mkdir()
for sample in source.glob('.env*.sample'):
shutil.copy2(sample, project / sample.name.removesuffix('.sample'))
private = (project / '.env').read_text() + '\nEXISTING_PRIVATE_SETTING=must-stay-private\n'
(project / '.env').write_text(private)
original = yaml.safe_load((source / 'docker-compose.yml').read_text())
original['name'] = 'original-customer-project'
original['services']['easyai-server']['environment'].append('EXISTING_OPTION=keep')
if kind == 'conflicting-port':
original['services']['easyai-web']['ports'] = ['127.0.0.1:39111:3010']
(project / 'docker-compose.yml').write_text(yaml.safe_dump(original, sort_keys=False))
marker = project / 'data/upload/existing-file.txt'
marker.parent.mkdir(parents=True)
marker.write_text('existing upload must remain')
original_compose = (project / 'docker-compose.yml').read_bytes()
result = run(['python3', str(source / 'scripts/adopt-deployment-directory.py'), str(project), str(template)], project, check=False)
assert 'must-stay-private' not in result.stdout + result.stderr
assert marker.read_text() == 'existing upload must remain'
if kind == 'compatible':
assert result.returncode == 0, result.stdout + result.stderr
assert (project / '.env').read_text().startswith(private)
assert (project / '.git').is_dir()
resolved = json.loads(run(['docker','compose','config','--format','json'], project).stdout)
assert resolved['name'] == 'original-customer-project'
assert resolved['services']['easyai-server']['environment']['EXISTING_OPTION'] == 'keep'
assert len(resolved['services']['easyai-web']['ports']) == 1
backups = list((project / 'data/deployment-backups').glob('package-*'))
assert len(backups) == 1
assert (backups[0] / 'files/.env').read_text() == private
assert (backups[0] / 'files/docker-compose.yml').read_bytes() == original_compose
assert os.stat(backups[0]).st_mode & 0o777 == 0o700
assert not run(['git','status','--porcelain'], project).stdout
else:
assert result.returncode != 0 and '端口' in result.stdout + result.stderr
assert not (project / '.git').exists()
assert (project / '.env').read_text() == private
assert (project / 'docker-compose.yml').read_bytes() == original_compose
print(kind + ': data/config preserved and unsafe merge rejected before mutation')