Reputation: 35
I'm building a MacOS application with Flutter. I'm new to MacOS-Development in general and might need some assistance with all the steps that are necessary to complete before I can successfully publish the application.
The main problem currently is that I get this "“xxx.app” cannot be opened because the developer cannot be verified." dialog, when I want to execute the application.
I want to deliver the app via dmg-File. The "installation" works fine I guess, but the dialog appears when I'm launching the app e.g. from launchpad.
I'm also using sparkle-project for updating the application later on.
My research has resulted in it being an issue with notarization.
I'll try to describe my whole build process as detailed as possible.
First of all I'm using GitHub Actions. For creating the build I'm using the flutter_distributor package. It simplifies the process of packaging and creating the dmg-file.
- name: Build with flutter_distributor
run: |
dart pub global activate flutter_distributor
flutter_distributor release --name prod --jobs macos-dmg
This is the configuration for flutter_distributor:
output: dist/
releases:
- name: prod
jobs:
- name: macos-dmg
package:
platform: macos
target: dmg
build_args:
dart-define:
APP_ENV: dev
Packaging went all good and it resulted in a dmg-image which could be executed. It uses node-appdmg for creation of the dmg-image.
The next step is notarization and stapling:
- name: "Notarize and Staple Release Build"
uses: GuillaumeFalourd/notary-tools@v1
with:
product_path: "dist/${{ steps.version.outputs.VERSION }}/xxx.dmg"
apple_id: ${{ secrets.NOTARIZATION_USERNAME }}
password: ${{ secrets.NOTARIZATION_PASSWORD }}
team_id: ${{ secrets.NOTARIZATION_TEAMID }}
staple: true
xcode_path: '/Applications/Xcode.app'
Notarization failed at this point.
I read in the logs of node-appdmg that it skipped the signing part when creating the dmg-image. Therefore I added the code-sign configuration:
"code-sign": {
"signing-identity": "Developer ID Application: xxx (***)"
}
After this it signed the dmg but notarization still failed. So I looked into the notarization logs:
{
"severity": "error",
"code": null,
"path": "xxx.dmg/xxx.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate",
"message": "The binary is not signed with a valid Developer ID certificate.",
"docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087721",
"architecture": "x86_64"
},
{
"severity": "error",
"code": null,
"path": "xxx.dmg/xxx.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate",
"message": "The signature does not include a secure timestamp.",
"docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087733",
"architecture": "x86_64"
},
There were more like this that appear to be related to sparkle.
So my solution to this was adding --timestamp
and --deep
to Other Code Signing Flags
in XCode.
After this notarization and stapling was successful, but the dialog still appeared when I try to start the application.
What am I missing? Do I need to notarize the app before adding it to the dmg-image aswell?
Upvotes: 2
Views: 10740
Reputation: 1373
If you are exporting your app from Xcode you won't see this issue, but since you want to automate this process, you must also sign the Sparkle framework manually with your Developer ID Application Certificate
.
Here is a ruby function that does that:
def self.sign_sparkle_framework(exported_app_path, application_cert_name)
sparkle_auto_update_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/AutoUpdate"
codesign('Signing Sparkle AutoUpdate...', application_cert_name, sparkle_auto_update_path)
sparkle_updater_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/Updater.app"
codesign('Signing Sparkle Updater...', application_cert_name, sparkle_updater_path)
sparkle_installer_xpc_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/XPCServices/Installer.xpc/Contents/MacOS/Installer"
codesign('Signing Sparkle Installer XPC Service...', application_cert_name, sparkle_installer_xpc_path)
sparkle_downloader_xpc_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/XPCServices/Downloader.xpc/Contents/MacOS/Downloader"
codesign('Signing Sparkle Downloader XPC Service...', application_cert_name, sparkle_downloader_xpc_path)
sparkle_framework_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework"
codesign('Signing Sparkle framework...', application_cert_name, sparkle_framework_path)
end
def self.codesign(title, application_cert_name, path)
puts_if_verbose title
execute_command("codesign -f -o runtime --timestamp -s \"#{application_cert_name}\" \"#{path}\"")
end
I have implemented an open-source command line tool that automates the whole process of archiving, exporting, notarizing, packaging, and uploading a macOS application outside the App Store using the Sparkle framework. It can be helpful for your case, check it out here.
Upvotes: 0