60 lines
3.0 KiB
Python
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)
|