Example #1
0
    def _get_publishing_application_packages(
        self, path_patterns: Sequence[pathlib.Path]
    ) -> List[Union[Ipa, MacOsPackage]]:
        found_application_paths = list(self.find_paths(*path_patterns))
        application_packages: List[Union[Ipa, MacOsPackage]] = []
        for path in found_application_paths:
            if path.suffix == '.ipa':
                application_package: Union[Ipa, MacOsPackage] = Ipa(path)
            elif path.suffix == '.pkg':
                application_package = MacOsPackage(path)
            else:
                raise AppStoreConnectError(
                    f'Unsupported package type for App Store Connect publishing: {path}'
                )

            try:
                application_package.get_summary()
            except FileNotFoundError as fnf:
                message = f'Invalid package for App Store Connect publishing: {fnf.args[0]} not found from {path}'
                self.logger.warning(Colors.YELLOW(message))
            except (ValueError, IOError) as error:
                message = f'Unable to process package {path} for App Store Connect publishing: {error.args[0]}'
                self.logger.warning(Colors.YELLOW(message))
            else:
                application_packages.append(application_package)

        if not application_packages:
            patterns = ', '.join(f'"{pattern}"' for pattern in path_patterns)
            raise AppStoreConnectError(
                f'No application packages found for patterns {patterns}')
        return application_packages
Example #2
0
    def _detect_project_bundle_ids(self,
                                   xcode_project: pathlib.Path,
                                   target_name: Optional[str],
                                   config_name: Optional[str],
                                   include_pods: bool) -> List[str]:

        def group(bundle_ids):
            groups = defaultdict(list)
            for bundle_id in bundle_ids:
                groups['$' in bundle_id].append(bundle_id)
            return groups[True], groups[False]

        if not include_pods and xcode_project.stem == 'Pods':
            self.logger.info(f'Skip Bundle ID detection from Pod project {xcode_project}')
            return []

        detector = BundleIdDetector(xcode_project, target_name, config_name)
        detector.notify()
        try:
            detected_bundle_ids = detector.detect()
        except (ValueError, IOError) as error:
            raise XcodeProjectException(*error.args)

        env_var_bundle_ids, valid_bundle_ids = group(detected_bundle_ids)
        if env_var_bundle_ids:
            msg = f'Bundle IDs {", ".join(env_var_bundle_ids)} contain environment variables, exclude them.'
            self.logger.info(Colors.YELLOW(msg))
        self.logger.info(f'Detected Bundle IDs: {", ".join(valid_bundle_ids)}')
        return valid_bundle_ids
Example #3
0
 def log_filtered(self, resource_type: Type[R], resources: Sequence[R], constraint: str):
     count = len(resources)
     name = resource_type.plural(count)
     if count == 0:
         self.logger.info(Colors.YELLOW(f'Did not find any {name} {constraint}'))
     else:
         self.logger.info(Colors.GREEN(f'Filtered out {count} {name} {constraint}'))
Example #4
0
 def missing_profile(bundle_id) -> bool:
     try:
         bundle_ids_profiles = self.api_client.bundle_ids.list_profile_ids(bundle_id)
         return not (profile_ids & {p.id for p in bundle_ids_profiles})
     except AppStoreConnectApiError as err:
         error = f'Listing {Profile.s} for {BundleId} {bundle_id.id} failed unexpectedly'
         self.logger.warning(Colors.YELLOW(f'{error}: {err.error_response}'))
         return True
Example #5
0
 def has_certificate(profile) -> bool:
     try:
         profile_certificates = self.api_client.profiles.list_certificate_ids(profile)
         return bool(certificate_ids.issubset({c.id for c in profile_certificates}))
     except AppStoreConnectApiError as err:
         error = f'Listing {SigningCertificate.s} for {Profile} {profile.id} failed unexpectedly'
         self.logger.warning(Colors.YELLOW(f'{error}: {err.error_response}'))
         return False
