#!/usr/bin/env python3 """Validate the version and checked-in artifacts for a LinkLog release.""" import json import re import sys import zipfile from pathlib import Path ROOT = Path(__file__).resolve().parents[2] MANIFEST_PATH = ROOT / 'webextension' / 'manifest.json' FRONTEND_VERSION_PATH = ROOT / 'frontend' / 'version.json' SETTINGS_PATH = ROOT / 'backend' / 'app' / 'core' / 'config.py' UPDATES_PATH = ROOT / 'webextension' / 'updates.json' SIGNED_DIR = ROOT / 'XPI' / 'signed' def fail(message: str) -> None: raise SystemExit(f'release validation failed: {message}') def main() -> None: manifest = json.loads(MANIFEST_PATH.read_text()) extension_version = manifest.get('version') if not isinstance(extension_version, str) or not re.fullmatch(r'\d+\.\d+\.\d+', extension_version): fail('webextension/manifest.json has no valid three-part version') frontend_version = json.loads(FRONTEND_VERSION_PATH.read_text()).get('version') if frontend_version != extension_version: fail(f'frontend version {frontend_version} does not match extension version {extension_version}') settings = SETTINGS_PATH.read_text() match = re.search(r"version: str = os\.getenv\('LINKLOG_VERSION', '([^']+)'\)", settings) if not match: fail('backend version default could not be found') backend_version = match.group(1) if backend_version != extension_version: fail(f'backend version {backend_version} does not match extension version {extension_version}') xpi_path = SIGNED_DIR / f'LinkLog-{extension_version}.xpi' if not xpi_path.is_file(): fail(f'missing manually signed artifact: {xpi_path.relative_to(ROOT)}') with zipfile.ZipFile(xpi_path) as archive: try: packaged_manifest = json.loads(archive.read('manifest.json')) except KeyError: fail('signed XPI does not contain manifest.json') if packaged_manifest.get('version') != extension_version: fail('signed XPI manifest version does not match webextension/manifest.json') if archive.testzip() is not None: fail('signed XPI contains a corrupt member') updates = json.loads(UPDATES_PATH.read_text()) addon_id = manifest['browser_specific_settings']['gecko']['id'] update_entries = updates.get('addons', {}).get(addon_id, {}).get('updates', []) if not any(entry.get('version') == extension_version for entry in update_entries): fail(f'webextension/updates.json has no update entry for {extension_version}') print(f'validated LinkLog release {frontend_version}') print(f'xpi={xpi_path.relative_to(ROOT)}') if __name__ == '__main__': main()