Skip to content

Instantly share code, notes, and snippets.

@txoof
Last active July 23, 2024 19:59
Show Gist options
  • Save txoof/0636835d3cc65245c6288b2374799c43 to your computer and use it in GitHub Desktop.
Save txoof/0636835d3cc65245c6288b2374799c43 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- These are required for binaries built by PyInstaller -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

Setup

  • Create a developer account with Apple
  • Download and install X-Code from the Apple App Store
  • Open and run X-Code app and install whatever extras it requires
  • Open the preferences pane (cmd+,)
    • click the + in the lower right corner
    • choose Apple ID
    • enter your apple ID and password
    • Previously created keys can be downloaded and installed from https://developer.apple.com

Create an App-Specific password for altool to use

  • Instructions from Apple
  • Open KeyChain Access
  • Create a "New Password Item"
    • Keychain Item Name: Developer-altool
    • Account Name: your developer account email
    • Password: the application-specific password you just created

Create an executable binary with Pyinstaller

NB! Additional args such as --add-data may be needed to build a functional binary

  • Create a onefile binary
    • pyinstaller --onefile myapp.py

Sign the executable

  • Add the entitements.plist to the directory (see below)
  • List the available keys and locate a Developer ID Application certificate:
    • security find-identity -p basic -v
    1) ABC123 "Apple Development: aaronciuffonl@gmail.com ()"
    2) XYZ234 "Developer ID Installer: Aaron Ciuffo ()"
    3) QRS333 "Developer ID Application: Aaron Ciuffo ()"
    4) LMN343 "Developer ID Application: Aaron Ciuffo ()"
    5) ZPQ234 "Apple Development: aaron.ciuffo@gmail.com ()"
    6) ASD234 "Developer ID Application: Aaron Ciuffo ()"
    7) 01010A "Developer ID Application: Aaron Ciuffo ()"
       7 valid identities found
    
  • codesign --deep --force --options=runtime --entitlements ./entitlements.plist --sign "HASH_OF_DEVELOPER_ID APPLICATION" --timestamp ./dist/foo.app

Package as a pkg for installation

  • Create a temp directory to build the package:
    • mkdir /tmp/myapp
  • Use ditto to build the pkg installer structure
    • ditto /path/to/myapp /tmp/myapp/path/to/install/location
    • repeat for all files that should be packaged
  • build the pkackage
  • productbuild --identifier "com.your.pkgname.pkg" --sign "HASH_OF_INSTALLER_ID" --timestamp --root /tmp/myapp / myapp.pkg

Notarize

  • xcrun altool --notarize-app --primary-bundle-id "com.foobar.fooapp" --username="developer@foo.com" --password "@keychain:Developer-altool" --file ./myapp.pkg
  • Check email for successful notarization
    • Alternatively check status using:
      • xcrun altool --notarization-history 0 -u "developer@***" -p "@keychain:Developer-altool"
  • If notarization fails use the following to review a detailed log:
  xcrun altool --notarization-info "Your-Request-UUID" \
             --username "username@example.com" \                                    
             --password "@keychain:Developer-altool"   

Staple notarization to pkg

  • add the notariztaion to the pkg
    • xcrun stapler staple ghostscript64.pkg

Useful Resources

Alternative workflows that may have issues:

Create a bundled app with pyinstaller

  • Only ".app" bundles appear to work using this procedure
    • pyinstaller --windowed --onefile foo.py
    • edit the spec file app = BUNDLE section to include a bundle_identifier
    app = BUNDLE(exe,
               name='helloworld.app',
               icon=None,
               bundle_identifier='com.txoof.helloworld'
               )
    
  • NOTE! Appbundles will not execute properly -- they must be run by execuing the bundle.app/Contents/MacOS/myapp

package as a dmg

NB! This may not work for single file executables -- use the PKG method above

  • Create a .dmg:
    • clean any uneeded files out of ./dist; only the .app should remain
    • hdiutil create ./myapp.dmg -ov -volname "MyApp" -fs HFS+ -srcfolder "./dist"
  • Shrink and make read-only:
    • $hdiutil convert ./myapp.dmg -format UDZO -o myapp.dmg
@txoof
Copy link
Author

txoof commented Jul 23, 2024

I haven't needed this is a while and haven't kept it current. It looks like there are a few issues that would be worth investigating. You should know that these instructions rely on altool and that is deprecated and needs to be migrated to notarytool.

Some resources that might help you out:

Best of luck!

@saif-ellafi
Copy link

Thanks @txoof - I could work around it and made it work successfuly (yes, I moved from alttool to notarytool), by using the BUNDLE builds instead of a COLLECTION, this way all onedir dependencies were successfully signed. I am wrapping the pkg over the .app file with a post-script install that distributes the files the way I want them. Notarization succeeded this way.

Cheers and thanks

@txoof
Copy link
Author

txoof commented Jul 23, 2024

so glad you found a solution! I know how awful this process is, hence the gist.

It would be amazing if you forked this gist and wrote up your findings -- I'll add a link to your gist at the top. This gist is linked from a couple of places that get some traffic (e.g. stack overflow). It would be wonderful if folks could find your solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment