以下腳本根據(jù)QCreator for mac工程的輸出二進(jìn)制文件 進(jìn)行打包 部署 簽名 上傳公證 簽章
可在工程目錄下新建一個(gè) deployed目錄, 并將腳本拷貝到該目錄下執(zhí)行, 執(zhí)行需要root權(quán)限
支持cmake工程或qmake工程
此腳本不含編譯部分, 需要自行編譯, 編譯成功后方可成功執(zhí)行
cd $projectDir && mkdir deployed
cd deployed
sudo ./notarization.sh [MinSizeRel | debug | release]
腳本需要兩個(gè)證書, 分別是Developer ID Application , 對代碼進(jìn)行簽名; Developer ID Installer 對pkg安裝包進(jìn)行簽名, 這兩個(gè)證書只有App開發(fā)者賬戶持有者才有權(quán)限生成.
腳本依賴工程中的一個(gè) info.h, 該文件描述程序版本, 內(nèi)容如下:
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開發(fā)者賬戶
7 PASSWORD="eeee-ffff-gggg-hhhh" # apple開發(fā)者專屬密碼(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模塊進(jìn)行簽名
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 # 對插件進(jìn)行簽名
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
簽名時(shí)需要提供權(quán)限描述, 見 Entitlements.plist, 權(quán)限描述見: https://developer.apple.com/documentation/security/app_sandbox, 可自行根據(jù)程序的功能進(jìn)行增減
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
- 根據(jù)工程文件 和 腳本參數(shù) 確認(rèn)當(dāng)前要處理的待處理程序在哪個(gè)目錄 [release | debug | MinSizeRel]
- 獲取當(dāng)前待處理程序的版本號
- 使用application證書對framework PlugIns 進(jìn)行簽名; 使用application證書對app目錄進(jìn)行簽名
- 制作pkg安裝包, 并使用installer證書進(jìn)行簽名
- 獲取開發(fā)者賬戶下的team id, 上傳pkg進(jìn)行公證
- 等待150s, 查看公證結(jié)果
- 對已公證的程序進(jìn)行簽章
> 簽名完成后 codesign -dvvv $projectName.app 可以查看簽名信息; 但此時(shí)用 spctl -a -vvvv $projectName.app 2>&1 |grep -v objc 檢查應(yīng)該是reject狀態(tài), 需要上傳到蘋果服務(wù)器進(jìn)行公證
> 公證完成后在用spctl檢查 才會是accepted 狀態(tài), 很多文章描述得比較粗糙. 導(dǎo)致有人認(rèn)為簽名就能accepted .
> 簽章應(yīng)該是用于離線認(rèn)證, 非必要步驟.
整個(gè)流程的日志給大家參考一下:
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. 附錄:
該腳本可根據(jù)參數(shù)選擇打包成pkg 或 dmg , 并簽名 公證 簽章
USAGE: sudo ./deploy -t[target] -p[package] -s
-t target: [debug|release|MinSizeRel]
-p package: [dmg|pkg] 打包類型
-s sign 是否需要簽名, 如攜帶該參數(shù), 會自動(dòng)公證簽章, 否則僅完成打包動(dòng)作
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開發(fā)者賬戶
7 PASSWORD="xxxx-dddd-xxx-xxxx" # apple開發(fā)者專屬密碼(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模塊進(jìn)行簽名
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 # 對插件進(jìn)行簽名
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復(fù)制軟鏈接 -p保留源文件修改時(shí)間和權(quán)限 -f強(qiáng)制覆蓋
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 ## 文件名全部轉(zhuǎn)成大寫;
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) ## 打包目標(biāo) [debug|release|MinSizeRel]
303 target="$OPTARG";;
304 p) ## 打包類型 [dmg|pkg|zip]
305 package="$OPTARG";;
306 s) ## 是否簽名, 默認(rèn)不簽名
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 # 臨時(shí)全局變量
329 getProjectType
330 projectType=$? # 工程類型 cmake 或 qmake
331 projectName=$(echo $(getProjectName $projectType)) # 項(xiàng)目名
332 appVersion=$(echo $(getVersion)) # 版本號
333 targetPath=$(echo $(getAppPath $projectType $projectName $target)) # 工程二進(jìn)制輸出目錄
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;