Skip to content

Commit 0cde9ac

Browse files
committed
Notarize and staple the installer pkg on macOS
Enable installation and executable on macOS Catalina (10.15) now that Gate Keeper requires notarization. We staple the notarization ticket to the package too to allow for offline installs.
1 parent d14c952 commit 0cde9ac

File tree

6 files changed

+222
-33
lines changed

6 files changed

+222
-33
lines changed

.azure-pipelines/release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,12 @@ jobs:
4747
name: VSEng-MicroBuildVS2017
4848
steps:
4949
- template: templates/osx/pack.signed/step4-signpack.yml
50+
51+
- job: osx_step5
52+
displayName: macOS (Prepare for distribution)
53+
dependsOn: osx_step4
54+
condition: succeeded()
55+
pool:
56+
vmImage: macOS 10.13
57+
steps:
58+
- template: templates/osx/pack.signed/step5-dist.yml

.azure-pipelines/templates/osx/compile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ steps:
1919
projects: 'Git-Credential-Manager.sln'
2020
arguments: '--configuration=Mac$(configuration)'
2121
publishTestResults: true
22-
#testRunTitle: 'Unit tests - common (macOS)' # option not yet available
22+
testRunTitle: 'Unit tests - common (macOS)'

.azure-pipelines/templates/osx/pack.signed/step4-signpack.yml

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,8 @@ steps:
3030
Remove-Item $dir\gcmcorepkg.zip -Force
3131
displayName: 'Unzip signed package file'
3232
33-
- task: DownloadPipelineArtifact@1
34-
displayName: Download signed payload
35-
inputs:
36-
buildType: 'current'
37-
artifactName: 'tmp.macpayload_signed'
38-
downloadPath: '$(Build.StagingDirectory)\payload'
39-
40-
- task: DownloadPipelineArtifact@1
41-
displayName: Download symbols
42-
inputs:
43-
buildType: 'current'
44-
artifactName: 'tmp.macsymbols'
45-
downloadPath: '$(Build.StagingDirectory)\symbols'
46-
47-
- script: |
48-
xcopy "$(Build.StagingDirectory)\pkg\*.pkg" "$(Build.StagingDirectory)\publish\"
49-
xcopy "$(Build.StagingDirectory)\payload" "$(Build.StagingDirectory)\publish\payload\"
50-
xcopy "$(Build.StagingDirectory)\symbols" "$(Build.StagingDirectory)\publish\payload.sym\"
51-
displayName: Prepare final build artifact
52-
5333
- task: PublishPipelineArtifact@0
54-
displayName: Publish signed installer artifacts
34+
displayName: Upload signed installer
5535
inputs:
56-
artifactName: 'Installer.Mac.Signed'
57-
targetPath: '$(Build.StagingDirectory)\publish'
36+
artifactName: 'tmp.macinstaller_signed'
37+
targetPath: '$(Build.StagingDirectory)\pkg'
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
steps:
2+
- task: DownloadPipelineArtifact@1
3+
displayName: Download signed installer
4+
inputs:
5+
buildType: 'current'
6+
artifactName: 'tmp.macinstaller_signed'
7+
downloadPath: '$(Build.StagingDirectory)\pkg'
8+
9+
- task: DownloadPipelineArtifact@1
10+
displayName: Download signed payload
11+
inputs:
12+
buildType: 'current'
13+
artifactName: 'tmp.macpayload_signed'
14+
downloadPath: '$(Build.StagingDirectory)\payload'
15+
16+
- task: DownloadPipelineArtifact@1
17+
displayName: Download symbols
18+
inputs:
19+
buildType: 'current'
20+
artifactName: 'tmp.macsymbols'
21+
downloadPath: '$(Build.StagingDirectory)\symbols'
22+
23+
- script: src/osx/SignFiles.Mac/notarize-pkg.sh -id "$(AppleId)" -p "$(AppleIdPassword)" -pkg '$(Build.StagingDirectory)\pkg\*.pkg'
24+
displayName: Notarize and staple installer package
25+
26+
- script: |
27+
cp "$(Build.StagingDirectory)/pkg/*.pkg" "$(Build.StagingDirectory)/publish/"
28+
cp "$(Build.StagingDirectory)/payload" "$(Build.StagingDirectory)/publish/payload/"
29+
cp "$(Build.StagingDirectory)/symbols" "$(Build.StagingDirectory)/publish/payload.sym/"
30+
displayName: Prepare final build artifact
31+
32+
- task: PublishPipelineArtifact@0
33+
displayName: Publish signed installer artifacts
34+
inputs:
35+
artifactName: 'Installer.Mac.Signed'
36+
targetPath: '$(Build.StagingDirectory)/publish'

