Skip to content

Instantly share code, notes, and snippets.

@estan
Created July 11, 2019 07:59
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save estan/505cd5b4c18d80f1dd17ac2ea0f6c69e to your computer and use it in GitHub Desktop.
Save estan/505cd5b4c18d80f1dd17ac2ea0f6c69e to your computer and use it in GitHub Desktop.
Python script for notarizing an app (e.g. a .dmg)
#!/usr/bin/env python3
#
# Notarize a file (e.g. a .dmg)
#
# Usage: notarize-macos.py <Apple ID username> <Apple ID password> <file>
#
# Note: The --primary-bundle-id flag is hard-coded below, so you'll want to
# change that!
#
# See https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution#3087727
# for information on how to prepare your app for notarization.
#
from argparse import ArgumentParser
from subprocess import check_output
from plistlib import loads
from time import sleep
def main():
parser = ArgumentParser()
parser.add_argument('username', help='Apple ID user')
parser.add_argument('password', help='Apple ID password')
parser.add_argument('path', help='File to be notarized (e.g. .dmg)')
args = parser.parse_args()
print('requesting notarization of {}...'.format(args.path))
request_uuid = loads(check_output([
'xcrun',
'altool',
'--notarize-app',
'--primary-bundle-id', 'com.yourdomain.yourapp.dmg',
'--username', args.username,
'--password', args.password,
'--file', args.path,
'--output-format', 'xml'
]))['notarization-upload']['RequestUUID']
for i in range(200):
response = loads(check_output([
'xcrun',
'altool',
'--notarization-info', request_uuid,
'--username', args.username,
'--password', args.password,
'--output-format', 'xml'
]))
if response['notarization-info']['Status'] == 'success':
print('notarization succeeded, see {}'.format(response['notarization-info']['LogFileURL']))
print('stapling notarization to {}'.format(args.path))
print(check_output(['xcrun', 'stapler', 'staple', args.path]).decode('utf-8'))
return
if response['notarization-info']['Status'] == 'invalid':
raise RuntimeError('notarization failed, response was\n{}'.format(response))
sleep(3)
raise RuntimeError('notarization timed out, last response was\n{}'.format(response))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment