ci: implement MacOS notarization using xcrun altool
This introduces an automated MacOS notarization process for Jenkins CI. The process involves: * Uploading the signed DMG file to the notary service * Checking periodically if the scanning process has completed * Stapling the successful scan ticket to the DMG file This is done by the `scripts/notarize-macos-pkg.sh` via the `make notarize-macos` target. The whole process is described in more details in `docs/macos_notarization.md`. Depends on: https://github.com/status-im/status-jenkins-lib/pull/27 Resolves: https://github.com/status-im/status-desktop/issues/2169 Signed-off-by: Jakub Sokołowski <jakub@status.im>
This commit is contained in:
parent
3f56885189
commit
af2ec66e0c
|
@ -28,3 +28,4 @@ resources.rcc
|
||||||
resources.qrc
|
resources.qrc
|
||||||
status-react-translations/
|
status-react-translations/
|
||||||
/.update.timestamp
|
/.update.timestamp
|
||||||
|
notarization.log
|
||||||
|
|
6
Makefile
6
Makefile
|
@ -340,6 +340,12 @@ ifdef MACOS_CODESIGN_IDENT
|
||||||
scripts/sign-macos-pkg.sh $(STATUS_CLIENT_DMG) $(MACOS_CODESIGN_IDENT)
|
scripts/sign-macos-pkg.sh $(STATUS_CLIENT_DMG) $(MACOS_CODESIGN_IDENT)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
notarize-macos: export CHECK_INTERVAL_SEC ?= 30
|
||||||
|
notarize-macos: export CHECK_RETRY_LIMIT ?= 20
|
||||||
|
notarize-macos: export MACOS_BUNDLE_ID ?= im.status.ethereum.desktop
|
||||||
|
notarize-macos:
|
||||||
|
scripts/notarize-macos-pkg.sh $(STATUS_CLIENT_DMG)
|
||||||
|
|
||||||
NIM_WINDOWS_PREBUILT_DLLS ?= tmp/windows/tools/pcre.dll
|
NIM_WINDOWS_PREBUILT_DLLS ?= tmp/windows/tools/pcre.dll
|
||||||
|
|
||||||
$(NIM_WINDOWS_PREBUILT_DLLS):
|
$(NIM_WINDOWS_PREBUILT_DLLS):
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
library 'status-jenkins-lib@v1.2.15'
|
library 'status-jenkins-lib@ci-macos-notarization'
|
||||||
|
|
||||||
pipeline {
|
pipeline {
|
||||||
agent {
|
agent {
|
||||||
|
@ -36,6 +36,8 @@ pipeline {
|
||||||
PATH = "${env.QTDIR}/bin:${env.PATH}"
|
PATH = "${env.QTDIR}/bin:${env.PATH}"
|
||||||
/* Control output the filename */
|
/* Control output the filename */
|
||||||
STATUS_CLIENT_DMG = "pkg/${utils.pkgFilename('dmg')}"
|
STATUS_CLIENT_DMG = "pkg/${utils.pkgFilename('dmg')}"
|
||||||
|
/* Apple Team ID for Notarization */
|
||||||
|
MACOS_NOTARIZE_TEAM_ID = "DTX7Z4U3YA"
|
||||||
}
|
}
|
||||||
|
|
||||||
stages {
|
stages {
|
||||||
|
@ -67,28 +69,16 @@ pipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Package') {
|
stage('Package') {
|
||||||
steps {
|
steps { script {
|
||||||
withCredentials([
|
macos.bundle()
|
||||||
string(
|
} }
|
||||||
credentialsId: 'macos-keychain-identity',
|
|
||||||
variable: 'MACOS_CODESIGN_IDENT'
|
|
||||||
),
|
|
||||||
string(
|
|
||||||
credentialsId: 'macos-keychain-pass',
|
|
||||||
variable: 'MACOS_KEYCHAIN_PASS'
|
|
||||||
),
|
|
||||||
file(
|
|
||||||
credentialsId: 'macos-keychain-file',
|
|
||||||
variable: 'MACOS_KEYCHAIN_FILE'
|
|
||||||
),
|
|
||||||
string(
|
|
||||||
credentialsId: utils.getInfuraTokenCred(),
|
|
||||||
variable: 'INFURA_TOKEN'
|
|
||||||
)
|
|
||||||
]) {
|
|
||||||
sh 'make pkg-macos'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stage('Notarize') {
|
||||||
|
when { expression { utils.isReleaseBuild() } }
|
||||||
|
steps { script {
|
||||||
|
macos.notarize()
|
||||||
|
} }
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Parallel Upload') {
|
stage('Parallel Upload') {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
|
@ -0,0 +1,75 @@
|
||||||
|
# Description
|
||||||
|
|
||||||
|
This document describes the process of notarizing a MacOS application.
|
||||||
|
|
||||||
|
# Notarization
|
||||||
|
|
||||||
|
The process [Software Notarization](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution) is necessary to avoid [Gatekeeper](https://en.wikipedia.org/wiki/Gatekeeper_(macOS)) warnings which look like this:
|
||||||
|
|
||||||
|
![Gatekeeper Warning](./images/gatekeeper_warning.png)
|
||||||
|
|
||||||
|
According to Apple the Notarization process will:
|
||||||
|
|
||||||
|
>Give users even more confidence in your software by submitting it to Apple to be notarized. The service automatically scans your Developer ID-signed software and performs security checks. When it's ready to export for distribution, a ticket is attached to your software to let Gatekeeper know it's been notarized.
|
||||||
|
|
||||||
|
https://developer.apple.com/developer-id/
|
||||||
|
|
||||||
|
The process involves the following steps:
|
||||||
|
|
||||||
|
>When you click Next, Xcode uploads your archive to the notary service. When the upload is complete, the notary service begins the scanning process, which usually takes less than an hour. (...) When the notarization process finishes, Xcode downloads the ticket and staples it to your archive. At that point, export your archive again to receive a distributable version of your software that includes the notary ticket.
|
||||||
|
|
||||||
|
https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution
|
||||||
|
|
||||||
|
# Tickets
|
||||||
|
|
||||||
|
>Notarization produces a ticket that tells Gatekeeper that your app is notarized. After notarization completes successfully, the next time any user attempts to run your app on macOS 10.14 or later, Gatekeeper finds the ticket online. This includes users who downloaded your app before notarization.
|
||||||
|
|
||||||
|
https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
|
||||||
|
The notarization process requires authentication.
|
||||||
|
|
||||||
|
>Add the `username` and `password` options to supply your App Store Connect credentials. Because App Store Connect now requires two-factor authentication (2FA) on all accounts, you must create an app-specific password for `altool`, as described in Using app-specific passwords.
|
||||||
|
|
||||||
|
https://support.apple.com/en-us/HT204397
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
|
||||||
|
Notarization can be performed by Xcode, or using the command line `xcrun altool` utility:
|
||||||
|
```sh
|
||||||
|
% xcrun altool --notarize-app
|
||||||
|
--primary-bundle-id "com.example.ote.zip"
|
||||||
|
--username "AC_USERNAME"
|
||||||
|
--password "@keychain:AC_PASSWORD"
|
||||||
|
--asc-provider <ProviderShortname>
|
||||||
|
--file OvernightTextEditor_11.6.8.zip
|
||||||
|
```
|
||||||
|
The request is created which has a UUID assigned to it which can be used to check progress:
|
||||||
|
```sh
|
||||||
|
% xcrun altool --notarization-info 2EFE2717-52EF-43A5-96DC-0797E4CA1041 -u "AC_USERNAME"
|
||||||
|
```
|
||||||
|
And once completed the ticket can be "stapled" to the bundle:
|
||||||
|
```
|
||||||
|
% xcrun stapler staple "Overnight TextEditor.app"
|
||||||
|
```
|
||||||
|
https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow
|
||||||
|
|
||||||
|
# Script
|
||||||
|
|
||||||
|
Our process is automated using the [`scripts/notarize-macos-pkg.sh`](../scripts/notarize-macos-pkg.sh) script, which performs all the necessary steps in CI:
|
||||||
|
```
|
||||||
|
../scripts/notarize-macos-pkg.sh pkg/StatusIm-Desktop-v0.1.0-beta.9-39582e.dmg
|
||||||
|
```
|
||||||
|
But it requires certain credentials to be provided:
|
||||||
|
|
||||||
|
* `MACOS_NOTARIZE_TEAM_ID` - Apple Team ID
|
||||||
|
* `MACOS_NOTARIZE_USERNAME` - Apple Dev Portal Username
|
||||||
|
* `MACOS_NOTARIZE_PASSWORD` - Apple Dev Portal Password or Keystore with password
|
||||||
|
|
||||||
|
# Links
|
||||||
|
|
||||||
|
* https://scriptingosx.com/2019/09/notarize-a-command-line-tool/
|
||||||
|
* https://stackoverflow.com/questions/56890749/macos-notarize-in-script
|
||||||
|
* https://github.com/rednoah/notarize-app
|
||||||
|
* https://support.apple.com/en-us/HT204397
|
|
@ -0,0 +1,70 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
[[ $(uname) != 'Darwin' ]] && { echo 'This only works on macOS.' >&2; exit 1; }
|
||||||
|
[[ $# -ne 1 ]] && { echo 'notarize-macos-pkg.sh <bundle_to_notarize>' >&2; exit 1; }
|
||||||
|
|
||||||
|
# Credential necessary for the upload.
|
||||||
|
[[ -z "${MACOS_NOTARIZE_TEAM_ID}" ]] && { echo -e "Missing env variable: MACOS_NOTARIZE_TEAM_ID" 1>&2; exit 1; }
|
||||||
|
[[ -z "${MACOS_NOTARIZE_USERNAME}" ]] && { echo -e "Missing env variable: MACOS_NOTARIZE_USERNAME" 1>&2; exit 1; }
|
||||||
|
[[ -z "${MACOS_NOTARIZE_PASSWORD}" ]] && { echo -e "Missing env variable: MACOS_NOTARIZE_PASSWORD" 1>&2; exit 1; }
|
||||||
|
|
||||||
|
# Path to MacOS bundle created by XCode.
|
||||||
|
BUNDLE_PATH="${1}"
|
||||||
|
# Notarization request check intervals/retries.
|
||||||
|
CHECK_INTERVAL_SEC="${CHECK_INTERVAL_SEC:-30}"
|
||||||
|
CHECK_RETRY_LIMIT="${CHECK_RETRY_LIMIT:-20}"
|
||||||
|
# Unique ID of MacOS application.
|
||||||
|
MACOS_BUNDLE_ID="${MACOS_BUNDLE_ID:-im.status.ethereum.desktop}"
|
||||||
|
# Log file path
|
||||||
|
NOTARIZATION_LOG="${NOTARIZATION_LOG:-${PWD}/notarization.log}"
|
||||||
|
|
||||||
|
function xcrun_altool() {
|
||||||
|
xcrun altool "${@}" \
|
||||||
|
--team-id "${MACOS_NOTARIZE_TEAM_ID}" \
|
||||||
|
--username "${MACOS_NOTARIZE_USERNAME}" \
|
||||||
|
--password "${MACOS_NOTARIZE_PASSWORD}" \
|
||||||
|
--output-format "json" \
|
||||||
|
2>&1 | tee -a "${NOTARIZATION_LOG}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Submit app for notarization. Should take 5-10 minutes.
|
||||||
|
echo -e "\n### Creating Notarization Request..."
|
||||||
|
OUT=$(xcrun_altool --notarize-app -f "${BUNDLE_PATH}" --primary-bundle-id "${MACOS_BUNDLE_ID}")
|
||||||
|
# Necessary to track notarization request progress.
|
||||||
|
REQUEST_UUID=$(echo "${OUT}" | jq -r '."notarization-upload".RequestUUID')
|
||||||
|
|
||||||
|
if [[ -z "${REQUEST_UUID}" ]]; then
|
||||||
|
echo "\n!!! FAILURE: No notarization request UUID found." >&1
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "\n### Request ID: ${REQUEST_UUID}"
|
||||||
|
|
||||||
|
# Check notarization ticket status periodically.
|
||||||
|
echo -e "\n### Checking Notarization Status..."
|
||||||
|
while sleep "${CHECK_INTERVAL_SEC}"; do
|
||||||
|
OUT=$(xcrun_altool --notarization-info "${REQUEST_UUID}")
|
||||||
|
|
||||||
|
# Once notarization is complete, run stapler and exit.
|
||||||
|
if $(echo "${OUT}" | jq -er '."notarization-info".Status == "in progress"'); then
|
||||||
|
((CHECK_RETRY_LIMIT-=1))
|
||||||
|
if [[ "${CHECK_RETRY_LIMIT}" -eq 0 ]]; then
|
||||||
|
echo -e "\n!!! FAILURE: Notarization timed out."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "In progress, sleeping ${CHECK_INTERVAL_SEC}s..."
|
||||||
|
elif $(echo "${OUT}" | jq -er '."notarization-info".Status == "success"'); then
|
||||||
|
echo -e "\n### Successful Notarization"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
echo -e "\n!!! Notariztion Error"
|
||||||
|
echo "${OUT}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Optional but preferrable to attach the ticket to the bundle.
|
||||||
|
echo -e "\n### Stapling Notarization Ticket..."
|
||||||
|
xcrun stapler staple "${BUNDLE_PATH}"
|
||||||
|
exit $?
|
Loading…
Reference in New Issue