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_ID
MACOSNOTARYLIB_KID
(Key ID)MACOSNOTARYLIB_PRIVATE_KEY
(in base64 format).
There are 2 archive plugins available:
- macospkgremote
- macospkg (a "local" variant of the above)
Also see it configured in Hugoreleaser's build config.