Skip to content

Instantly share code, notes, and snippets.

@txoof
Last active November 8, 2024 23:01
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: [email protected] ()"
    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: [email protected] ()"
    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="[email protected]" --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 "[email protected]" \                                    
             --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 Feb 18, 2022

@barrownicholas Haha, after writing this, I did the same thing: https://github.com/txoof/codesign

My solution is not at all elegant, but it's been getting the job done for the moment. It will be nice to see how you approached this.

@saif-ellafi
Copy link

saif-ellafi commented Jul 23, 2024

Hi, trying to package as PKG but failing to sign (my app is a command line utility)

I am compiling as "onedir" and using an Apple Developer signature, however when submitting for notarization, I get the following:

"issues": [
    {
      "severity": "error",
      "code": null,
      "path": "playbtw_macos_3_11.zip/playbtw_macos_3_11.pkg/playbtw_macos_3_11.pkg Contents/Payload/tmp/playbtw_install/scripts/_internal/Python.framework/Versions/3.11/Python",
      "message": "The signature of the binary is invalid.",
      "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087735",
      "architecture": "x86_64"
    }
  ]

Any ideas? Seems like the embedded Python binary does not sign properly.
I am signing the binaries through pyinstallers' "codesign_identity" spec options, the rest is as this guide indicates.
Tried so many things without success

@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