.azure-pipelines/templates/windows/compile.yml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ steps:
3838
otherConsoleOptions: '/Framework:.NETCoreApp,Version=2.1'
3939
testRunTitle: 'Unit tests - common (Windows)'
4040

41-
- task: VSTest@2
42-
displayName: Run helpers unit tests
43-
inputs:
44-
testAssemblyVer2: |
45-
out\windows\*.Tests\bin\**\*.Tests.dll
46-
configuration: 'Windows$(configuration)'
47-
otherConsoleOptions: '/Framework:.NETFramework,Version=v4.6.1'
48-
testRunTitle: 'Unit tests - helpers (Windows)'
49-
continueOnError: true
41+
# Uncomment once Windows helpers have unit tests
42+
# - task: VSTest@2
43+
# displayName: Run helpers unit tests
44+
# inputs:
45+
# testAssemblyVer2: |
46+
# out\windows\*.Tests\bin\**\*.Tests.dll
47+
# configuration: 'Windows$(configuration)'
48+
# otherConsoleOptions: '/Framework:.NETFramework,Version=v4.6.1'
49+
# testRunTitle: 'Unit tests - helpers (Windows)'
50+
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#!/bin/bash
2+
3+
# This file was based on https://github.com/microsoft/BuildXL/blob/8c2348ff04e6ca78726bb945fb2a0f6a55a5c7d6/Private/macOS/notarize.sh
4+
#
5+
# For detailed explanation see: https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow
6+
7+
usage() {
8+
cat <<EOM
9+
$(basename $0) - Handy script to notarize an installer package (.pkg)
10+
Usage: $(basename $0) -id <apple_id> -p <password> -pkg <path_to_pkg>
11+
-id or --appleid # A valid Apple ID email address, account must have correct certificates available
12+
-p or --password # The password for the specified Apple ID or Apple One-Time password (to avoid 2FA)
13+
-pkg or --package # The path to an already signed flat-package
14+
EOM
15+
exit 0
16+
}
17+
18+
declare arg_AppleId=""
19+
declare arg_Password=""
20+
declare arg_PackagePath=""
21+
22+
[ $# -eq 0 ] && { usage; }
23+
24+
function parseArgs() {
25+
arg_Positional=()
26+
while [[ $# -gt 0 ]]; do
27+
cmd="$1"
28+
case $cmd in
29+
--help | -h)
30+
usage
31+
shift
32+
exit 0
33+
;;
34+
--appleid | -id)
35+
arg_AppleId=$2
36+
shift
37+
;;
38+
--password | -p)
39+
arg_Password="$2"
40+
shift
41+
;;
42+
--package | -pkg)
43+
arg_PackagePath="$2"
44+
shift
45+
;;
46+
*)
47+
arg_Positional+=("$1")
48+
shift
49+
;;
50+
esac
51+
done
52+
}
53+
54+
function getPackageId {
55+
local PKG=$(cd "$(dirname "$1")"; pwd)/$(basename "$1")
56+
local PKGDEST=$(mktemp -d | tr -d '\r')
57+
xar -x -f "${PKG}" --exclude '^(?:(?!PackageInfo).)*$' -C "${PKGDEST}"
58+
if [ ! -e "${PKGDEST}/PackageInfo" ]; then
59+
echo "error: can't find 'PackageInfo'; maybe meta-package"
60+
return 1
61+
fi
62+
cat "${PKGDEST}/PackageInfo" | tr -d '\r' | tr -d '\n' | sed 's:^.*identifier="\([^"]*\)".*$:\1:g'
63+
rm -rf "${PKGDEST}"
64+
}
65+
66+
parseArgs "$@"
67+
68+
if [[ -z $arg_AppleId ]]; then
69+
echo "[ERROR] Must supply valid / non-empty Apple ID!"
70+
exit 1
71+
fi
72+
73+
if [[ -z $arg_Password ]]; then
74+
echo "[ERROR] Must supply valid / non-empty password!"
75+
exit 1
76+
fi
77+
78+
if [[ ! -f "$arg_PackagePath" ]]; then
79+
echo "[ERROR] Must supply valid / non-empty path to package!"
80+
exit 1
81+
fi
82+
83+
declare bundle_id=$(getPackageId ${arg_PackagePath})
84+
85+
if [[ -z $bundle_id ]]; then
86+
echo "[ERROR] No identifier found in package info!"
87+
exit 1
88+
fi
89+
90+
echo "Notarizating $arg_PackagePath"
91+
92+
echo -e "Current state:\n"
93+
xcrun stapler validate -v "$arg_PackagePath"
94+
95+
if [[ $? -eq 0 ]]; then
96+
echo "$arg_PackagePath already notarized and stapled, nothing to do!"
97+
exit 0
98+
fi
99+
100+
set -e
101+
102+
declare start_time=$(date +%s)
103+
104+
declare output="/tmp/progress.xml"
105+
106+
echo "Uploading package to notarization service, please wait..."
107+
xcrun altool --notarize-app -t osx -f $arg_PackagePath --primary-bundle-id $bundle_id -u $arg_AppleId -p $arg_Password --output-format xml | tee $output
108+
109+
declare request_id=$(/usr/libexec/PlistBuddy -c "print :notarization-upload:RequestUUID" $output)
110+
111+
echo "Checking notarization request validity..."
112+
if [[ $request_id =~ ^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$ ]]; then
113+
declare attempts=5
114+
115+
while :
116+
do
117+
echo "Waiting a bit before checking on notarization status again..."
118+
119+
sleep 20
120+
xcrun altool --notarization-info $request_id -u $arg_AppleId -p $arg_Password --output-format xml | tee $output
121+
122+
declare status=$(/usr/libexec/PlistBuddy -c "print :notarization-info:Status" $output)
123+
echo "Status: $status"
124+
125+
if [[ -z $status ]]; then
126+
echo "Left attempts: $attempts"
127+
128+
if (($attempts <= 0)); then
129+
break
130+
fi
131+
132+
((attempts--))
133+
else
134+
if [[ $status != "in progress" ]]; then
135+
break
136+
fi
137+
fi
138+
done
139+
140+
declare end_time=$(date +%s)
141+
echo -e "Completed in $(($end_time-$start_time)) seconds\n"
142+
143+
if [[ "$status" != "success" ]]; then
144+
echo "Error notarizing, exiting..." >&2
145+
exit 1
146+
else
147+
declare url=$(/usr/libexec/PlistBuddy -c "print :notarization-info:LogFileURL" $output)
148+
149+
if [ "$url" ]; then
150+
curl $url
151+
fi
152+
153+
# Staple the ticket to the package
154+
xcrun stapler staple "$arg_PackagePath"
155+
156+
echo -e "State after notarization:\n"
157+
xcrun stapler validate -v "$arg_PackagePath"
158+
echo -e "Stapler exit code: $? (must be zero on success!)\n"
159+
fi
160+
else
161+
echo "Invalid request id found in 'altool' output, aborting!" >&2
162+
exit 1
163+
fi

0 commit comments

Comments
 (0)