This journey started with my naive idea that I could do all of this outside of Macintosh/MacOS. It started out great when I found Apple's Notary API, so I wrote macosnotarylib. But I also needed binary and package signing, and I gave up the grand idea. There are some third party libraries that claim to do some of this, but for me, even getting passed the trust part where I would have to inspect the code, would be too much work.
So, I wrote some tooling for this myself that uses Apple's CLI tools and API, and this library is the core part of it. There are Go alternatives out there, most notably gon. The biggest difference is that gon produces DMG or ZIP files. This library produces the very end-user-friendly PKG format.
The bulding blocks:
- Running
buildpkg.New(opts).Build()will- Sign the binary with
codesign - Package the binary with
pkgbuild - Sign the package with
productsign - Check the package with
pkgutil - Notarize the package with macosnotarylib (uses the Apple API)
- Staple the package with
stapler
- Sign the binary with
For the codesign step you need create a Developer ID Application Certificate and for the package signing step you need a Developer ID Installer Certificate. These needs to be imported into your Keychain. Follow the instructions at [developer.apple.com.
Once you have those imported in the Keychain you can locate their common signing identity with security find-identity -v, which is XYZJUFSYL4 in the example below:
~/d/g/hugoreleaser ❯❯❯ security find-identity -v 1) D4A412805301423E2DF63D90CE37C8A050B3AA2F "Developer ID Application: Bjørn Erik Pedersen (XYZJUFSYL4)" 2) D4A412805301423E2DF63D90CE37C8A050B3AA2F "Developer ID Application: Bjørn Erik Pedersen (XYZJUFSYL4)" 3) EADAD38B73CADB2E6975F55B8735F17B09138217 "Developer ID Installer: Bjørn Erik Pedersen (XYZJUFSYL4)" 4) EADAD38B73CADB2E6975F55B8735F17B09138217 "Developer ID Installer: Bjørn Erik Pedersen (XYZJUFSYL4)" 4 valid identities found
For the notarizer step you need to create a new new API access key with Developer access and download the private key. Take note of the Issuer ID and Key ID:
Also See Creating API Keys for App Store Connect AP.
With the above you could put the signing identity in the Options struct and pass it to New:
type Options struct { // The Info logger. // If nil, no Info logging will be done. Infof func(format string, a ...interface{}) // The Dir to build from. Dir string // Developer ID Application + Developer ID Installer // https://developer.apple.com/account/resources/certificates/list SigningIdentity string // The result PackageOutputFilename string // The staging directory where all your build artifacts are located. StagingDirectory string // E.g. io.gohugo.hugo Identifier string // E.g. 234 Version string // E.g. /usr/local/bin InstallLocation string // Scripts passed on the command line --scripts flag. // E.g. /mypkgscripts ScriptsDirectory string // Flags to enable skipping of build steps. SkipCodeSigning bool SkipInstallerSigning bool SkipNotarization bool }
The other settings currently needs to be set as OS environment variables:
MACOSNOTARYLIB_ISSUER_IDMACOSNOTARYLIB_KID(Key ID)MACOSNOTARYLIB_PRIVATE_KEY(in base64 format).
Use with Hugoreleaser
There are 2 archive plugins available:
- macospkgremote
- macospkg (a "local" variant of the above)
Also see it configured in Hugoreleaser's build config.

