#!/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')