Skip to content

Commit

Permalink
macOS|Builder: Notarize application bundles
Browse files Browse the repository at this point in the history
  • Loading branch information
skyjake committed Oct 10, 2019
1 parent 1c95615 commit 3de60a8
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 19 deletions.
125 changes: 125 additions & 0 deletions doomsday/build/scripts/notarize.py
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
#
# macOS App Notarization
# Usage: notarize.py (app) (apple-id-email)
#
# The given app bundle is (eventually) stapled with a notarization ticket.
# Waits until Apple's servers finish checking the files. Returns non-zero
# on error.

import json
import os
import subprocess
import sys
import time
import urllib.request
import xml.etree.ElementTree as xet
pjoin = os.path.join

BUNDLE_PATH = sys.argv[1]
APPLE_ID = sys.argv[2]
POLL_INTERVAL = 60 # seconds
START_TIME = time.time()


def parse_dict(root):
res = {}
key = None
for child in root:
if key:
if child.tag == 'string':
res[key] = child.text
elif child.tag == 'integer':
res[key] = int(child.text)
key = None
elif child.tag == 'key':
key = child.text
return res

def key_value(root, key):
found_key = False
for child in root:
if child.tag == 'dict':
for elem in child:
if elem.tag == 'key':
if elem.text == key:
found_key = True
elif found_key:
if elem.tag == 'string':
return elem.text
elif elem.tag == 'dict':
return parse_dict(elem)
return None


# Check the bundle ID.
app_info = xet.parse(pjoin(BUNDLE_PATH, 'Contents/Info.plist')).getroot()
bundle_id = key_value(app_info, 'CFBundleIdentifier')
print('Bundle Identifier:', bundle_id)

# Compress the application for upload.
pbid = bundle_id + '.zip'
print('Compressing:', pbid)
subprocess.check_call(['zip', '-9', '-r', '-y', pbid, BUNDLE_PATH])

# Submit the compressed app.
print('Submitting for notarization...')
result_text = subprocess.check_output(['/usr/bin/xcrun', 'altool',
'--notarize-app',
'--primary-bundle-id', pbid,
'--username', APPLE_ID,
'--password', '@keychain:notarize.py',
'--file', pbid,
'--output-format', 'xml'
])

# Check the submission result.
result = xet.fromstring(result_text)
req_uuid = key_value(result, 'notarization-upload')['RequestUUID']
msg = key_value(result, 'success-message')
print('Request UUID:', req_uuid)
print('Status:', msg)
os.remove(pbid) # clean up

# Wait for the processing to finish.
print('Waiting for result...')
while True:
result_text = subprocess.check_output(['/usr/bin/xcrun', 'altool',
'--notarization-info', req_uuid,
'--username', APPLE_ID,
'--password', '@keychain:notarize.py',
'--output-format', 'xml'
])
print(result_text)
result = xet.fromstring(result_text)
info = key_value(result, 'notarization-info')
if info['Status'] != 'in progress':
break
time.sleep(POLL_INTERVAL)

print('Finished at:', info['Date'])
log_file_url = info['LogFileURL']
status = info['Status']
status_code = info['Status Code']
status_msg = info['Status Message']
print('%s (%d): %s' % (status, status_code, status_msg))

# Check errors and warnings.
local_fn, headers = urllib.request.urlretrieve(log_file_url)
log = json.load(open(local_fn))
print(log)
# TODO: If there are issues, abort build with an error code.

# Staple.
subprocess.check_call(['/usr/bin/xcrun', 'stapler', 'staple', BUNDLE_PATH])

# Verify.
print(subprocess.check_output(['/usr/sbin/spctl', '-a', '-v', BUNDLE_PATH]))
# TODO: If verification fails, abort build with an error code.

# Successful.
nb_sec = int(time.time() - START_TIME)
nb_hour = int(nb_sec / 3600)
nb_min = int(nb_sec / 60) % 60
print('Notarization finished in %dh %dm %ds' % (nb_hour, nb_min, nb_sec % 60))
sys.exit(0)
2 changes: 1 addition & 1 deletion doomsday/cmake/MacOSXBundleInfo.plist.in
Expand Up @@ -17,7 +17,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${DENG_VERSION}</string>
<string>${DENG_VERSION_WITH_BUILD}</string>
<key>NSHumanReadableCopyright</key>
<string>${DENG_TEAM_COPYRIGHT}</string>
<key>NSPrincipalClass</key>
Expand Down
32 changes: 16 additions & 16 deletions doomsday/cmake/MacOSXPluginBundleInfo.plist.in
Expand Up @@ -2,31 +2,31 @@
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_BUNDLE_EXECUTABLE}</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleVersion</key>
<string>${DENG_VERSION_WITH_BUILD}</string>
<key>CFBundleLongVersionString</key>
<string>${DENG_VERSION_WITH_BUILD}</string>
<key>CFBundleShortVersionString</key>
<string>${DENG_VERSION}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${DENG_VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${DENG_VERSION}</string>
<key>NSHumanReadableCopyright</key>
<string>${DENG_TEAM_COPYRIGHT}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_BUNDLE_EXECUTABLE}</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
</dict>
</plist>
13 changes: 11 additions & 2 deletions doomsday/cmake/Macros.cmake
Expand Up @@ -504,7 +504,9 @@ macro (deng_codesign target)
endif ()
if (NOT _skip)
message (STATUS \"Signing \${fn}...\")
execute_process (COMMAND ${CODESIGN_COMMAND} --verbose
execute_process (COMMAND ${CODESIGN_COMMAND} --verbose
--options runtime
--timestamp
-s \"${DENG_CODESIGN_APP_CERT}\"
${DENG_FW_CODESIGN_EXTRA_FLAGS}
\"\${fn}\"
Expand All @@ -513,10 +515,17 @@ macro (deng_codesign target)
endforeach (fn)
message (STATUS \"Signing \${CMAKE_INSTALL_PREFIX}/${_outName}.app using '${DENG_CODESIGN_APP_CERT}'...\")
execute_process (COMMAND ${CODESIGN_COMMAND} --verbose
--options runtime
--timestamp
--force -s \"${DENG_CODESIGN_APP_CERT}\"
${DENG_CODESIGN_EXTRA_FLAGS}
\"\${CMAKE_INSTALL_PREFIX}/${_outName}.app\"
)")
)
message (STATUS \"Notarizing \${CMAKE_INSTALL_PREFIX}/${_outName}.app...\")
execute_process (COMMAND ${DENG_SOURCE_DIR}/build/scripts/notarize.py
\"\${CMAKE_INSTALL_PREFIX}/${_outName}.app\"
${DENG_NOTARIZATION_APPLE_ID}
)")
endif ()
if (WIN32 AND DENG_SIGNTOOL_CERT)
get_property (_outName TARGET ${target} PROPERTY OUTPUT_NAME)
Expand Down
5 changes: 5 additions & 0 deletions doomsday/cmake/Version.cmake
Expand Up @@ -3,6 +3,11 @@ set (DENG_VERSION_MINOR 2)
set (DENG_VERSION_PATCH 0)

set (DENG_VERSION ${DENG_VERSION_MAJOR}.${DENG_VERSION_MINOR}.${DENG_VERSION_PATCH})
if (DEFINED DENG_BUILD)
set (DENG_VERSION_WITH_BUILD ${DENG_VERSION}.${DENG_BUILD})
else ()
set (DENG_VERSION_WITH_BUILD ${DENG_VERSION})
endif ()

# Binary compatibility version for shared libraries / APIs.
set (DENG_COMPAT_VERSION 2.2)
Expand Down

0 comments on commit 3de60a8

Please sign in to comment.