Example #6
0
    def notify_profile_usage(self):
        self.logger.info(
            Colors.GREEN('Completed configuring code signing settings'))

        if not self._matched_profiles:
            message = 'Did not find matching provisioning profiles for code signing!'
            self.logger.warning(Colors.YELLOW(message))
            return

        for info in sorted(self._matched_profiles, key=lambda i: i.sort_key()):
            self.logger.info(Colors.BLUE(info.format()))
    def _deprecation_notice(self):
        from .android_app_bundle import AndroidAppBundle

        current_action = f'{self.get_executable_name()} {self.generate.action_name}'
        new_action = f'{AndroidAppBundle.get_executable_name()} {AndroidAppBundle.build_universal_apks.action_name}'
        lines = (
            f'Warning! Action "{current_action}" is deprecated and will be removed in future releases.',
            f'Please use action "{new_action}" instead.',
            f'See "{AndroidAppBundle.get_executable_name()} --help" for more information.',
        )
        for line in lines:
            self.logger.info(Colors.YELLOW(line))
Example #8
0
 def log_found(self,
               resource_type: Type[R],
               resources: Sequence[R],
               resource_filter: Optional[ResourceManager.Filter] = None,
               related_resource_type: Optional[Type[R2]] = None):
     count = len(resources)
     name = resource_type.plural(count)
     suffix = f' matching specified filters: {resource_filter}.' if resource_filter is not None else ''
     related = f' for {related_resource_type}' if related_resource_type is not None else ''
     if count == 0:
         self.logger.info(Colors.YELLOW(f'Did not find any {name}{related}{suffix}'))
     else:
         self.logger.info(Colors.GREEN(f'Found {count} {name}{related}{suffix}'))
    def use_profiles(self,
                     xcode_project_patterns: Sequence[pathlib.Path],
                     profile_path_patterns: Sequence[pathlib.Path],
                     export_options_plist: pathlib.Path = ExportIpaArgument.
                     EXPORT_OPTIONS_PATH.get_default(),
                     custom_export_options: Optional[Dict] = None,
                     warn_only: bool = False):
        """
        Set up code signing settings on specified Xcode projects
        to use given provisioning profiles
        """
        from .keychain import Keychain

        self.logger.info(Colors.BLUE('Configure code signing settings'))

        profile_paths = self.find_paths(*profile_path_patterns)
        xcode_projects = self.find_paths(*xcode_project_patterns)

        try:
            profiles = [
                ProvisioningProfile.from_path(p) for p in profile_paths
            ]
        except (ValueError, IOError) as error:
            raise XcodeProjectException(*error.args)

        available_certs = Keychain().list_code_signing_certificates(
            should_print=False)
        code_signing_settings_manager = CodeSigningSettingsManager(
            profiles, available_certs)

        for xcode_project in xcode_projects:
            try:
                code_signing_settings_manager.use_profiles(xcode_project)
            except (ValueError, IOError) as error:
                if warn_only:
                    self.logger.warning(
                        Colors.YELLOW(
                            f'Using profiles on {xcode_project} failed'))
                else:
                    raise XcodeProjectException(*error.args)

        code_signing_settings_manager.notify_profile_usage()
        export_options = code_signing_settings_manager.generate_export_options(
            custom_export_options)
        export_options.notify(
            Colors.GREEN('Generated options for exporting the project'))
        export_options.save(export_options_plist)

        self.logger.info(
            Colors.GREEN(f'Saved export options to {export_options_plist}'))
        return export_options
    def _find_certificates(self):
        process = self.execute(('security', 'find-certificate', '-a', '-p', self.path), show_output=False)
        if process.returncode != 0:
            raise KeychainError(f'Unable to list certificates from keychain {self.path}', process)

        pem = ''
        for line in process.stdout.splitlines():
            pem += line + '\n'
            if line == '-----END CERTIFICATE-----':
                try:
                    yield Certificate.from_pem(pem)
                except ValueError:
                    self.logger.warning(Colors.YELLOW('Failed to read certificate from keychain'))
                pem = ''