package_contract.py (25574B)
1 #!/usr/bin/env python3 2 """Structured standalone package-contract verification for the iOS capsule.""" 3 4 from __future__ import annotations 5 6 import argparse 7 import hashlib 8 import json 9 import os 10 import plistlib 11 import re 12 import subprocess 13 import sys 14 import tempfile 15 import tomllib 16 from collections.abc import Mapping 17 from pathlib import Path 18 from typing import Any 19 20 import app_dependency_graph 21 import legacy_identifiers 22 import package_privacy 23 24 MAX_CONTRACT_BYTES = 2 * 1024 * 1024 25 GIT_REVISION = re.compile(r"^[0-9a-f]{40}$") 26 SHA256 = re.compile(r"^[0-9a-f]{64}$") 27 APPLE_KIT_REMOTE = "https://github.com/radrootslabs/apple_kit.git" 28 LIB_REMOTE = "https://github.com/radrootslabs/lib" 29 SECP256K1_REMOTE = "https://github.com/21-DOT-DEV/swift-secp256k1.git" 30 SECP256K1_REVISION = "e70a10e036a55fffea31568f0af92d69b6d449cd" 31 32 33 class PackageContractError(Exception): 34 """A stable, source-free package-contract rejection.""" 35 36 37 def _read_regular(path: Path, *, maximum: int = MAX_CONTRACT_BYTES) -> bytes: 38 try: 39 if path.is_symlink() or not path.is_file(): 40 raise PackageContractError("required contract input is not a regular file") 41 size = path.stat().st_size 42 if size < 0 or size > maximum: 43 raise PackageContractError("required contract input exceeds its byte limit") 44 value = path.read_bytes() 45 except OSError as error: 46 raise PackageContractError("required contract input cannot be read") from error 47 if len(value) != size: 48 raise PackageContractError("required contract input changed while reading") 49 return value 50 51 52 def _read_text(path: Path) -> str: 53 try: 54 return _read_regular(path).decode("utf-8") 55 except UnicodeDecodeError as error: 56 raise PackageContractError("required contract input is not UTF-8") from error 57 58 59 def _read_toml(path: Path) -> dict[str, Any]: 60 try: 61 value = tomllib.loads(_read_text(path)) 62 except tomllib.TOMLDecodeError as error: 63 raise PackageContractError("required TOML contract is malformed") from error 64 if not isinstance(value, dict): 65 raise PackageContractError("required TOML contract is not an object") 66 return value 67 68 69 def _read_json(path: Path) -> dict[str, Any]: 70 try: 71 value = json.loads(_read_text(path)) 72 except json.JSONDecodeError as error: 73 raise PackageContractError("required JSON contract is malformed") from error 74 if not isinstance(value, dict): 75 raise PackageContractError("required JSON contract is not an object") 76 return value 77 78 79 def _read_plist(path: Path) -> dict[str, Any]: 80 try: 81 value = plistlib.loads(_read_regular(path)) 82 except (plistlib.InvalidFileException, ValueError, TypeError) as error: 83 raise PackageContractError("required plist contract is malformed") from error 84 if not isinstance(value, dict): 85 raise PackageContractError("required plist contract is not a dictionary") 86 return value 87 88 89 def _mapping(value: object, key: str) -> Mapping[str, Any]: 90 if not isinstance(value, Mapping): 91 raise PackageContractError(f"structured contract field is invalid: {key}") 92 return value 93 94 95 def _exact(value: object, expected: object, key: str) -> None: 96 if value != expected: 97 raise PackageContractError(f"structured contract field differs: {key}") 98 99 100 def parse_xcconfig_assignments(text: str) -> dict[str, str]: 101 assignments: dict[str, str] = {} 102 expression = re.compile(r"^([A-Z][A-Z0-9_]*)\s*=\s*(\S(?:.*\S)?)$") 103 for raw in text.splitlines(): 104 line = raw.strip() 105 if not line or line.startswith("//") or line.startswith("#"): 106 continue 107 match = expression.fullmatch(line) 108 if match is None: 109 raise PackageContractError("xcconfig contains an unsupported statement") 110 key, value = match.groups() 111 if key in assignments: 112 raise PackageContractError("xcconfig assignment is duplicated") 113 assignments[key] = value 114 return assignments 115 116 117 def parse_project_package(text: str, package_name: str) -> dict[str, str]: 118 lines = text.splitlines() 119 packages_line, packages_end = _project_package_bounds(lines) 120 start = _project_package_start(lines, packages_line, packages_end, package_name) 121 return _project_package_fields(lines[start + 1 :]) 122 123 124 def _project_package_bounds(lines: list[str]) -> tuple[int, int]: 125 start = next( 126 (index for index, line in enumerate(lines) if line == "packages:"), None 127 ) 128 if start is None: 129 raise PackageContractError("project package inventory is absent") 130 end = next( 131 ( 132 index 133 for index in range(start + 1, len(lines)) 134 if lines[index] and not lines[index].startswith((" ", "#")) 135 ), 136 len(lines), 137 ) 138 return start, end 139 140 141 def _project_package_start( 142 lines: list[str], start: int, end: int, package_name: str 143 ) -> int: 144 expected = f" {package_name}:" 145 result = next( 146 (index for index in range(start + 1, end) if lines[index] == expected), None 147 ) 148 if result is None: 149 raise PackageContractError("project package entry is absent") 150 return result 151 152 153 def _project_package_fields(lines: list[str]) -> dict[str, str]: 154 values: dict[str, str] = {} 155 for line in lines: 156 if line and not line.startswith(" "): 157 break 158 match = re.fullmatch(r" ([a-z_]+): (\S+)", line) 159 if match is None: 160 if line.strip(): 161 raise PackageContractError("project package entry is malformed") 162 continue 163 key, value = match.groups() 164 if key in values: 165 raise PackageContractError("project package field is duplicated") 166 values[key] = value 167 return values 168 169 170 def validate_resolved(document: dict[str, Any], apple_revision: str) -> None: 171 _exact(document.get("version"), 3, "package lock version") 172 pins = document.get("pins") 173 if not isinstance(pins, list) or len(pins) != 2: 174 raise PackageContractError("package lock pin inventory differs") 175 selected: dict[str, str] = {} 176 for pin in pins: 177 item = _mapping(pin, "package lock pin") 178 _exact(item.get("kind"), "remoteSourceControl", "package lock pin kind") 179 location = item.get("location") 180 state = _mapping(item.get("state"), "package lock pin state") 181 revision = state.get("revision") 182 if not isinstance(location, str) or not location.startswith("https://"): 183 raise PackageContractError("package lock location is invalid") 184 if not isinstance(revision, str) or GIT_REVISION.fullmatch(revision) is None: 185 raise PackageContractError("package lock revision is invalid") 186 if location in selected: 187 raise PackageContractError("package lock location is duplicated") 188 selected[location] = revision 189 _exact(selected.get(APPLE_KIT_REMOTE), apple_revision, "AppleKit package pin") 190 _exact( 191 selected.get(SECP256K1_REMOTE), 192 SECP256K1_REVISION, 193 "secp256k1 package pin", 194 ) 195 196 197 def _swift_package(repo_root: Path) -> dict[str, Any]: 198 with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr: 199 try: 200 result = subprocess.run( 201 [ 202 "swift", 203 "package", 204 "--package-path", 205 str(repo_root), 206 "dump-package", 207 ], 208 check=False, 209 stdout=stdout, 210 stderr=stderr, 211 timeout=60, 212 ) 213 except (OSError, subprocess.TimeoutExpired) as error: 214 raise PackageContractError( 215 "Swift package manifest cannot be evaluated" 216 ) from error 217 stdout.seek(0) 218 output = stdout.read(MAX_CONTRACT_BYTES + 1) 219 if result.returncode != 0 or len(output) > MAX_CONTRACT_BYTES: 220 raise PackageContractError("Swift package manifest evaluation failed") 221 try: 222 value = json.loads(output) 223 except (UnicodeDecodeError, json.JSONDecodeError) as error: 224 raise PackageContractError( 225 "Swift package manifest output is malformed" 226 ) from error 227 if not isinstance(value, dict): 228 raise PackageContractError("Swift package manifest output is not an object") 229 return value 230 231 232 def _apple_revision(package: dict[str, Any]) -> str: 233 dependencies = package.get("dependencies") 234 if not isinstance(dependencies, list): 235 raise PackageContractError("Swift package dependencies are absent") 236 matches: list[str] = [] 237 for dependency in dependencies: 238 candidate = _apple_dependency_revision(dependency) 239 if candidate is not None: 240 matches.append(candidate) 241 if ( 242 len(matches) != 1 243 or not isinstance(matches[0], str) 244 or GIT_REVISION.fullmatch(matches[0]) is None 245 ): 246 raise PackageContractError("AppleKit dependency is not one exact revision") 247 return matches[0] 248 249 250 def _apple_dependency_revision(dependency: object) -> object | None: 251 item = _mapping(dependency, "Swift package dependency") 252 source = item.get("sourceControl") 253 if not isinstance(source, list) or len(source) != 1: 254 return None 255 identity = _mapping(source[0], "Swift package source") 256 remote = identity.get("location") 257 requirement = identity.get("requirement") 258 remote_values = remote.get("remote") if isinstance(remote, dict) else None 259 if remote_values != [{"urlString": APPLE_KIT_REMOTE}]: 260 return None 261 if not isinstance(requirement, dict): 262 return None 263 revisions = requirement.get("revision") 264 if not isinstance(revisions, list) or len(revisions) != 1: 265 return None 266 return revisions[0] 267 268 269 def _validate_privacy(document: dict[str, Any]) -> None: 270 try: 271 package_privacy.validate_manifest(document) 272 except ValueError as error: 273 raise PackageContractError(str(error)) from error 274 275 276 def _validate_app_plist(document: dict[str, Any]) -> None: 277 for key in ( 278 "NSCameraUsageDescription", 279 "NSFaceIDUsageDescription", 280 "NSLocalNetworkUsageDescription", 281 ): 282 value = document.get(key) 283 if not isinstance(value, str) or not value.strip(): 284 raise PackageContractError(f"required plist purpose is absent: {key}") 285 try: 286 package_privacy.validate_purposes(document) 287 except ValueError as error: 288 raise PackageContractError(str(error)) from error 289 _exact( 290 document.get("NSAppTransportSecurity"), 291 {"NSAllowsLocalNetworking": True}, 292 "app transport security", 293 ) 294 for forbidden in ("NSBonjourServices", "NSPhotoLibraryUsageDescription"): 295 if forbidden in document: 296 raise PackageContractError(f"forbidden plist field is present: {forbidden}") 297 298 _exact(document.get("CFBundleDisplayName"), "Tera", "app display name") 299 _exact(document.get("CFBundleName"), "$(PRODUCT_NAME)", "app bundle name") 300 301 302 def _validate_ui_test_plist(document: dict[str, Any]) -> None: 303 required = { 304 "TERA_IOS_UI_TEST_FIXTURE_CONTROL", 305 "TERA_IOS_UI_TEST_FIXTURE_EVIDENCE", 306 "TERA_IOS_UI_TEST_NETWORK_PROFILE", 307 "TERA_IOS_UI_TEST_SOURCE_COMMIT", 308 "TERA_IOS_UI_TEST_SOURCE_TREE", 309 "TERA_IOS_UI_TEST_APP_BUILD_SHA256", 310 "TERA_IOS_UI_TEST_SIMULATOR_ID", 311 } 312 if required.difference(document): 313 raise PackageContractError("UI test plist inventory is incomplete") 314 315 316 def _verify_repository_layout(root: Path) -> None: 317 for forbidden in ("docs", ".github", ".act"): 318 path = root / forbidden 319 if path.exists() or path.is_symlink(): 320 raise PackageContractError("forbidden public repository root exists") 321 322 323 def _cargo_workspace(root: Path, *, resolved: bool = False) -> dict[str, Any]: 324 with tempfile.TemporaryFile() as stdout, tempfile.TemporaryFile() as stderr: 325 try: 326 result = subprocess.run( 327 [ 328 "cargo", 329 "metadata", 330 "--manifest-path", 331 str(root / "Cargo.toml"), 332 "--locked", 333 *([] if resolved else ["--no-deps"]), 334 "--offline", 335 "--format-version", 336 "1", 337 ], 338 check=False, 339 stdin=subprocess.DEVNULL, 340 stdout=stdout, 341 stderr=stderr, 342 timeout=60, 343 ) 344 except (OSError, subprocess.TimeoutExpired) as error: 345 raise PackageContractError("Cargo workspace cannot be evaluated") from error 346 stdout.seek(0) 347 output = stdout.read(MAX_CONTRACT_BYTES + 1) 348 if result.returncode != 0 or len(output) > MAX_CONTRACT_BYTES: 349 raise PackageContractError("Cargo workspace evaluation failed") 350 try: 351 value = json.loads(output) 352 except (UnicodeDecodeError, json.JSONDecodeError) as error: 353 raise PackageContractError("Cargo workspace output is malformed") from error 354 if not isinstance(value, dict): 355 raise PackageContractError("Cargo workspace output is not an object") 356 return value 357 358 359 def _local_cargo_path(value: object, root: Path) -> Path: 360 if not isinstance(value, str) or not value: 361 raise PackageContractError("local Cargo path is invalid") 362 path = Path(value).resolve() 363 if not path.is_relative_to(root.resolve()): 364 raise PackageContractError("local Cargo path escapes the standalone repository") 365 return path.relative_to(root.resolve()) 366 367 368 def _validate_app_workspace(document: dict[str, Any], root: Path) -> None: 369 _exact(document.get("workspace_root"), str(root), "Cargo workspace root") 370 packages = document.get("packages") 371 members = document.get("workspace_members") 372 if not isinstance(packages, list) or not packages or not isinstance(members, list): 373 raise PackageContractError("Cargo workspace members are absent") 374 identifiers = [ 375 package.get("id") for package in packages if isinstance(package, dict) 376 ] 377 if not all(isinstance(item, str) for item in identifiers + members): 378 raise PackageContractError("Cargo workspace member identity is invalid") 379 _exact(sorted(identifiers), sorted(members), "Cargo workspace member inventory") 380 for package in packages: 381 _validate_app_package(_mapping(package, "Cargo package"), root) 382 _validate_mobile_defaults(document, packages, root) 383 384 385 def _validate_mobile_defaults( 386 document: Mapping[str, Any], packages: list[Any], root: Path 387 ) -> None: 388 owned = { 389 _local_cargo_path(package.get("manifest_path"), root).parent.name: package["id"] 390 for package in packages 391 } 392 if "tera_wasm" not in owned: 393 return 394 defaults = document.get("workspace_default_members") 395 if not isinstance(defaults, list) or owned["tera_wasm"] in defaults: 396 raise PackageContractError("non-default WASM package entered mobile defaults") 397 for name in ("tera_core", "tera_ffi"): 398 if name in owned and owned[name] not in defaults: 399 raise PackageContractError("owned runtime is missing from mobile defaults") 400 401 402 def _validate_app_package(package: Mapping[str, Any], root: Path) -> None: 403 manifest = _local_cargo_path(package.get("manifest_path"), root) 404 directory = manifest.parent 405 if not directory.is_relative_to("core/crates"): 406 raise PackageContractError("application Rust package is outside its owned root") 407 dependencies = package.get("dependencies") 408 if not isinstance(dependencies, list): 409 raise PackageContractError("Cargo dependency inventory is absent") 410 for dependency in dependencies: 411 item = _mapping(dependency, "Cargo dependency") 412 if "path" in item: 413 _local_cargo_path(item["path"], root) 414 415 416 def _verify_cargo_and_source(root: Path) -> tuple[str, str]: 417 cargo = _read_toml(root / "Cargo.toml") 418 workspace = _mapping(cargo.get("workspace"), "Cargo workspace") 419 workspace_package = _mapping(workspace.get("package"), "Cargo workspace package") 420 _exact( 421 workspace_package.get("repository"), 422 "https://github.com/radrootslabs/tera", 423 "Cargo repository", 424 ) 425 consumer = _read_toml(root / "radroots.lib.source-lock.v1.toml") 426 _exact(consumer.get("repository"), LIB_REMOTE, "consumer Lib remote") 427 lib_revision = consumer.get("revision") 428 if ( 429 not isinstance(lib_revision, str) 430 or GIT_REVISION.fullmatch(lib_revision) is None 431 ): 432 raise PackageContractError("consumer Lib revision is invalid") 433 release_version = consumer.get("version") 434 _exact(release_version, "0.1.0-alpha", "consumer Lib version") 435 try: 436 app_dependency_graph.validate(_cargo_workspace(root, resolved=True), consumer) 437 except app_dependency_graph.GraphError as error: 438 raise PackageContractError(str(error)) from error 439 _verify_owned_source_lock(root, consumer) 440 return release_version, lib_revision 441 442 443 def _verify_owned_source_lock(root: Path, foundation: dict[str, Any]) -> None: 444 lock = _read_toml(root / "TeraFFI/source.lock") 445 _exact( 446 set(lock), 447 { 448 "schema", 449 "repository", 450 "source_tree", 451 "manifest_sha256", 452 "source_date_epoch", 453 "foundation", 454 }, 455 "installed source fields", 456 ) 457 _exact(lock["schema"], "tera.installed-source.v1", "installed source schema") 458 _exact( 459 lock["repository"], "https://github.com/radrootslabs/tera", "installed producer" 460 ) 461 _exact( 462 lock["foundation"], 463 {key: foundation[key] for key in ("repository", "revision", "version")}, 464 "installed foundation", 465 ) 466 if ( 467 not isinstance(lock["source_tree"], str) 468 or GIT_REVISION.fullmatch(lock["source_tree"]) is None 469 ): 470 raise PackageContractError("installed source tree is invalid") 471 _exact(lock["source_date_epoch"], 1787871027, "installed source epoch") 472 _exact( 473 lock["manifest_sha256"], 474 hashlib.sha256(_read_regular(root / "TeraFFI/provenance.json")).hexdigest(), 475 "installed manifest digest", 476 ) 477 478 479 def _verify_apple_dependencies(root: Path) -> str: 480 package = _swift_package(root) 481 _exact(package.get("name"), "tera", "Swift package name") 482 _exact(package.get("defaultLocalization"), "en", "Swift localization") 483 apple_revision = _apple_revision(package) 484 project = parse_project_package(_read_text(root / "project.yml"), "RadrootsKit") 485 if set(project) != {"url", "revision"}: 486 raise PackageContractError("project AppleKit field inventory differs") 487 _exact(project.get("url"), APPLE_KIT_REMOTE, "project AppleKit remote") 488 _exact(project.get("revision"), apple_revision, "project AppleKit revision") 489 return apple_revision 490 491 492 def _verify_apple_configuration(root: Path) -> None: 493 _validate_privacy(_read_plist(root / "Tera/Resources/PrivacyInfo.xcprivacy")) 494 _validate_app_plist(_read_plist(root / "Tera/Info.plist")) 495 _validate_ui_test_plist(_read_plist(root / "TeraUITests/Info.plist")) 496 497 base = parse_xcconfig_assignments(_read_text(root / "Tera/Config/Base.xcconfig")) 498 debug = parse_xcconfig_assignments(_read_text(root / "Tera/Config/Debug.xcconfig")) 499 if set(base) != { 500 "TERA_IOS_RUNTIME_MODE", 501 "TERA_IOS_NOSTR_RELAY_URLS", 502 "TERA_IOS_BLOSSOM_ORIGINS", 503 "TERA_IOS_KEYCHAIN_SERVICE_PREFIX", 504 }: 505 raise PackageContractError("base xcconfig field inventory differs") 506 if set(debug) != { 507 "TERA_IOS_RUNTIME_MODE", 508 "PRODUCT_BUNDLE_IDENTIFIER", 509 "TERA_IOS_NOSTR_RELAY_URLS", 510 "TERA_IOS_BLOSSOM_ORIGINS", 511 "TERA_IOS_KEYCHAIN_SERVICE_PREFIX", 512 }: 513 raise PackageContractError("debug xcconfig field inventory differs") 514 _exact( 515 base.get("TERA_IOS_NOSTR_RELAY_URLS"), 516 "wss:$(SLASH)$(SLASH)radroots.org$(SLASH)", 517 "base relay", 518 ) 519 _exact( 520 base.get("TERA_IOS_BLOSSOM_ORIGINS"), 521 "https:$(SLASH)$(SLASH)blossom.radroots.org", 522 "base Blossom origin", 523 ) 524 _exact( 525 debug.get("TERA_IOS_NOSTR_RELAY_URLS"), 526 "ws:$(SLASH)$(SLASH)127.0.0.1:21000", 527 "debug relay", 528 ) 529 _exact( 530 debug.get("TERA_IOS_BLOSSOM_ORIGINS"), 531 "http:$(SLASH)$(SLASH)127.0.0.1:21100", 532 "debug Blossom origin", 533 ) 534 _verify_installation_compatibility(root, base, debug) 535 536 537 def _verify_installation_compatibility( 538 root: Path, base: dict[str, str], debug: dict[str, str] 539 ) -> None: 540 baseline = _read_json(root / "test-fixtures/tera-compatibility.v1.json") 541 _exact( 542 baseline.get("schema"), "tera.compatibility-baseline.v1", "compatibility schema" 543 ) 544 production = parse_xcconfig_assignments(_read_text(root / "Tera/tera.xcconfig")) 545 actual = { 546 "production_bundle_identifier": production.get("PRODUCT_BUNDLE_IDENTIFIER"), 547 "debug_bundle_identifier": debug.get("PRODUCT_BUNDLE_IDENTIFIER"), 548 "production_keychain_service_prefix": base.get( 549 "TERA_IOS_KEYCHAIN_SERVICE_PREFIX" 550 ), 551 "debug_keychain_service_prefix": debug.get("TERA_IOS_KEYCHAIN_SERVICE_PREFIX"), 552 } 553 _exact(actual, baseline.get("installation"), "installed identity compatibility") 554 555 556 def _verify_package_locks(root: Path, apple_revision: str) -> None: 557 resolved_paths = ( 558 root / "Package.resolved", 559 root 560 / "Tera.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved", 561 ) 562 resolved = [_read_json(path) for path in resolved_paths] 563 for document in resolved: 564 validate_resolved(document, apple_revision) 565 if resolved[0].get("pins") != resolved[1].get("pins"): 566 raise PackageContractError("Swift and Xcode package locks disagree") 567 568 569 def _verify_persona_toolchain(root: Path) -> None: 570 verifier_project = _read_toml(root / "scripts/persona-verifier/pyproject.toml") 571 verifier_lock = _read_toml(root / "scripts/persona-verifier/uv.lock") 572 verifier_metadata = _mapping(verifier_project.get("project"), "verifier project") 573 _exact( 574 verifier_metadata.get("requires-python"), 575 "==3.14.7", 576 "verifier Python", 577 ) 578 _exact( 579 verifier_metadata.get("dependencies"), 580 ["jsonschema==4.26.0"], 581 "verifier dependencies", 582 ) 583 dependency_groups = _mapping( 584 verifier_project.get("dependency-groups"), "verifier dependency groups" 585 ) 586 _exact(dependency_groups, {"dev": ["ruff==0.12.12"]}, "verifier dev tools") 587 _exact(verifier_lock.get("requires-python"), "==3.14.7", "verifier lock Python") 588 package_rows = verifier_lock.get("package") 589 if not isinstance(package_rows, list): 590 raise PackageContractError("verifier lock package inventory is invalid") 591 locked_packages = { 592 item.get("name"): item.get("version") 593 for item in package_rows 594 if isinstance(item, dict) 595 } 596 _exact(locked_packages.get("jsonschema"), "4.26.0", "verifier jsonschema lock") 597 _exact(locked_packages.get("ruff"), "0.12.12", "verifier ruff lock") 598 599 600 def _verify_required_files(root: Path) -> None: 601 required_files = ( 602 ".swiftformat", 603 ".swiftlint.yml", 604 "scripts/maintainability_ratchet.py", 605 "scripts/local-social-fixture.py", 606 "scripts/swift-quality.sh", 607 "scripts/linux-shared-rust.sh", 608 "test-fixtures/maintainability-baseline.v1.json", 609 "test-fixtures/swiftlint-maintainability-baseline.v1.json", 610 "test-fixtures/bud11-upload-authorization-mutations.v1.json", 611 "test-fixtures/bud11-upload-authorization-mutations.v1.schema.json", 612 "test-fixtures/local-social-personas.v1.json", 613 "test-fixtures/local-social-personas.v1.schema.json", 614 "test-fixtures/local-social-persona-results.v1.schema.json", 615 "test-fixtures/local-social-persona-attempt-evidence.v1.schema.json", 616 "test-fixtures/local-social-persona-results.v2.schema.json", 617 ) 618 for relative in required_files: 619 _read_regular(root / relative) 620 for relative in ("scripts/swift-quality.sh", "scripts/linux-shared-rust.sh"): 621 if not os.access(root / relative, os.X_OK): 622 raise PackageContractError("required package command is not executable") 623 624 625 def verify(repo_root: Path) -> tuple[str, str]: 626 root = repo_root.resolve() 627 _verify_repository_layout(root) 628 try: 629 legacy_identifiers.verify(root) 630 except legacy_identifiers.LegacyIdentifierError as error: 631 raise PackageContractError(str(error)) from error 632 _validate_app_workspace(_cargo_workspace(root), root) 633 release_version, _ = _verify_cargo_and_source(root) 634 apple_revision = _verify_apple_dependencies(root) 635 _verify_apple_configuration(root) 636 _verify_package_locks(root, apple_revision) 637 _verify_persona_toolchain(root) 638 _verify_required_files(root) 639 return release_version, apple_revision 640 641 642 def main(argv: list[str] | None = None) -> int: 643 parser = argparse.ArgumentParser() 644 parser.add_argument("--repo-root", type=Path, required=True) 645 arguments = parser.parse_args(argv) 646 try: 647 version, apple_revision = verify(arguments.repo_root) 648 except PackageContractError as error: 649 print(f"package_contract: {error}", file=sys.stderr) 650 return 1 651 print(f"package contracts agree at {version}; apple_kit@{apple_revision}") 652 return 0 653 654 655 if __name__ == "__main__": 656 raise SystemExit(main())