diff --git a/Makefile b/Makefile index 0af75b42c29..ed96a2438e2 100644 --- a/Makefile +++ b/Makefile @@ -1247,7 +1247,7 @@ $(PKG): release-only --resources $(MACOSOUTDIR)/installer/productbuild/Resources \ --package-path $(MACOSOUTDIR)/pkgs ./$(PKG) SIGN="$(PRODUCTSIGN_CERT)" PKG="$(PKG)" sh tools/osx-productsign.sh - sh tools/osx-notarize.sh $(FULLVERSION) + sh tools/osx-notarize.sh $(PKG) .PHONY: pkg pkg: $(PKG) ## Build the macOS installer for releases. @@ -1433,6 +1433,7 @@ else endif ifeq ($(OSTYPE),darwin) SIGN="$(CODESIGN_CERT)" PKGDIR="$(BINARYNAME)" sh tools/osx-codesign.sh + sh tools/osx-notarize.sh $(BINARYNAME)/bin/node endif tar -cf $(BINARYNAME).tar $(BINARYNAME) $(RM) -r $(BINARYNAME) diff --git a/tools/osx-notarize.sh b/tools/osx-notarize.sh index 06ba801c9e8..ea9401b4816 100755 --- a/tools/osx-notarize.sh +++ b/tools/osx-notarize.sh @@ -1,12 +1,27 @@ #!/bin/sh -# Notarize a generated node-.pkg file as an Apple requirement for installation on macOS Catalina and later, as validated by Gatekeeper. +# Notarize a macOS release artifact as an Apple requirement for running or +# installing on macOS Catalina and later, as validated by Gatekeeper. # Uses notarytool and requires Xcode >= 13.0. +# +# Usage: osx-notarize.sh +# +# ending in .pkg: the installer is submitted as is, assessed with +# spctl and the notarization ticket is stapled to it. +# any other : a Mach-O binary, such as bin/node from the release +# tarball. Apple only accepts zip, dmg or pkg submissions, so the binary +# is zipped for the upload and the zip is discarded afterwards. A bare +# binary cannot carry a stapled ticket; Gatekeeper fetches it online by +# the code directory hash, so the binary itself is left untouched and the +# published checksums do not change. +# +# For backwards compatibility a bare version id (for example v26.13.0) is +# accepted and resolved to node-.pkg. -pkgid="$1" +target="$1" -if [ -z "$pkgid" ]; then - echo "Usage: $0 " +if [ -z "$target" ]; then + echo "Usage: $0 " exit 1 fi @@ -26,6 +41,21 @@ if [ -z "$NOTARIZATION_TEAM_ID" ]; then exit 0 fi +# Legacy call form: a version id instead of a path. +if [ ! -e "$target" ] && [ -e "node-$target.pkg" ]; then + target="node-$target.pkg" +fi + +if [ ! -e "$target" ]; then + echo "error: $target does not exist." 1>&2 + exit 1 +fi + +case "$target" in + *.pkg) kind=pkg ;; + *) kind=binary ;; +esac + echo "Notarization process is done with Notarytool." if ! command -v xcrun notarytool > /dev/null @@ -34,20 +64,6 @@ then exit 1 fi -echo "Submitting node-$pkgid.pkg for notarization..." - -xcrun notarytool submit \ - --keychain-profile "NODE_RELEASE_PROFILE" \ - --wait \ - "node-$pkgid.pkg" - -if [ $? -eq 0 ]; then - echo "Notarization node-$pkgid.pkg submitted successfully." -else - echo "Notarization node-$pkgid.pkg failed." - exit 1 -fi - # The notarization ticket can take a while to become available from Apple's # servers after notarytool reports "Accepted", so retry with backoff. retry() { @@ -64,15 +80,62 @@ retry() { done } -if ! retry xcrun spctl --assess --type install --context context:primary-signature --ignore-cache --verbose=2 "node-$pkgid.pkg"; then - echo "error: Signature will not be accepted by Gatekeeper!" 1>&2 - exit 1 +submit() { + echo "Submitting $1 for notarization..." + if xcrun notarytool submit \ + --keychain-profile "NODE_RELEASE_PROFILE" \ + --wait \ + "$1"; then + echo "Notarization $1 submitted successfully." + else + echo "Notarization $1 failed." + return 1 + fi +} + +if [ "$kind" = "pkg" ]; then + submit "$target" || exit 1 + + if ! retry xcrun spctl --assess --type install --context context:primary-signature --ignore-cache --verbose=2 "$target"; then + echo "error: Signature will not be accepted by Gatekeeper!" 1>&2 + exit 1 + else + echo "Verification was successful." + fi + + if ! retry xcrun stapler staple "$target"; then + echo "error: Stapling $target failed." 1>&2 + exit 1 + fi + echo "Stapler was successful." else - echo "Verification was successful." -fi + # ditto preserves the code signature and resource forks, which plain zip + # does not, so it is the archiver Apple documents for notarization uploads. + # The zip only exists for the upload. Remove it on every exit path, + # including a failed ditto or an interrupted build, the same way the + # dep_updaters scripts clean their workspace. + zip="$target.notarize.zip" + cleanup () { + EXIT_CODE=$? + rm -f "$zip" + exit $EXIT_CODE + } + trap cleanup INT TERM EXIT + rm -f "$zip" + if ! ditto -c -k --keepParent "$target" "$zip"; then + echo "error: Could not create $zip for submission." 1>&2 + exit 1 + fi -if ! retry xcrun stapler staple "node-$pkgid.pkg"; then - echo "error: Stapling node-$pkgid.pkg failed." 1>&2 - exit 1 + submit "$zip" || exit 1 + rm -f "$zip" + + # There is nothing to staple to a bare binary, so the only way to confirm + # the ticket is live is to have codesign look it up. spctl cannot be used + # here: it rejects command line tools with "does not seem to be an app". + if ! retry codesign --verify --verbose=2 -R=notarized "$target"; then + echo "error: $target will not be accepted by Gatekeeper!" 1>&2 + exit 1 + fi + echo "Verification was successful." fi -echo "Stapler was successful." \ No newline at end of file