[Bf-blender-cvs] [d2888558f10] codesign: Codesign: Hook up notarial office
Sergey Sharybin
noreply at git.blender.org
Thu Dec 12 16:29:15 CET 2019
Commit: d2888558f10e64d44c5e3d124c61643aaeef2b7c
Author: Sergey Sharybin
Date: Thu Dec 12 16:26:49 2019 +0100
Branches: codesign
https://developer.blender.org/rBd2888558f10e64d44c5e3d124c61643aaeef2b7c
Codesign: Hook up notarial office
===================================================================
M build_files/buildbot/codesign/base_code_signer.py
M build_files/buildbot/codesign/config_server_template.py
M build_files/buildbot/codesign/macos_code_signer.py
===================================================================
diff --git a/build_files/buildbot/codesign/base_code_signer.py b/build_files/buildbot/codesign/base_code_signer.py
index a72662963bf..0481bcf2564 100644
--- a/build_files/buildbot/codesign/base_code_signer.py
+++ b/build_files/buildbot/codesign/base_code_signer.py
@@ -409,8 +409,43 @@ class BaseCodeSigner(metaclass=abc.ABCMeta):
If the platform is different then it will only be printed allowing
to verify logic of the code signing process.
"""
- if platform != self.platform:
+
+ if platform != self.platform or True:
logger_server.info(
f'Will run command for {platform}: {command}')
return
+
+ logger_server.info(f'Running command: {command}')
subprocess.run(command)
+
+ # TODO(sergey): What is the type annotation for the command?
+ def check_output_or_mock(self, command,
+ platform: util.Platform,
+ allow_nonzero_exit_code=False) -> str:
+ """
+ Run given command if current platform matches given one
+
+ If the platform is different then it will only be printed allowing
+ to verify logic of the code signing process.
+
+ If allow_nonzero_exit_code is truth then the output will be returned
+ even if application quit with non-zero exit code.
+ Otherwise an subprocess.CalledProcessError exception will be raised
+ in such case.
+ """
+
+ if platform != self.platform:
+ logger_server.info(
+ f'Will run command for {platform}: {command}')
+ return
+
+ if allow_nonzero_exit_code:
+ process = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ output = process.communicate()[0]
+ return output.decode()
+
+ logger_server.info(f'Running command: {command}')
+ return subprocess.check_output(
+ command, stderr=subprocess.STDOUT).decode()
diff --git a/build_files/buildbot/codesign/config_server_template.py b/build_files/buildbot/codesign/config_server_template.py
index 326e76d0283..1d6ddc54380 100644
--- a/build_files/buildbot/codesign/config_server_template.py
+++ b/build_files/buildbot/codesign/config_server_template.py
@@ -50,6 +50,15 @@ MACOS_ENTITLEMENTS_FILE = \
# NOTE: This identity is just an example from release/darwin/README.txt.
MACOS_CODESIGN_IDENTITY = 'AE825E26F12D08B692F360133210AF46F4CF7B97'
+# User name (Apple ID) which will be used to request notarization.
+MACOS_XCRUN_USERNAME = 'me at example.com'
+
+# One-time application password which will be used to request notarization.
+MACOS_XCRUN_PASSWORD = '@keychain:altool-password'
+
+# Timeout in seconds within which the notarial office is supposed to reply.
+MACOS_NOTARIZE_TIMEOUT_IN_SECONDS = 60 * 60
+
################################################################################
# Windows-specific configuration.
diff --git a/build_files/buildbot/codesign/macos_code_signer.py b/build_files/buildbot/codesign/macos_code_signer.py
index 279b77e1ec6..79df50ae674 100644
--- a/build_files/buildbot/codesign/macos_code_signer.py
+++ b/build_files/buildbot/codesign/macos_code_signer.py
@@ -19,7 +19,9 @@
# <pep8 compliant>
import logging
+import re
import subprocess
+import time
from pathlib import Path
from typing import List
@@ -87,9 +89,27 @@ def is_bundle_executable_file(file: AbsoluteAndRelativeFileName) -> bool:
return True
+def xcrun_field_value_from_output(field: str, output: str) -> str:
+ """
+ Get value of a given field from xcrun output.
+
+ If field is not found empty string is returned.
+ """
+
+ field_prefix = field + ': '
+ for line in output.splitlines():
+ line = line.strip()
+ if line.startswith(field_prefix):
+ return line[len(field_prefix):]
+ return ''
+
+
class MacOSCodeSigner(BaseCodeSigner):
def check_file_is_to_be_signed(
self, file: AbsoluteAndRelativeFileName) -> bool:
+ if file.relative_filepath.name.startswith('.'):
+ return False
+
if is_bundle_executable_file(file):
return True
@@ -100,6 +120,9 @@ class MacOSCodeSigner(BaseCodeSigner):
return file.relative_filepath.suffix in EXTENSIONS_TO_BE_SIGNED
+ ############################################################################
+ # Codesign.
+
def codesign_remove_signature(
self, file: AbsoluteAndRelativeFileName) -> None:
"""
@@ -222,6 +245,161 @@ class MacOSCodeSigner(BaseCodeSigner):
return True
+ ############################################################################
+ # Notarization.
+
+ def notarize_get_bundle_id(self, file: AbsoluteAndRelativeFileName) -> str:
+ """
+ Get bundle ID which will be used to notarize DMG
+ """
+ name = file.relative_filepath.name
+ app_name = name.split('-', 2)[0].lower()
+
+ # TODO(sergey): Consider using "alpha" for buildbot builds.
+ return f'org.blenderfoundation.{app_name}.release'
+
+ def notarize_request(self, file) -> str:
+ """
+ Request notarization of the given file.
+
+ Returns UUID of the notarization request. If error occurred None is
+ returned instead of UUID.
+ """
+
+ bundle_id = self.notarize_get_bundle_id(file)
+ logger_server.info('Bundle ID: %s', bundle_id)
+
+ logger_server.info('Submitting file to the notarial office.')
+ command = [
+ 'xcrun', 'altool', '--notarize-app', '--verbose',
+ '-f', file.absolute_filepath,
+ '--primary-bundle-id', bundle_id,
+ '--username', self.config.MACOS_XCRUN_USERNAME,
+ '--password', self.config.MACOS_XCRUN_PASSWORD]
+
+ output = self.check_output_or_mock(
+ command, util.Platform.MACOS, allow_nonzero_exit_code=True)
+
+ for line in output.splitlines():
+ line = line.strip()
+ if line.startswith('RequestUUID = '):
+ request_uuid = line[14:]
+ return request_uuid
+
+ # Check whether the package has been already submitted.
+ if 'The software asset has already been uploaded.' in line:
+ request_uuid = re.sub(
+ '.*The upload ID is ([A-Fa-f0-9\-]+).*', '\\1', line)
+ logger_server.warning(
+ f'The package has been already submitted under UUID {request_uuid}')
+ return request_uuid
+
+ logger_server.error('xcrun command did not report RequestUUID')
+ return None
+
+ def notarize_wait_result(self, request_uuid: str) -> bool:
+ """
+ Wait for until notarial office have a reply
+ """
+
+ logger_server.info(
+ 'Waiting for a result from the notarization office.')
+
+ command = ['xcrun', 'altool',
+ '--notarization-info', request_uuid,
+ '--username', self.config.MACOS_XCRUN_USERNAME,
+ '--password', self.config.MACOS_XCRUN_PASSWORD]
+
+ time_start = time.monotonic()
+ timeout_in_seconds = self.config.MACOS_NOTARIZE_TIMEOUT_IN_SECONDS
+
+ while True:
+ output = self.check_output_or_mock(command, util.Platform.MACOS)
+ # Parse status and message
+ status = xcrun_field_value_from_output('Status', output)
+ status_message = xcrun_field_value_from_output(
+ 'Status Message', output)
+
+ # Review status.
+ if status:
+ if status == 'success':
+ logger_server.info(
+ 'Package successfully notarized: %s', status_message)
+ return True
+ elif status == 'invalid':
+ logger_server.error(
+ 'Package notarization has failed: %s', status_message)
+ return False
+ else:
+ logger_server.info(
+ 'Unknown notarization status %s (%s)', status, status_message)
+
+ logger_server.info('Keep waiting for notarization office.')
+ time.sleep(30)
+
+ time_slept_in_seconds = time.monotonic() - time_start
+ if time_slept_in_seconds > timeout_in_seconds:
+ logger_server.error(
+ "Notarial office didn't reply in %f seconds.",
+ timeout_in_seconds)
+
+ def notarize_staple(self, file: AbsoluteAndRelativeFileName) -> bool:
+ """
+ Staple notarial label on the file
+ """
+
+ logger_server.info(
+ 'Waiting for a result from the notarization office.')
+
+ command = ['xcrun', 'stapler', 'staple', '-v', file.absolute_filepath]
+ self.check_output_or_mock(command, util.Platform.MACOS)
+
+ return True
+
+ def notarize_dmg(self, file: AbsoluteAndRelativeFileName) -> bool:
+ """
+ Run entire pipeline to get DMG notarized.
+ """
+ logger_server.info('Begin notarization routines on %s',
+ file.relative_filepath)
+
+ # Submit file for notarization.
+ request_uuid = self.notarize_request(file)
+ if not request_uuid:
+ return False
+ logger_server.info('Received Request UUID: %s', request_uuid)
+
+ # Wait for the status from the notarization office.
+ if not self.notarize_wait_result(request_uuid):
+ return False
+
+ # Staple.
+ if not self.notarize_staple(file):
+ return False
+
+ return True
+
+ def notarize_all_dmg(
+ self, files: List[AbsoluteAndRelativeFileName]) -> bool:
+ """
+ Notarize all DMG images from the input.
+
+ Images are supposed to be codesigned already.
+ """
+ for file in files:
+ if not file.relative_filepath.name.endswith('.dmg'):
+ continue
+ if not self.check_file_is_to_be_sig
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list