Files
archy/scripts/prepare-target-release.py
T

60 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Stage one target-bound pending manifest against shared immutable app artifacts.
No signing, target mutation, publication, or release-feed changes. Target names and
access information stay in the operator's private mapping, never in public assets.
"""
import argparse
import json
import os
from pathlib import Path
import subprocess
from release_payload import check_source, digest, os_component, regular
def prepare(root, version, plan, base_url, release_date=None):
check_source(root, version)
component = os_component(plan, version, base_url, content_addressed=True)
raw = regular(plan)
if digest(raw) != component['sha256']:
raise ValueError('OS plan changed during preparation')
artifacts = root / 'releases' / ('v' + version)
backend = artifacts / 'archipelago'
frontend = artifacts / ('archipelago-frontend-' + version + '.tar.gz')
if not backend.is_file() or not frontend.is_file():
raise ValueError('shared backend/frontend artifacts must already be built')
pending = root / 'releases/pending' / ('v' + version) / 'targets' / component['sha256']
pending.mkdir(parents=True, mode=0o700, exist_ok=False)
asset = artifacts / ('os-update-plan-' + component['sha256'] + '.json')
# An existing identical content-addressed plan is reusable; changed bytes or
# symlinks are refused, and existing pending manifests are never overwritten.
if os.path.lexists(asset):
if regular(asset) != raw:
raise ValueError('existing OS-plan asset differs')
else:
with os.fdopen(os.open(asset, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o644), 'wb') as stream:
stream.write(raw)
stream.flush()
os.fsync(stream.fileno())
manifest = pending / 'manifest.json'
command = ['bash', str(root / 'scripts/create-release-manifest.sh'), '--version', version,
'--backend', str(backend), '--frontend', str(frontend), '--os-plan', str(asset),
'--target-specific', '--base-url', base_url, '--output', str(manifest)]
if release_date:
command.extend(['--date', release_date])
subprocess.run(command, cwd=root, check=True)
subprocess.run(['bash', str(root / 'scripts/check-release-manifest.sh'), str(manifest)], cwd=root, check=True)
print(json.dumps({'pending_manifest': str(manifest), 'os_plan_sha256': component['sha256'],
'shared_artifacts': True, 'signed': False, 'published': False}))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--version', required=True)
parser.add_argument('--os-plan', type=Path, required=True)
parser.add_argument('--base-url', default='https://source.archipelago-foundation.org/lfg2025/archy/releases/download')
parser.add_argument('--date')
args = parser.parse_args()
prepare(Path(__file__).resolve().parents[1], args.version, args.os_plan.absolute(), args.base_url, args.date)