From 6331ebe746ca27a376c0d1d1e8e7f3fb63931bf0 Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Thu, 24 Jul 2025 14:13:59 -0400 Subject: [PATCH] feat: Add binary UUID to basic info --- src/launchpad/api/update_api_models.py | 1 + src/launchpad/artifacts/apple/zipped_xcarchive.py | 13 ++++++++++++- src/launchpad/service.py | 5 ++--- src/launchpad/size/analyzers/apple.py | 9 +++++---- src/launchpad/size/models/apple.py | 1 + tests/unit/test_apple_basic_info.py | 1 + 6 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/launchpad/api/update_api_models.py b/src/launchpad/api/update_api_models.py index d86629c8..46b0f100 100644 --- a/src/launchpad/api/update_api_models.py +++ b/src/launchpad/api/update_api_models.py @@ -9,6 +9,7 @@ class AppleAppInfo(BaseModel): profile_name: Optional[str] = None is_code_signature_valid: Optional[bool] = None code_signature_errors: Optional[List[str]] = None + main_binary_uuid: Optional[str] = None # TODO: add "date_built" field once exposed in 'AppleAppInfo' diff --git a/src/launchpad/artifacts/apple/zipped_xcarchive.py b/src/launchpad/artifacts/apple/zipped_xcarchive.py index c0f9a03f..6e23760c 100644 --- a/src/launchpad/artifacts/apple/zipped_xcarchive.py +++ b/src/launchpad/artifacts/apple/zipped_xcarchive.py @@ -160,6 +160,10 @@ def get_app_bundle_path(self) -> Path: raise FileNotFoundError(f"No .app bundle found in {self._extract_dir}") + def get_main_binary_uuid(self) -> str | None: + main_binary_path = self._get_main_binary_path() + return self._extract_binary_uuid(main_binary_path) + def get_all_binary_paths(self) -> List[BinaryInfo]: """Find all binaries in the app bundle and their corresponding dSYM files. @@ -176,7 +180,7 @@ def get_all_binary_paths(self) -> List[BinaryInfo]: main_executable = self.get_plist().get("CFBundleExecutable") if main_executable is None: raise RuntimeError("CFBundleExecutable not found in Info.plist") - main_binary_path = Path(os.path.join(str(app_bundle_path), main_executable)) + main_binary_path = self._get_main_binary_path() # Find corresponding dSYM for main executable main_uuid = self._extract_binary_uuid(main_binary_path) @@ -284,6 +288,13 @@ def get_asset_catalog_details(self, relative_path: Path) -> List[AssetCatalogEle logger.error(f"Failed to get asset catalog details for {relative_path}: {e}") return [] + def _get_main_binary_path(self) -> Path: + app_bundle_path = self.get_app_bundle_path() + main_executable = self.get_plist().get("CFBundleExecutable") + if main_executable is None: + raise RuntimeError("CFBundleExecutable not found in Info.plist") + return Path(os.path.join(str(app_bundle_path), main_executable)) + def _parse_asset_element(self, item: dict[str, Any], parent_path: Path) -> AssetCatalogElement: """Parse a dictionary item into an AssetCatalogElement.""" name = item.get("name", "") diff --git a/src/launchpad/service.py b/src/launchpad/service.py index 6248f30f..3baa7e87 100644 --- a/src/launchpad/service.py +++ b/src/launchpad/service.py @@ -14,9 +14,7 @@ from arroyo.backends.kafka import KafkaPayload from arroyo.processing.processor import StreamProcessor -from sentry_kafka_schemas.schema_types.preprod_artifact_events_v1 import ( - PreprodArtifactEvents, -) +from sentry_kafka_schemas.schema_types.preprod_artifact_events_v1 import PreprodArtifactEvents from launchpad.api.update_api_models import AppleAppInfo as AppleAppInfoModel from launchpad.api.update_api_models import UpdateData @@ -437,6 +435,7 @@ def _get_artifact_type(artifact: Artifact) -> ArtifactType: profile_name=app_info.profile_name, is_code_signature_valid=app_info.is_code_signature_valid, code_signature_errors=app_info.code_signature_errors, + main_binary_uuid=app_info.main_binary_uuid, ) # TODO: add "date_built" and custom android fields diff --git a/src/launchpad/size/analyzers/apple.py b/src/launchpad/size/analyzers/apple.py index 0cba5f5c..d2b975c0 100644 --- a/src/launchpad/size/analyzers/apple.py +++ b/src/launchpad/size/analyzers/apple.py @@ -18,14 +18,14 @@ from launchpad.size.insights.apple.image_optimization import ImageOptimizationInsight from launchpad.size.insights.apple.localized_strings import LocalizedStringsInsight from launchpad.size.insights.apple.loose_images import LooseImagesInsight -from launchpad.size.insights.apple.main_binary_export_metadata import MainBinaryExportMetadataInsight +from launchpad.size.insights.apple.main_binary_export_metadata import ( + MainBinaryExportMetadataInsight, +) from launchpad.size.insights.apple.small_files import SmallFilesInsight from launchpad.size.insights.apple.strip_symbols import StripSymbolsInsight from launchpad.size.insights.apple.unnecessary_files import UnnecessaryFilesInsight from launchpad.size.insights.common.duplicate_files import DuplicateFilesInsight -from launchpad.size.insights.common.hermes_debug_info import ( - HermesDebugInfoInsight, -) +from launchpad.size.insights.common.hermes_debug_info import HermesDebugInfoInsight from launchpad.size.insights.common.large_audios import LargeAudioFileInsight from launchpad.size.insights.common.large_images import LargeImageFileInsight from launchpad.size.insights.common.large_videos import LargeVideoFileInsight @@ -243,6 +243,7 @@ def _extract_app_info(self, xcarchive: ZippedXCArchive) -> AppleAppInfo: profile_name=profile_name, is_code_signature_valid=is_code_signature_valid, code_signature_errors=code_signature_errors, + main_binary_uuid=xcarchive.get_main_binary_uuid(), ) def _get_profile_type(self, profile_data: dict[str, Any]) -> Tuple[str, str]: diff --git a/src/launchpad/size/models/apple.py b/src/launchpad/size/models/apple.py index e3a0bdb2..305d1764 100644 --- a/src/launchpad/size/models/apple.py +++ b/src/launchpad/size/models/apple.py @@ -157,6 +157,7 @@ class AppleAppInfo(BaseAppInfo): code_signature_errors: List[str] = Field( default_factory=list, description="List of code signature validation errors" ) + main_binary_uuid: str | None = Field(None, description="UUID of the main binary") class MachOBinaryAnalysis(BaseBinaryAnalysis): diff --git a/tests/unit/test_apple_basic_info.py b/tests/unit/test_apple_basic_info.py index 9859ce1a..1127ee6c 100644 --- a/tests/unit/test_apple_basic_info.py +++ b/tests/unit/test_apple_basic_info.py @@ -25,3 +25,4 @@ def test_basic_info(self) -> None: assert basic_info.codesigning_type == "development" assert basic_info.is_code_signature_valid is True assert basic_info.code_signature_errors == [] + assert basic_info.main_binary_uuid == "BEB3C0D6-2518-343D-BB6F-FF5581C544E8"