以下脚本根据QCreator for mac工程的输出二进制文件 进行打包 部署 签名 上传公证 签章
可在工程目录下新建一个 deployed目录, 并将脚本拷贝到该目录下执行, 执行需要root权限
支持cmake工程或qmake工程
此脚本不含编译部分, 需要自行编译, 编译成功后方可成功执行
cd $projectDir && mkdir deployed
cd deployed
sudo ./notarization.sh [MinSizeRel | debug | release]
脚本需要两个证书, 分别是Developer ID Application , 对代码进行签名; Developer ID Installer 对pkg安装包进行签名, 这两个证书只有App开发者账户持有者才有权限生成.
脚本依赖工程中的一个 info.h, 该文件描述程序版本, 内容如下:
1 #ifndef INFO_H
2 #define INFO_H
3
4 #define VERSION "0.10.4"
5
6 #endif // INFO_H
7
1 #!/bin/bash
2
3 ###########################################################################
4 QDeployd="/Users/khan/Develop/Qt/5.15.2/clang_64/bin/macdeployqt"
5 BUNLDE_ID="com.cc.dd.app" # bundle id
6 USER="aa@bb.com". # apple开发者账户
7 PASSWORD="eeee-ffff-gggg-hhhh" # apple开发者专属密码(specific password) 见: https://support.apple.com/zh-cn/HT204397
8 CERTIFICATE="Developer ID Application:iiii(team id)"
9 INSTALLER_CERTIFICATE="Developer ID Installer: iiii (team id)"
10 ###########################################################################
11
12 ###########################################################################
13 COLOR_NORMAL="\033[0m"
14 COLOR_BLACK="\033[1;30m"
15 COLOR_RED="\033[1;31m"
16 COLOR_GREEN="\033[1;32m"
17 COLOR_YELLOW="\033[1;33m"
18 COLOR_BLUE="\033[1;34m"
19 COLOR_PURPLE="\033[1;35m"
20 COLOR_WHITE="\033[1;37m"
21
22 CMAKE="1"
23 projectName=""
24 targetPaths=""
25 appVersion="0.0.0"
26
27 FindRet=$(find ../ -iname "info.h")
28 if [ -n "$FindRet" ] ; then
29 appVersion=$(cat $FindRet |grep -i 'VERSION' |sed 's|.*\"\([0-9\.]*\)\".*|\1|g')
30 fi;
31
32
33 if [ -f "../CMakeLists.txt" ] ; then
34 CMAKE="1"
35 echo -e "$COLOR_PURPLE[CMake project]$COLOR_NORMAL"
36 else
37 CMAKE="0"
38 echo -e "$COLOR_PURPLE[QMake project]$COLOR_NORMAL"
39 fi;
40
41
42
43 if [ "$CMAKE" -eq "1" ] ; then
44 projectName=$(cat ../CMakeLists.txt |grep -i 'Project(' | sed 's|.*(\([a-zA-Z0-9]*\)\ .*|\1|g')
45 else
46 projectName=$(ls ../*.pro)
47 projectName=$(echo "$projectName" | sed 's|../||g' | sed 's|.pro||g' )
48 fi;
49
50 echo -e "Project Name $COLOR_PURPLE[$projectName V $appVersion]$COLOR_NORMAL"
51
52 printHelp() {
53 echo -e "USAGE: $0 $COLOR_YELLOW[target]$COLOR_NORMAL"
54 echo -e " target: $COLOR_YELLOW[debug|release]$COLOR_NORMAL"
55 echo -e " example: $0 $COLOR_YELLOW release $COLOR_NORMAL"
56 }
57
58 # echo -e "$#"
59 if [ "$#" -ne "1" ] ; then
60 printHelp
61 exit 1
62 fi;
63
64 target=$1
65
66 if [ "$CMAKE" -eq "1" ] ; then
67 data="$(cat ../CMakeLists.txt.user)"
68 count=$(xmllint --xpath 'count(//data/valuemap/valuemap/value[@key="ProjectExplorer.BuildConfiguration.BuildDirectory"])' - <<< "$data")
69 for((index=1; $index<=$count; index++)) ; do
70 tmp=$(xmllint --xpath "(//data/valuemap/valuemap/value[@key='ProjectExplorer.BuildConfiguration.BuildDirectory'])[$index]/text()" - <<< "$data")
71 targetPaths="$targetPaths$tmp\n"
72 done
73 else
74 data="$(cat ../$projectName.pro.user)"
75 count=$(xmllint --xpath 'count(//data/valuemap/valuemap/value[@key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir"])' - <<< "$data")
76 for((index=1; $index<=$count; index++)) ; do
77 tmp=$(xmllint --xpath "(//data/valuemap/valuemap/value[@key='ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir'])[$index]/text()" - <<< "$data")
78 targetPaths="$targetPaths$tmp\n"
79 done
80 fi;
81
82
83 targetPath=$(echo -e "$targetPaths" |grep -E -i "$target" | head -n 1)
84 if [ -z "$targetPath" ] ; then
85 echo -e "$projectName target$COLOR_RED dir [$target] not found$COLOR_NORMAL"
86 exit 2
87 fi
88
89 echo -e "Deployd $COLOR_PURPLE$targetPath$COLOR_NORMAL package"
90 ###########################################################################
91
92 ###########################################################################
93 DST_NAME=${projectName}_V${appVersion}.pkg
94 if [ -f $DST_NAME ] ; then
95 rm -rf $DST_NAME
96 fi
97
98 rm -rf ./$projectName.app
99 cp -R $targetPath/$projectName.app ./
100
101
102 $QDeployd $projectName.app -appstore-compliant -always-overwrite -qmldir=../Resource
103
104 find $projectName.app -name '*.dSYM'|xargs rm -rf
105
106 # 对qt模块进行签名
107 FrameworksName=$(ls $projectName.app/Contents/Frameworks/ | cut -d '.' -f 1)
108 for Item in ${FrameworksName[@]}
109 do
110 codesign --force --timestamp --entitlements Entitlements.plist --sign "$CERTIFICATE" $projectName.app/Contents/Frameworks/$Item.framework/$Item
111 done
112
113 # 对插件进行签名
114 DylibName=$(find $projectName.app/Contents/PlugIns/ -name *.dylib)
115 for Item in ${DylibName[@]}
116 do
117 codesign --force --timestamp --entitlements Entitlements.plist --sign "$CERTIFICATE" $Item
118 done
119
120 codesign --sign "$CERTIFICATE" --verbose=4 --timestamp -o runtime --entitlements Entitlements.plist $projectName.app
121
122 echo -e ""
123 codesign -dvvv $projectName.app
124 echo -e "$COLOR_PURPLE End codesign $COLOR_NORMAL\n\n"
125 ###########################################################################
126
127 ###########################################################################
128 pkgbuild --component ${projectName}.app --install-location /Applications --sign "${INSTALLER_CERTIFICATE}" --identifier "${BUNLDE_ID}" --version ${appVersion} ${DST_NAME}
129 echo -e ""
130 pkgutil --check-signature ${DST_NAME}
131 echo -e ""
132
133
134 # get TEAM_ID
135 data=$(xcrun altool --list-providers -u "$USER" -p "$PASSWORD" --output-format xml)
136 TEAM_ID=$(xmllint --xpath "//dict/array/dict[key='ProviderShortname']/key[text()='ProviderShortname']/following-sibling::*[1]/text()" - <<< "$data")
137 if [ "$TEAM_ID" ]; then
138 # notarization
139 echo -e "$COLOR_PURPLE[TEAM ID: $TEAM_ID]$COLOR_NORMAL"
140 RESULT=$(xcrun altool --notarize-app --primary-bundle-id "$BUNLDE_ID" --username "$USER" --password "$PASSWORD" --asc-provider "$TEAM_ID" -t macos --file ${DST_NAME})
141 FILTER_RESULT=$(echo -e "$RESULT" | grep RequestUUID)
142 if [ "$FILTER_RESULT" ] ; then
143 RequestUUID=$(echo -e "$RESULT" |grep RequestUUID | awk '{print $3}')
144 echo -e "$projectName $COLOR_PURPLE[RequestUUID: $RequestUUID]$COLOR_NORMAL"
145
146 sleep 150
147 xcrun altool --notarization-info "$RequestUUID" --username "$USER" --password "$PASSWORD"
148
149 xcrun stapler staple ${DST_NAME}
150 else
151 echo -e "$projectName $COLOR_RED upload ERROR:$COLOR_NORMAL \n $RESULT"
152 fi
153 fi
154 # ###########################################################################
155
156 echo 'Done.'
157
158 exit
159
签名时需要提供权限描述, 见 Entitlements.plist, 权限描述见: https://developer.apple.com/documentation/security/app_sandbox, 可自行根据程序的功能进行增减
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0">
4 <dict>
5 <key>com.apple.security.app-sandbox</key>
6 <true/>
7 <key>com.apple.security.network.client</key>
8 <true/>
9 <key>com.apple.security.device.usb</key>
10 <true/>
11 <key>com.apple.security.files.user-selected.read-write</key>
12 <true/>
13 <key>com.apple.security.files.downloads.read-write</key>
14 <true/>
15 </dict>
16 </plist>
17
脚本主要逻辑是
- 判断工程文件类型是qmake 还是cmake
- 根据工程文件 和 脚本参数 确认当前要处理的待处理程序在哪个目录 [release | debug | MinSizeRel]
- 获取当前待处理程序的版本号
- 使用application证书对framework PlugIns 进行签名; 使用application证书对app目录进行签名
- 制作pkg安装包, 并使用installer证书进行签名
- 获取开发者账户下的team id, 上传pkg进行公证
- 等待150s, 查看公证结果
- 对已公证的程序进行签章
> 签名完成后 codesign -dvvv $projectName.app 可以查看签名信息; 但此时用 spctl -a -vvvv $projectName.app 2>&1 |grep -v objc 检查应该是reject状态, 需要上传到苹果服务器进行公证
> 公证完成后在用spctl检查 才会是accepted 状态, 很多文章描述得比较粗糙. 导致有人认为签名就能accepted .
> 签章应该是用于离线认证, 非必要步骤.
整个流程的日志给大家参考一下:
1 khan@Khan-WorkMini deployd %sudo ./notarization.sh MinSizeRel
2 Password:
3 [CMake project]
4 Project Name [DonnerSynths V 0.10.4]
5 Deployd /Users/khan/Project/Cpp/build-donner-synths-Desktop_Qt_5_15_2_clang_64bit-MinSizeRel package
6 DonnerSynths.app: signed app bundle with Mach-O thin (x86_64) [com.cc.dd.app]
7
8 Executable=/Users/khan/Project/Cpp/donner-synths/deployd/DonnerSynths.app/Contents/MacOS/DonnerSynths
9 Identifier=com.cc.dd.app
10 Format=app bundle with Mach-O thin (x86_64)
11 CodeDirectory v=20500 size=9219 flags=0x10000(runtime) hashes=277+7 location=embedded
12 Hash type=sha256 size=32
13 CandidateCDHash sha256=088f2a88da85775cd13b727ff82dc6397fe45bbd
14 CandidateCDHashFull sha256=088f2a88da85775cd13b727ff82dc6397fe45bbd167801a5438ba1256534b65a
15 Hash choices=sha256
16 CMSDigest=088f2a88da85775cd13b727ff82dc6397fe45bbd167801a5438ba1256534b65a
17 CMSDigestType=2
18 CDHash=088f2a88da85775cd13b727ff82dc6397fe45bbd
19 Signature size=9023
20 Authority=Developer ID Application: iiii (team id)
21 Authority=Developer ID Certification Authority
22 Authority=Apple Root CA
23 Timestamp=Jan 7, 2022 at 10:44:53 AM
24 Info.plist entries=14
25 TeamIdentifier=team id
26 Runtime Version=12.0.0
27 Sealed Resources version=2 rules=13 files=1196
28 Internal requirements count=1 size=184
29 End codesign
30
31
32 pkgbuild: Adding component at /Users/khan/Project/Cpp/donner-synths/deployd/DonnerSynths.app
33 pkgbuild: Using timestamp authority for signature
34 pkgbuild: Signing package with identity "Developer ID Installer: iiii (team id)" from keychain /Users/khan/Library/Keychains/login.keychain-db
35 pkgbuild: Adding certificate "Developer ID Certification Authority"
36 pkgbuild: Adding certificate "Apple Root CA"
37 pkgbuild: Wrote package to DonnerSynths_V0.10.4.pkg
38
39 Package "DonnerSynths_V0.10.4.pkg":
40 Status: signed by a developer certificate issued by Apple for distribution
41 Signed with a trusted timestamp on: 2022-01-07 02:44:56 +0000
42 Certificate Chain:
43 1. Developer ID Installer: iiii (team id)
44 Expires: 2027-01-08 01:57:26 +0000
45 SHA256 Fingerprint:
46 EA B7 AC 9A 93 E8 53 36 A2 C4 C2 05 3E 25 9E 03 E4 F1 47 EB CC 56
47 A4 1A 50 81 25 7D 8E 68 4C D2
48 ------------------------------------------------------------------------
49 2. Developer ID Certification Authority
50 Expires: 2027-02-01 22:12:15 +0000
51 SHA256 Fingerprint:
52 7A FC 9D 01 A6 2F 03 A2 DE 96 37 93 6D 4A FE 68 09 0D 2D E1 8D 03
53 F2 9C 88 CF B0 B1 BA 63 58 7F
54 ------------------------------------------------------------------------
55 3. Apple Root CA
56 Expires: 2035-02-09 21:40:36 +0000
57 SHA256 Fingerprint:
58 B0 B1 73 0E CB C7 FF 45 05 14 2C 49 F1 29 5E 6E DA 6B CA ED 7E 2C
59 68 C5 BE 91 B5 A1 10 01 F0 24
60
61
62 [TEAM ID: 7FAP7F8RGQ]
63 DonnerSynths [RequestUUID: 7d500295-6fb9-42c9-9225-6392d90d79ac]
64 No errors getting notarization info.
65
66 Date: 2022-01-07 02:52:14 +0000
67 Hash: 7e67a9c9ff19ac005118979c919ad2f1c455ce76c6367f1f004eb7a8fc813c9d
68 LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma126/v4/88/86/48/88864802-9a8f-3298-dbb1-eca3bb5e0125/developer_log.json?accessKey=1641718489_3503811620240527835_%2Fx0eXAds4HsDELxjADZUUu%2BgPIcHlwg8Rjwix%2FUDtgq0EL0zekzWYRuqxVmxuMB77dWVSlrDIx8l8Tt2m%2BNS%2FFsbl5vpGv6bfKksg%2FErnX%2BI0YWo1yOVKECEBLIVS3%2B8Aeuh4zEl%2BXG1W4fddYy94YDlq6X8Jw7XnY3uDyJ1Kvw%3D
69 RequestUUID: 7d500295-6fb9-42c9-9225-6392d90d79ac
70 Status: success
71 Status Code: 0
72 Status Message: Package Approved
73
74 Processing: /Users/khan/Project/Cpp/donner-synths/deployd/DonnerSynths_V0.10.4.pkg
75 Processing: /Users/khan/Project/Cpp/donner-synths/deployd/DonnerSynths_V0.10.4.pkg
76 The staple and validate action worked!
77 Done.
2. 附录:
该脚本可根据参数选择打包成pkg 或 dmg , 并签名 公证 签章
USAGE: sudo ./deploy -t[target] -p[package] -s
-t target: [debug|release|MinSizeRel]
-p package: [dmg|pkg] 打包类型
-s sign 是否需要签名, 如携带该参数, 会自动公证签章, 否则仅完成打包动作
example: ./deploy -s -t release -p dmg
1 #!/bin/bash
2
3 ###########################################################################
4 QDeployd="/Users/khan/Develop/Qt/5.15.2/clang_64/bin/macdeployqt"
5 BUNLDE_ID="com.xxx.keyboard.app" # bundle id
6 USER="xxxx@xxx.com" # apple开发者账户
7 PASSWORD="xxxx-dddd-xxx-xxxx" # apple开发者专属密码(specific password) 见: https://support.apple.com/zh-cn/HT204397 在https://appleid.apple.com/ 申请
8 CERTIFICATE="Developer ID Application: xxxx Co.,Ltd (7FAP7F8RGQ)" # 代码签名证书, app 和 dmg需要
9 INSTALLER_CERTIFICATE="Developer ID Installer: xxxx Co.,Ltd (7FAP7F8RGQ)" # 安装包签名证书, pkg需要
10 ###########################################################################
11
12 ###########################################################################
13 COLOR_NORMAL="\033[0m"
14 COLOR_BLACK="\033[1;30m"
15 COLOR_RED="\033[1;31m"
16 COLOR_GREEN="\033[1;32m"
17 COLOR_YELLOW="\033[1;33m"
18 COLOR_BLUE="\033[1;34m"
19 COLOR_PURPLE="\033[1;35m"
20 COLOR_WHITE="\033[1;37m"
21 ###########################################################################
22
23
24 getVersion(){
25 local appVersion="0.0.0"
26 local FindRet=$(find ../ -iname "info.h")
27 if [ -n "$FindRet" ] ; then
28 appVersion=$(cat $FindRet |grep -i 'VERSION' |sed 's|.*\"\([0-9\.]*\)\".*|\1|g');
29 fi;
30 echo "$appVersion"
31 }
32
33 getProjectType(){
34 if [ -f "../CMakeLists.txt" ] ; then
35 return 1;
36 else
37 return 0;
38 fi;
39 }
40
41 getProjectName(){
42 local CMAKE=$1
43 local projectName=""
44 if [ "$CMAKE" -eq "1" ] ; then
45 projectName=$(cat ../CMakeLists.txt |grep -i 'Project(' | sed 's|.*(\([a-zA-Z0-9]*\)\ .*|\1|g');
46 else
47 projectName=$(ls ../*.pro)
48 projectName=$(echo "$projectName" | sed 's|../||g' | sed 's|.pro||g' );
49 fi;
50 echo "$projectName"
51 }
52
53 getAppPath(){
54 local CMAKE=$1
55 local projectName=$2
56 local target=$3
57
58 local targetPaths=""
59 if [ "$CMAKE" -eq "1" ] ; then
60 data="$(cat ../CMakeLists.txt.user)"
61 count=$(xmllint --xpath 'count(//data/valuemap/valuemap/value[@key="ProjectExplorer.BuildConfiguration.BuildDirectory"])' - <<< "$data")
62 for((index=1; $index<=$count; index++)) ; do
63 tmp=$(xmllint --xpath "(//data/valuemap/valuemap/value[@key='ProjectExplorer.BuildConfiguration.BuildDirectory'])[$index]/text()" - <<< "$data")
64 targetPaths="$targetPaths$tmp\n"
65 done;
66 else
67 data="$(cat ../$projectName.pro.user)"
68 count=$(xmllint --xpath 'count(//data/valuemap/valuemap/value[@key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir"])' - <<< "$data")
69 for((index=1; $index<=$count; index++)) ; do
70 tmp=$(xmllint --xpath "(//data/valuemap/valuemap/value[@key='ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir'])[$index]/text()" - <<< "$data")
71 targetPaths="$targetPaths$tmp\n"
72 done;
73 fi;
74
75 local targetPath=$(echo -e "$targetPaths" |grep -E -i "$target" | head -n 1)
76 echo "$targetPath";
77 }
78
79 printHelp() {
80 echo -e "USAGE: $0 -t$COLOR_YELLOW[target]$COLOR_NORMAL -p$COLOR_YELLOW[package]$COLOR_NORMAL -s"
81 echo -e " -t target: $COLOR_YELLOW[debug|release|MinSizeRel]$COLOR_NORMAL"
82 echo -e " -p package: $COLOR_YELLOW[dmg|pkg|zip]$COLOR_NORMAL"
83 echo -e " -s sign"
84 echo -e " example: $0 -s -t$COLOR_YELLOW release $COLOR_NORMAL-p$COLOR_YELLOW dmg$COLOR_NORMAL"
85 }
86
87
88 unMountDmg(){
89 DMG_NAME=$1
90 data=$(hdiutil info -plist)
91 VOL_DEVICE=$(xmllint --xpath "//dict[contains(., '$DMG_NAME') and key='image-path']/array/dict[key='dev-entry' and contains(., 'GUID_partition_scheme')]/key[text()='dev-entry']/following-sibling::*[1]/text()" - 2>/dev/null <<< "$data")
92 if [ "$VOL_DEVICE" ]; then
93 echo -e "Unmount DMG$COLOR_PURPLE $VOL_DEVICE $COLOR_NORMAL"
94 hdiutil detach $VOL_DEVICE;
95 fi
96 }
97
98 signApp(){
99 local appPath=$1
100 local certificate=$2
101 local entitlementsPath=$3
102 # 对qt模块进行签名
103 local FrameworksName=$(ls $appPath/Contents/Frameworks/ | cut -d '.' -f 1)
104 for Item in ${FrameworksName[@]}
105 do
106 codesign --force --timestamp --entitlements $entitlementsPath --sign "$certificate" $appPath/Contents/Frameworks/$Item.framework/$Item;
107 done;
108
109 # 对插件进行签名
110 local DylibName=$(find $appPath/Contents/PlugIns/ -name *.dylib)
111 for Item in ${DylibName[@]}
112 do
113 codesign --force --timestamp --entitlements $entitlementsPath --sign "$CERTIFICATE" $Item;
114 done;
115
116 codesign --sign "$CERTIFICATE" --verbose=4 --timestamp -o runtime --entitlements $entitlementsPath $appPath
117
118 echo -e ""
119 codesign -dvvv $appPath
120 echo -e "${COLOR_PURPLE}End codesign ${COLOR_NORMAL}\n\n"
121 }
122
123 makeDmg(){
124 local appName=$1
125 local certificate=$2
126 local bundleId=$3
127 local DMG_BACKGROUND_IMG=$4
128
129
130 local VOL_NAME="${appName}"
131 local APP_EXE="${appName}.app/Contents/MacOS/${appName}"
132
133 local DMG_TMP="${appName}-rw.dmg"
134 local DMG_FINAL="${appName}.dmg"
135 local STAGING_DIR="./Install"
136
137
138 # Check the background image DPI and convert it if it isn't 72x72
139 local _BACKGROUND_IMAGE_DPI_H=`sips -g dpiHeight ${DMG_BACKGROUND_IMG} | grep -Eo '[0-9]+\.[0-9]+'`
140 local _BACKGROUND_IMAGE_DPI_W=`sips -g dpiWidth ${DMG_BACKGROUND_IMG} | grep -Eo '[0-9]+\.[0-9]+'`
141
142
143 if [ $(echo " $_BACKGROUND_IMAGE_DPI_H != 72.0 " | bc) -eq 1 -o $(echo " $_BACKGROUND_IMAGE_DPI_W != 72.0 " | bc) -eq 1 ]; then
144 echo "WARNING: The background image's DPI is not 72. This will result in distorted backgrounds on Mac OS X 10.7+."
145 echo " I will convert it to 72 DPI for you."
146
147 local _DMG_BACKGROUND_TMP="${DMG_BACKGROUND_IMG%.*}"_dpifix."${DMG_BACKGROUND_IMG##*.}"
148 sips -s dpiWidth 72 -s dpiHeight 72 ${DMG_BACKGROUND_IMG} --out ${_DMG_BACKGROUND_TMP}
149 DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}";
150 fi
151
152 # clear out any old data
153 rm -rf "${STAGING_DIR}" "${DMG_TMP}" "${DMG_FINAL}"
154
155 # copy over the stuff we want in the final disk image to our staging dir
156 mkdir -p "${STAGING_DIR}"
157 cp -Rpf "${projectName}.app" "${STAGING_DIR}" # -R复制软链接 -p保留源文件修改时间和权限 -f强制覆盖
158
159 # figure out how big our DMG needs to be
160 # assumes our contents are at least 1M!
161 local SIZE=`du -sh "${STAGING_DIR}" | sed 's/\([0-9]*\)M\(.*\)/\1/'`
162 # echo -e "DMG size: ${SIZE}"
163 SIZE=`echo "${SIZE} + 1.0" | bc | awk '{print int($1*1.5)}'`
164 echo -e "DMG size: ${SIZE}"
165
166 if [ $? -ne 0 ]; then
167 echo "Error: Cannot compute size of staging dir"
168 exit;
169 fi
170
171 # create the temp DMG file
172 hdiutil create -srcfolder "${STAGING_DIR}" -volname "${VOL_NAME}" -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "${DMG_TMP}"
173
174 echo "Created DMG: ${DMG_TMP}"
175
176 # mount it and save the device
177 local DEVICE=$(hdiutil attach -readwrite -noverify "${DMG_TMP}" | egrep '^/dev/' | sed 1q | awk '{print $1}')
178
179 sleep 3
180
181 # add a link to the Applications dir
182 echo "Add link to /Applications"
183 pushd /Volumes/"${VOL_NAME}"
184 ln -s /Applications
185 popd
186
187 # add a background image
188 mkdir /Volumes/"${VOL_NAME}"/.background
189 cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
190
191 # tell the Finder to resize the window, set the background,
192 # change the icon size, place the icons in the right position, etc.
193 echo '
194 tell application "Finder"
195 tell disk "'${VOL_NAME}'"
196 open
197 set current view of container window to icon view
198 set toolbar visible of container window to false
199 set statusbar visible of container window to false
200 set the bounds of container window to {400, 100, 920, 460}
201 set viewOptions to the icon view options of container window
202 set arrangement of viewOptions to not arranged
203 set icon size of viewOptions to 72
204 set background picture of viewOptions to file ".background:'${DMG_BACKGROUND_IMG}'"
205 set position of item "'${appName}'.app" of container window to {160, 205}
206 set position of item "Applications" of container window to {360, 205}
207 close
208 open
209 update without registering applications
210 delay 2
211 end tell
212 end tell
213 ' | osascript
214
215 sync
216
217 # unmount it
218 hdiutil detach "${DEVICE}"
219
220 sleep 2
221
222 # now make the final image a compressed disk image
223 echo "Creating compressed image"
224 hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${VOL_NAME}_V${appVersion}.dmg"
225
226 codesign -s "${certificate}" "${VOL_NAME}_V${appVersion}.dmg" -i "${bundleId}" --options runtime
227
228 # clean up
229 rm -rf "${DMG_TMP}"
230 rm -rf "${STAGING_DIR}"
231 rm -f Applications
232 }
233
234 makePkg(){
235 local appName=$1
236 local certificate=$2
237 local bundleId=$3
238 local version=$4
239 local DST_NAME=$5
240 pkgbuild --component ${appName}.app --install-location /Applications --sign "${certificate}" --identifier "${bundleId}" --version ${version} ${DST_NAME}
241 echo -e ""
242 pkgutil --check-signature ${DST_NAME}
243 echo -e ""
244 }
245
246 makeZip(){
247 local appName=$1
248 local certificate=$2
249 local bundleId=$3
250 local DST_NAME=$4
251 # -y 或 --symlinks 存储软链接; -r 压缩目录及子目录; -q 静默模式
252 zip --symlinks -q -r ${DST_NAME} ${appName}.app
253
254 codesign -s "${certificate}" "${DST_NAME}" -i "${bundleId}" --options runtime
255
256 echo -e ""
257 codesign -dvvv "${DST_NAME}"
258 echo -e "${COLOR_PURPLE}End codesign ${COLOR_NORMAL}\n\n"
259 }
260
261 notarization(){
262 local appName=$1
263 local USER=$2
264 local PASSWORD=$3
265 local DST_NAME=$4
266 # get TEAM_ID
267 local data=$(xcrun altool --list-providers -u "$USER" -p "$PASSWORD" --output-format xml)
268 local TEAM_ID=$(xmllint --xpath "//dict/array/dict[key='ProviderShortname']/key[text()='ProviderShortname']/following-sibling::*[1]/text()" - <<< "$data")
269 if [ "$TEAM_ID" ]; then
270 # notarization
271 echo -e "$COLOR_PURPLE[TEAM ID: $TEAM_ID]$COLOR_NORMAL"
272 local RESULT=$(xcrun altool --notarize-app --primary-bundle-id "$BUNLDE_ID" --username "$USER" --password "$PASSWORD" --asc-provider "$TEAM_ID" -t macos --file ${DST_NAME})
273 local FILTER_RESULT=$(echo -e "$RESULT" | grep RequestUUID)
274 if [ "$FILTER_RESULT" ] ; then
275 local RequestUUID=$(echo -e "$RESULT" |grep RequestUUID | awk '{print $3}')
276 echo -e "$projectName $COLOR_PURPLE[RequestUUID: $RequestUUID]$COLOR_NORMAL"
277
278 sleep 150
279 xcrun altool --notarization-info "$RequestUUID" --username "$USER" --password "$PASSWORD"
280 ## 文件名全部转成大写;
281 local TMP01=$(echo "$DST_NAME" | tr '[:lower:]' '[:upper:]')
282 local TMP02=$( echo "$TMP01" | grep -E '\.ZIP$')
283 if [ "$TMP02" ]; then
284 echo "The validate action worked!"
285 else
286 xcrun stapler staple ${DST_NAME}
287 xcrun stapler validate ${DST_NAME};
288 fi
289
290 else
291 echo -e "$projectName $COLOR_RED upload ERROR:$COLOR_NORMAL \n $RESULT";
292 fi;
293 fi;
294 }
295
296 ###########################################################################
297 isSign="no" ## 是否需要签名
298 FLAG="0"
299 package="dmg"
300 while getopts 't:p:vsh' OPT; do
301 case $OPT in
302 t) ## 打包目标 [debug|release|MinSizeRel]
303 target="$OPTARG";;
304 p) ## 打包类型 [dmg|pkg|zip]
305 package="$OPTARG";;
306 s) ## 是否签名, 默认不签名
307 isSign="yes";;
308 v)
309 echo -e $0$COLOR_PURPLE v$(getVersion)$COLOR_NORMAL;;
310 h) ## 帮助
311 printHelp
312 exit 0;;
313 ?)
314 printHelp
315 exit 1;;
316 esac
317 FLAG="1";
318 done
319 shift $(($OPTIND - 1))
320
321 if [ "$FLAG" -eq "0" ]; then
322 printHelp
323 exit 1
324 fi;
325 ###########################################################################
326
327 ###########################################################################
328 # 临时全局变量
329 getProjectType
330 projectType=$? # 工程类型 cmake 或 qmake
331 projectName=$(echo $(getProjectName $projectType)) # 项目名
332 appVersion=$(echo $(getVersion)) # 版本号
333 targetPath=$(echo $(getAppPath $projectType $projectName $target)) # 工程二进制输出目录
334 ###########################################################################
335
336
337 if [ "$projectType" = 1 ] ; then
338 echo -e "$COLOR_PURPLE[CMake project]$COLOR_NORMAL";
339 else
340 echo -e "$COLOR_PURPLE[QMake project]$COLOR_NORMAL";
341 fi
342
343 echo -e "Deployd $COLOR_PURPLE${targetPath}$COLOR_NORMAL $COLOR_YELLOW${projectName} v${appVersion}$COLOR_NORMAL ${package} package"
344
345 rm -rf ./$projectName.app
346 cp -R ${targetPath}/${projectName}.app ./
347
348 $QDeployd $projectName.app -always-overwrite -appstore-compliant -qmldir=../Resource
349 find $projectName.app -name '*.dSYM'|xargs rm -rf
350
351 if [ "$package" = "dmg" ] ; then
352 DST_NAME=${projectName}_V${appVersion}.dmg
353
354 unMountDmg $DST_NAME
355 if [ -f $DST_NAME ] ; then
356 rm -rf $DST_NAME;
357 fi
358
359 if [ "$isSign" = "yes" ] ; then
360 signApp "$projectName.app" "${CERTIFICATE}" "Entitlements.plist" ;
361 fi
362 makeDmg "$projectName" "${CERTIFICATE}" "${BUNLDE_ID}" "background.png"
363
364 # 已签名程序才可公证
365 if [ "$isSign" = "yes" ] ; then
366 notarization "$projectName" "${USER}" "${PASSWORD}" "${DST_NAME}";
367 fi
368
369 echo 'Done.';
370 elif [ "$package" = "pkg" ] ; then
371 DST_NAME=${projectName}_V${appVersion}.pkg
372 if [ -f $DST_NAME ] ; then
373 rm -rf $DST_NAME;
374 fi
375
376 if [ "$isSign" = "yes" ] ; then
377 signApp "$projectName.app" "${CERTIFICATE}" "Entitlements.plist" ;
378 fi;
379 makePkg "$projectName" "${INSTALLER_CERTIFICATE}" "${BUNLDE_ID}" "${appVersion}" "${DST_NAME}"
380
381 # 已签名程序才可公证
382 if [ "$isSign" = "yes" ] ; then
383 notarization "$projectName" "${USER}" "${PASSWORD}" "${DST_NAME}";
384 fi
385
386 echo 'Done.';
387 elif [ "$package" = "zip" ] ; then
388 DST_NAME=${projectName}_V${appVersion}.zip
389 if [ -f $DST_NAME ] ; then
390 rm -rf $DST_NAME;
391 fi
392 if [ "$isSign" = "yes" ] ; then
393 signApp "$projectName.app" "${CERTIFICATE}" "Entitlements.plist" ;
394 fi
395
396 makeZip "$projectName" "${CERTIFICATE}" "${BUNLDE_ID}" "${DST_NAME}"
397 # 已签名程序才可公证
398 if [ "$isSign" = "yes" ] ; then
399 notarization "$projectName" "${USER}" "${PASSWORD}" "${DST_NAME}";
400 fi
401
402 echo 'Done.';
403 else
404 printHelp
405 exit 1;
406 fi;