← back to Dw Activation Debug TK11314

scripts/deploy-tk11314.py

107 lines

"""Apply Steve-approved TK-11314 files under the existing worker locks.
Task-specific allowlist; preserve unrelated changes, backup bytes, keep locks
until the canary and independent verification have finished.
"""
from pathlib import Path
import datetime
import hashlib
import json
import os
import subprocess

ROOT = Path('/Users/macstudio3/Projects')
EVIDENCE = ROOT / 'dw-activation-debug-TK11314/verification/rollout'
LOCKS = [ROOT / 'dw-rotation-activator/.drain.lock', ROOT / 'dw-five-field-step0/.drain.lock']
SPECS = [
    ('Designer-Wallcoverings', 'dw-validator-debug-TK11314', '8cb713c5^', '8cb713c5', [
        'shopify/scripts/lib/validate-before-activate.js', 'shopify/scripts/lib/validate-before-activate.test.js']),
    ('dw-rotation-activator', 'dw-activation-debug-TK11314', '37bb28a', '8a7469e', [
        '.gitignore', 'rotate-activate.js', 'lib/settlement-gate.js', 'lib/live-product-fields.js',
        'lib/vision-http.js', 'lib/readiness-repairs.js', 'lib/activation-result.js',
        'test/activation-result.test.js', 'test/live-product-fields.test.js',
        'test/readiness-repairs.test.js', 'test/vision-http.test.js']),
    ('dw-five-field-step0', 'dw-repair-debug-TK11314', 'ff27b09^', 'ff27b09', [
        '.gitignore', 'bulk-fivefield-exec.py', 'drain.sh', 'worklist_progress.py',
        'test_worklist_progress.py', 'test_executor_progress.py']),
]


def blob(repo, revision, relative):
    result = subprocess.run(['git', '-C', str(repo), 'show', f'{revision}:{relative}'], capture_output=True)
    return result.stdout if result.returncode == 0 else None


def main():
    EVIDENCE.mkdir(parents=True, exist_ok=True)
    manifest_path = EVIDENCE / 'deployment-manifest.json'
    if manifest_path.exists():
        raise RuntimeError('Deployment manifest already exists; inspect before any retry')
    prepared = []
    for target_name, source_name, base, revision, relatives in SPECS:
        target_repo, source_repo = ROOT / target_name, ROOT / source_name
        for relative in relatives:
            target = target_repo / relative
            if target.is_symlink():
                raise RuntimeError(f'Refusing unexpected symlink: {target}')
            previous = blob(target_repo, base, relative)
            current = target.read_bytes() if target.exists() else None
            if current != previous:
                raise RuntimeError(f'Concurrent source drift: {target}')
            reviewed = blob(source_repo, revision, relative)
            if reviewed is None or (source_repo / relative).read_bytes() != reviewed:
                raise RuntimeError(f'Reviewed source drift: {source_repo / relative}')
            prepared.append((target_name, relative, target, previous, reviewed,
                             (source_repo / relative).stat().st_mode & 0o777))
    acquired = []
    try:
        for lock in LOCKS:
            lock.mkdir()
            acquired.append(lock)
        for lock in acquired:
            (lock / 'TK-11314-owner.json').write_text(json.dumps({
                'ticket': 'TK-11314', 'purpose': 'approved coordinated rollout and canary',
                'created_at': datetime.datetime.now(datetime.timezone.utc).isoformat()}))
    except Exception:
        for lock in reversed(acquired):
            owner = lock / 'TK-11314-owner.json'
            if owner.exists(): owner.unlink()
            lock.rmdir()
        raise
    manifest = {'ticket': 'TK-11314', 'approved_by': 'Steve: go do it',
                'started_at': datetime.datetime.now(datetime.timezone.utc).isoformat(),
                'status': 'locked', 'locks': [str(p) for p in acquired], 'files': []}
    for name, relative, target, previous, reviewed, mode in prepared:
        backup = EVIDENCE / 'before' / name / relative
        if previous is not None:
            backup.parent.mkdir(parents=True, exist_ok=True)
            backup.write_bytes(previous)
        manifest['files'].append({'target': str(target), 'existed': previous is not None,
            'backup': str(backup) if previous is not None else None,
            'before_sha256': hashlib.sha256(previous).hexdigest() if previous is not None else None,
            'after_sha256': hashlib.sha256(reviewed).hexdigest(), 'mode': mode})
    manifest_path.write_text(json.dumps(manifest, indent=2) + '\n')
    try:
        for name, relative, target, previous, reviewed, mode in prepared:
            # Recheck after locks; do not overwrite a concurrent editor.
            if (target.read_bytes() if target.exists() else None) != previous:
                raise RuntimeError(f'Concurrent edit after lock: {target}')
            target.parent.mkdir(parents=True, exist_ok=True)
            staged = target.with_name(target.name + '.TK11314-stage')
            staged.write_bytes(reviewed)
            staged.chmod(mode)
            os.replace(staged, target)
        manifest['status'] = 'applied-awaiting-canary'
        manifest['applied_at'] = datetime.datetime.now(datetime.timezone.utc).isoformat()
    except Exception as error:
        manifest['status'] = 'partial-apply-locks-retained'
        manifest['error'] = str(error)
        raise
    finally:
        manifest_path.write_text(json.dumps(manifest, indent=2) + '\n')
    print(json.dumps({'status': manifest['status'], 'files': len(prepared),
                      'locks_retained': len(acquired), 'manifest': str(manifest_path)}))


if __name__ == '__main__':
    main()