MacOS notarisation with Electron
While working on Ghostmail, an Electron-based Maildir
frontend I’ve built, I had to get the app ready for distribution on other people’s machines by code signing and notarising it.
I’m using Electron Forge to build the app, and I expected everything to work pretty much out of the box. However, I ran into an important, niche issue that should be highlighted.
I added the dotenv
package to my development dependencies, initialised it at the top, and filled out my .env
file with the relevant values, taking care to create an app-specific password for my account.
const { FusesPlugin } = require("@electron-forge/plugin-fuses");
const { FuseV1Options, FuseVersion } = require("@electron/fuses");
if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
module.exports = {
packagerConfig: {
asar: true,
icon: "assets/ghost",
osxSign: {},
osxNotarize: {
tool: "notarytool",
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_PASSWORD,
teamId: process.env.APPLE_TEAM_ID,
},
},
rebuildConfig: {},
makers: [
{
name: "@electron-forge/maker-zip",
platforms: ["darwin"],
config: (arch) => ({
// Note that we must provide this S3 URL here
// in order to support smooth version transitions
// especially when using a CDN to front your updates
macUpdateManifestBaseUrl: `${process.env.S3_PUBLIC_HOSTNAME}/Ghostmail/darwin/${arch}`,
}),
},
],
publishers: [
{
name: "@electron-forge/publisher-s3",
config: {
bucket: process.env.S3_BUCKET,
region: "auto",
endpoint: process.env.S3_ENDPOINT,
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
public: true,
},
},
],
plugins: [
{
name: "@electron-forge/plugin-auto-unpack-natives",
config: {},
},
// Fuses are used to enable/disable various Electron functionality
// at package time, before code signing the application
new FusesPlugin({
version: FuseVersion.V1,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true,
}),
],
};
My Apple ID has existed for well over a decade. The first thing I had to do was leave an expired team I was no longer involved with. Immediately, problems. I logged in and out and kept seeing the old team, and after 3-4 attempts over 30+ minutes, the old team was gone and I was able to join again with my own membership.
From there, running my npm run package
script to generate Ghostmail and get it signed and notarised, I kept getting stuck.
Every time, no matter how long I left it, I’d get stuck on the following debug output:
electron-notarize:notarytool zip succeeded, attempting to upload to Apple +7s
I eventually extrapolated the command that Forge was running, dropped the JSON output and wait flags from it, and ran it directly:
xcrun notarytool submit /var/folders/kg/g2tj3w9n6tj3fvn9kqydcj3m0000gn/T/electron-notarize-FCWaM3/Ghostmail.zip --apple-id [email protected] --password pass-goes-here --team-id ABC123
This one uploaded the zip and gave me a successful notarisation response within 30 seconds! 🤯
I decided to re-run the original package command and it still continued to fail.
At this point, I had a sneaking suspicion that the problem was that whilst my actual Apple ID was very old, my membership in the Developer Programme was brand new. When I re-ran the command again the next day, it succeeded without issue.
Key takeaway⌗
The important takeaway is that you seemingly cannot sign up for the Developer Programme and start notarising in the first ~8 hours - you must be patient.