#!/usr/bin/env python3 """Check the 0.31.2 dependency/source migration; optionally require a resolved lock. This is not a substitute for rustc, cargo metadata or runtime tests. """ from __future__ import annotations import argparse from pathlib import Path import re import tomllib ROOT = Path(__file__).resolve().parents[2] parser = argparse.ArgumentParser() parser.add_argument('--require-lock', action='store_true', help='Require the post-upgrade Cargo.lock produced by cargo update') args = parser.parse_args() manifest = tomllib.loads((ROOT / 'Cargo.toml').read_text()) assert manifest['package']['version'] == '0.31.2' assert manifest['package']['rust-version'] == '1.99' deps = manifest['dependencies'] expected_requirements = { 'argon2': '0.6', 'rand': '0.10', 'sqlx': '0.9', 'rcgen': '0.14.10', 'tower-http': '0.7.1', 'brotli': '9', 'sysinfo': '0.39', 'h3': '=2.5.20', 'tokio': '1.53.2', 'hyper': '1.12.0', } for name, version in expected_requirements.items(): entry = deps[name] found = entry if isinstance(entry, str) else entry['version'] assert found == version, f'{name} requirement changed: {found!r} != {version!r}' assert deps['h3']['package'] == 'hpx-h3' assert deps['h3']['features'] == ['quinn'] assert deps['sqlx']['default-features'] is False assert deps['sqlx']['features'] == ['runtime-tokio', 'sqlite'] assert set(deps['sysinfo']['features']) == {'network','system'} assert set(deps['rcgen']['features']) == {'crypto', 'pem', 'ring'} assert 'tls12' in deps['rustls']['features'] and 'ring' in deps['rustls']['features'] assert 'tls12' in deps['tokio-rustls']['features'] for target in ('Dockerfile', 'integrations/routeros-oci-build/Dockerfile.routeros'): assert 'FROM rust:1.99-trixie AS build' in (ROOT / target).read_text(), target files = [p for p in (ROOT / 'src').rglob('*.rs')] sources = '\n'.join(p.read_text() for p in files) for old in ('rngs::OsRng', 'rand::thread_rng()', 'SaltString::generate(', 'distributions::Alphanumeric', '.key_pair.serialize_pem()', 'hash_password(password.as_bytes(), &salt)'): assert old not in sources, f'obsolete dependency API: {old}' for new in ('SysRng.try_fill_bytes(', 'phc::PasswordHash', 'hash_password(password.as_bytes())', 'verify_password(password.as_bytes()', 'signing_key.serialize_pem()', 'rand::rng()'): assert new in sources, f'missing dependency migration: {new}' assert 'VERSION = "5.33.1"' in (ROOT / 'scripts/build/swagger_assets.py').read_text() lock = tomllib.loads((ROOT / 'Cargo.lock').read_text()) installed: dict[str,set[str]] = {} for item in lock['package']: installed.setdefault(item['name'], set()).add(item['version']) latest_required = { 'argon2': '0.6.0', 'rand': '0.10.3', 'sqlx': '0.9.0', 'rcgen': '0.14.10', 'tower-http': '0.7.1', 'brotli': '9.0.0', 'sysinfo': '0.39.6', 'hpx-h3': '2.5.20', 'hyper': '1.12.0', 'tokio': '1.53.2', } def compatible_update(package: str, installed_version: str, minimum: str) -> bool: candidate = tuple(int(x) for x in installed_version.split('.')[:3]) baseline = tuple(int(x) for x in minimum.split('.')[:3]) if package == 'hpx-h3': return candidate == baseline # Intentionally exact-pinned for the QUIC integration. if candidate[0] != baseline[0]: return False if baseline[0] == 0 and candidate[1] != baseline[1]: return False return candidate >= baseline missing = {name: version for name,version in latest_required.items() if not any(compatible_update(name, candidate, version) for candidate in installed.get(name, set()))} if missing: details = ', '.join(f'{name} {version}' for name,version in missing.items()) if args.require_lock: raise SystemExit(f'FAIL: Cargo.lock not resolved to current deps: {details}. Run cargo update first.') print(f'PENDING: lockfile still requires cargo update ({details})') else: print('PASS: pinned key packages found in post-upgrade lock; cargo --locked verification is still required') print('PASS: 0.31.2 source dependency APIs, manifest, TLS features, toolchain images and Swagger pin (static only)')