67 lines
4.7 KiB
Python
67 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Cross-shell release behavior: stable defaults, independent versions, legacy compatibility."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
|
|
root = Path(__file__).resolve().parent.parent
|
|
cases = [
|
|
('new install', 'VERSION=latest\n', {}, True),
|
|
('legacy latest', 'VERSION=latest\nMODEL_CONVERSION_WORKER_VERSION=latest\n', {}, True),
|
|
('watchtower aliases', 'VERSION=latest\nSYSTEM_UPDATE_MODE=legacy_watchtower\n', {}, True),
|
|
('watchtower variant', 'VERSION=latest-no-ssr\nSYSTEM_UPDATE_MODE=legacy_watchtower\n', {}, True),
|
|
('legacy pinned', 'VERSION=3.29.0.20261014.2\n', {}, True),
|
|
('recorded product', 'VERSION=latest\n', {'UPDATE_RELEASE_VERSION': '3.29.0.20261014.2'}, True),
|
|
('selected server', 'VERSION=latest\n', {'UPDATE_SERVICES': 'easyai-server', 'UPDATE_SERVER_VERSION': '3.29.0.20261014.2'}, True),
|
|
('web variant', 'VERSION=latest-no-ssr\n', {}, True),
|
|
('updater independent semver', 'VERSION=latest\n', {'UPDATE_SERVICES': 'easyai-updater', 'UPDATE_UPDATER_VERSION': '1.1.0'}, True),
|
|
('unknown product', 'VERSION=latest\n', {'UPDATE_RELEASE_VERSION': '9.9.9.20990101'}, False),
|
|
('invalid product path', 'VERSION=latest\n', {'UPDATE_RELEASE_VERSION': '../latest'}, False),
|
|
('invalid scope', 'VERSION=latest\n', {'UPDATE_SERVICES': 'easyai-server', 'UPDATE_WEB_VERSION': '3.29.0.20261014.2'}, False),
|
|
('floating component rejected', 'VERSION=latest\n', {'UPDATE_WEB_VERSION': 'latest'}, False),
|
|
]
|
|
with tempfile.TemporaryDirectory(prefix='easyai-release-resolution-') as temporary:
|
|
path = Path(temporary) / '.env'
|
|
for name, content, options, succeeds in cases:
|
|
path.write_text(content + 'UPDATE_MANIFEST_SOURCE=local\nCONFIG_JWT_SECRET=private-fixture-must-not-be-printed\n')
|
|
before = path.read_bytes()
|
|
env = {k: v for k, v in os.environ.items() if not k.startswith(('UPDATE_', 'DEPLOY_'))}
|
|
env.update(options)
|
|
bash = ['python3', str(root / 'scripts/resolve-deployment-release.py'), '--env-file', str(path), '--format', 'json']
|
|
ps = ['pwsh', '-NoProfile', '-NonInteractive', '-Command',
|
|
'. $args[0]; . $args[1]; @(Get-ApplicationVersionPlan -Path $args[2]) | ConvertTo-Json -Compress',
|
|
str(root / 'scripts/Initialize-SecurityEnv.ps1'), str(root / 'scripts/Initialize-ApplicationVersions.ps1'), str(path)]
|
|
# -Command positional arguments are parsed as PowerShell expressions;
|
|
# use a saved script to preserve paths with spaces on both shells.
|
|
script = Path(temporary) / 'plan.ps1'
|
|
script.write_text('param($Security,$Versions,$Path)\n$ErrorActionPreference="Stop"\n. $Security\n. $Versions\n@(Get-ApplicationVersionPlan -Path $Path) | ConvertTo-Json -Compress\n')
|
|
ps = ['pwsh', '-NoProfile', '-NonInteractive', '-File', str(script),
|
|
str(root / 'scripts/Initialize-SecurityEnv.ps1'), str(root / 'scripts/Initialize-ApplicationVersions.ps1'), str(path)]
|
|
plans = []
|
|
for command in (bash, ps):
|
|
result = subprocess.run(command, env=env, capture_output=True, text=True, errors='replace', timeout=30)
|
|
assert (result.returncode == 0) == succeeds, (name, result.stdout, result.stderr)
|
|
assert 'private-fixture-must-not-be-printed' not in result.stdout + result.stderr
|
|
if succeeds:
|
|
plan = json.loads(result.stdout)
|
|
plan = plan if isinstance(plan, list) else [plan]
|
|
plans.append({entry['Key']: entry['Version'] for entry in plan})
|
|
assert path.read_bytes() == before, 'planning must not mutate private settings'
|
|
if succeeds:
|
|
assert plans[0] == plans[1], (name, plans)
|
|
if name in ('new install', 'legacy latest', 'legacy pinned', 'recorded product'):
|
|
assert plans[0]['WS_GATEWAY_VERSION'].startswith('3.25.0.20261009@sha256:')
|
|
assert plans[0]['MODEL_CONVERSION_WORKER_VERSION'].startswith('3.25.0.20261009.2@sha256:')
|
|
assert plans[0]['UPDATER_VERSION'] == 'latest', 'keep the existing updater maintenance channel'
|
|
if name == 'selected server':
|
|
assert set(plans[0]) == {'SERVER_VERSION', 'SERVER_STABLE_DIGEST'}
|
|
if name.startswith('watchtower'):
|
|
assert plans[0]['SERVER_VERSION'] == plans[0]['WS_GATEWAY_VERSION'] == 'latest'
|
|
assert plans[0]['SERVER_STABLE_DIGEST'].startswith('sha256:')
|
|
assert plans[0]['WEB_VERSION'] == ('latest-no-ssr' if name.endswith('variant') else 'latest')
|
|
if name == 'web variant':
|
|
assert '-no-ssr@sha256:' in plans[0]['WEB_VERSION']
|
|
print(name + ': passed in Bash planner and PowerShell')
|