fix(deploy): 保留已有覆盖配置并支持重复迁移

This commit is contained in:
easyai02
2026-10-09 03:16:51 +08:00
parent 94f9313e24
commit f01d36d2b8
2 changed files with 43 additions and 4 deletions
+27 -4
View File
@@ -150,6 +150,29 @@ if token_key in old_monitor and token_key not in monitor and token_key in new_mo
monitor[token_key] = new_monitor[token_key] monitor[token_key] = new_monitor[token_key]
merged = merge(base, local, remote) merged = merge(base, local, remote)
override = delta(remote, merged) override = delta(remote, merged)
config_dir = root / 'data/deployment-config'
override_path = config_dir / 'legacy-compose.json'
def apply_override(original, additional):
if isinstance(original, dict) and isinstance(additional, dict):
result = copy.deepcopy(original)
for key, value in additional.items():
result[key] = apply_override(result.get(key, MISSING), value)
return result
return copy.deepcopy(additional)
# Repeated adoption must preserve already migrated settings. A newly reviewed
# main-file difference has the same precedence as the old main file; only
# explicitly restored target images are released back to the repository.
if override_path.exists():
if override_path.is_symlink() or not override_path.is_file():
sys.exit('❌ 旧配置覆盖文件类型不受支持;尚未修改配置或容器')
previous_override = canonical(json.loads(override_path.read_text()))
for service in restore_images:
previous_override.get('services', {}).get(service, {}).pop('image', None)
override = apply_override(previous_override, {} if override is MISSING else override)
merged = apply_override(remote, override)
with tempfile.NamedTemporaryFile(mode='w', dir=root, prefix='.update-compose-', suffix='.json', delete=False) as candidate: with tempfile.NamedTemporaryFile(mode='w', dir=root, prefix='.update-compose-', suffix='.json', delete=False) as candidate:
json.dump(serialize(merged), candidate) json.dump(serialize(merged), candidate)
candidate_path = Path(candidate.name) candidate_path = Path(candidate.name)
@@ -165,7 +188,9 @@ backup_root = root / 'data/deployment-backups'
backup_root.mkdir(parents=True, exist_ok=True, mode=0o700) backup_root.mkdir(parents=True, exist_ok=True, mode=0o700)
backup = Path(tempfile.mkdtemp(prefix='legacy-update-' + time.strftime('%Y%m%d-%H%M%S') + '-', dir=backup_root)) backup = Path(tempfile.mkdtemp(prefix='legacy-update-' + time.strftime('%Y%m%d-%H%M%S') + '-', dir=backup_root))
backup.chmod(0o700) backup.chmod(0o700)
for name in changed | collisions | {'.env'}: backup_files = changed | collisions | {'.env'}
if override_path.exists(): backup_files.add(str(override_path.relative_to(root)))
for name in backup_files:
destination = backup / name destination = backup / name
destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700) destination.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
shutil.copy2(root / name, destination) shutil.copy2(root / name, destination)
@@ -174,8 +199,6 @@ config_dir = root / 'data/deployment-config'
config_dir.mkdir(parents=True, exist_ok=True, mode=0o700) config_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
override_path = config_dir / 'legacy-compose.json' override_path = config_dir / 'legacy-compose.json'
if override is not MISSING and override: if override is not MISSING and override:
if override_path.exists():
sys.exit('❌ 已存在旧配置迁移结果;请检查备份后再重试,尚未修改仓库')
override_path.write_text(json.dumps(serialize(override), indent=2) + '\n') override_path.write_text(json.dumps(serialize(override), indent=2) + '\n')
override_path.chmod(0o600) override_path.chmod(0o600)
env_path = root / '.env' env_path = root / '.env'
@@ -192,7 +215,7 @@ for name in collisions:
(root / name).unlink() (root / name).unlink()
if changed: if changed:
subprocess.run(['git', 'restore', '--source=HEAD', '--staged', '--worktree', '--', 'docker-compose.yml'], check=True) subprocess.run(['git', 'restore', '--source=HEAD', '--staged', '--worktree', '--', 'docker-compose.yml'], check=True)
(backup / 'migration.json').write_text(json.dumps({'from': git('rev-parse', 'HEAD').strip(), 'to': git('rev-parse', upstream).strip(), 'preservedFields': preserved, 'restoredRepositoryImages': sorted(restore_images), 'files': sorted(changed | collisions)}, indent=2)) (backup / 'migration.json').write_text(json.dumps({'from': git('rev-parse', 'HEAD').strip(), 'to': git('rev-parse', upstream).strip(), 'preservedFields': preserved, 'restoredRepositoryImages': sorted(restore_images), 'files': sorted(backup_files)}, indent=2))
(backup / 'migration.json').chmod(0o600) (backup / 'migration.json').chmod(0o600)
print('✅ 已备份旧配置并迁移本地差异;新版部署字段由仓库继续管理') print('✅ 已备份旧配置并迁移本地差异;新版部署字段由仓库继续管理')
print(' 备份目录: ' + str(backup)) print(' 备份目录: ' + str(backup))
+16
View File
@@ -86,6 +86,22 @@ for restore in ('', 'easyai-web', 'redis', 'ws-gateway'):
# A second adoption preserves the result and creates no extra backup. # A second adoption preserves the result and creates no extra backup.
checked(['python3', str(prepare), str(root)], root, env) checked(['python3', str(prepare), str(root)], root, env)
assert len(list((root / 'data/deployment-backups').glob('legacy-update-*'))) == 1 assert len(list((root / 'data/deployment-backups').glob('legacy-update-*'))) == 1
# A later main-file customization is adopted without dropping the
# existing override or changing an unrelated dependency setting.
main_path = root / 'docker-compose.yml'
main_path.write_text(main_path.read_text().replace('LOCAL_SETTING=original', 'LOCAL_SETTING=second-custom'))
override_file = root / 'data/deployment-config/legacy-compose.json'
previous_override = override_file.read_bytes()
checked(['python3', str(prepare), str(root)], root, env)
config = json.loads(checked(['docker', 'compose', '--env-file', '.env', 'config', '--format', 'json'], root))
assert config['services']['easyai-server']['environment']['LOCAL_SETTING'] == 'second-custom'
assert config['services']['redis']['image'] == 'redis:6.2'
assert config['services']['easyai-web']['image'] == ('repository-web:latest' if restore else pin)
backups = list((root / 'data/deployment-backups').glob('legacy-update-*'))
assert len(backups) == 2
newest = max(backups, key=lambda p: p.stat().st_mtime)
assert (newest / 'data/deployment-config/legacy-compose.json').read_bytes() == previous_override
assert (newest / 'data/deployment-config/legacy-compose.json').stat().st_mode & 0o077 == 0
for mode, adopt in (('image', '1'), ('full', '0')): for mode, adopt in (('image', '1'), ('full', '0')):
env = {**os.environ, 'UPDATE_MODE': mode, 'UPDATE_ADOPT_LEGACY_CONFIG': adopt, 'UPDATE_RESTORE_REPOSITORY_IMAGES': 'easyai-web'} env = {**os.environ, 'UPDATE_MODE': mode, 'UPDATE_ADOPT_LEGACY_CONFIG': adopt, 'UPDATE_RESTORE_REPOSITORY_IMAGES': 'easyai-web'}