theme: smartblue
在以前的 《 Android 和 iOS 打包提交審核指南》 里介紹了 Flutter 下打包 Android 和 iOS 的指南蛀骇,不過這部分內(nèi)容主要介紹的是如何在本地打包發(fā)布流程。
但事實(shí)上一般的產(chǎn)品發(fā)布流程檐晕,都會有專門的機(jī)器用于打包服務(wù),在統(tǒng)一干凈的環(huán)境下進(jìn)行打包更有利于發(fā)布的管理个榕,避免各種本地環(huán)境差異問題西采。
當(dāng)然大多數(shù)時(shí)候可以直接使用第三方的 CI 服務(wù)械馆,但是專門支持 Flutter 的第三方服務(wù)并不多霹崎,并且自己動(dòng)手還免費(fèi)仿畸,所以本篇主要介紹自己搭建獨(dú)立打包服務(wù)的過程错沽。
由于 Android 的命令打包服務(wù)比較簡單千埃,這里主要介紹配置搭建 iOS 下的 Flutter 打包和發(fā)布 CI 放可,其實(shí)主要也是 iOS 的 CI 。
一拾氓、參數(shù)支持
首先在 iOS 上很多的配置信息都是寫在 info.plist
文件房官,所以一開始需要解決打包時(shí)支持動(dòng)態(tài)修改 info.plist
的參數(shù),這樣有利于我們在輸出不同環(huán)境的包配置孵奶,如:QA了袁、Release早像、Dev 等等。
/usr/libexec/PlistBuddy -c "Set CFBundleVersion ${CFBundleVersion}" ./Runner/Info.plist
/usr/libexec/PlistBuddy -c "Print CFBundleVersion " ./Runner/Info.plist
在 Mac 上其實(shí)本身就自帶了滿足需求的命令行工具:PlistBuddy
劝堪, 如上命令所示
- 通過
Set
命令可以直接動(dòng)態(tài)配置plist
下的版本號秒啦、 code 和第三方 App Id 等相關(guān)配置余境; - 通過
Print
命令直接輸出對應(yīng)的plist
信息芳来;
完成 plist
配置的支持即舌, 接下來就需要在機(jī)器上配置開發(fā)者信息,最簡單的做法就是打開 Xcode 然后直接登陸上開發(fā)者賬號盯仪,通過賬號直接讓 Xcode 的 Automatically manage signing
幫助我們完成整個(gè)開發(fā)信息的配置過程耀石。
但是我個(gè)人不推薦這種方式娶牌,打包機(jī)器本身可能會涉及多個(gè)項(xiàng)目組使用诗良,都把自己的開發(fā)賬號登陸在一個(gè)公用機(jī)器上存在風(fēng)險(xiǎn)鉴裹,而且多個(gè)賬號同時(shí)登陸容易混亂径荔,最后直接登陸也不利于證書和描述和管理总处。
所以要實(shí)現(xiàn)一個(gè)較為安全和通用的服務(wù)鹦马,這里比較推薦:通過在機(jī)器上配置證書和 mobile provision 等文件的方式來完成打包認(rèn)證荸频。
二旭从、手動(dòng)配置證書
手動(dòng)配置證書和 mobile provision 會比較麻煩和悦,但是它可以讓服務(wù)更加通用蹄咖,也讓你更熟悉 iOS 打包的流程澜汤。
1俊抵、首先通過本地鑰匙串創(chuàng)建 CertificateSigningRequest.certSigningRequest
文件徽诲,如圖所示自動(dòng)生成就可以了谎替。
2钱贯、在蘋果官方的 developer 上點(diǎn)擊創(chuàng)建證書秩命,上傳步驟 1 中的 CertificateSigningRequest.certSigningRequest
文件弃锐,然后下載 .cer
證書文件霹菊。
3券敌、這里需要注意不能直接把這個(gè) .cer
證書文件安裝到打包服務(wù)上柳洋,而是把這個(gè) .cer
先安裝到上面第 1 步中生成的 CertificateSigningRequest.certSigningRequest
的機(jī)器上熊镣,然后通過導(dǎo)出證書生成帶有密碼的 p12
證書文件,這個(gè)文件才是可以安裝到打包機(jī)器上的證書文件莹捡。
4篮赢、安裝證書涣脚,把 p12
文件放置到打包服務(wù)上寥茫,然后點(diǎn)擊證書,輸入 3 中創(chuàng)建時(shí)輸入的密碼险耀,安裝到鑰匙串的 “登陸” 甩牺,這時(shí)候就可以看到鑰匙串證書里帶有 TeamId 的 Apple Distribution 證書柴灯。
5赠群、需要額外注意安裝后可能會看到說“證書不受信任”的提示查描,這可能是因?yàn)闄C(jī)器上缺少 AppleWWDRCA (Apple Worldwide Developer Relations Certification Authority)證書冬三,可以通過下面的地址進(jìn)行安裝解決:
三勾笆、配置描述文件
配置完證書后就是配置描述文件窝爪,在蘋果開發(fā)者網(wǎng)站的 Profiles 創(chuàng)建對應(yīng)的 mobile provision 蒲每。
1邀杏、選擇 Distribution
- App Store
創(chuàng)建對應(yīng)的打包模式望蜡,如果是 QA 的話一般選擇 Ad Hoc 泣特,也就是需要文件綁定設(shè)備 UDID 状您,而不需要上架 Store 的模式。
2眯分、選擇需要支持的 App Id 弊决,也就是 bundle Id 。
3昆稿、選擇前面生成的 Distribution
證書 溉潭,這里主要一定要選擇同意同一個(gè)喳瓣。
4畏陕、最后輸入 Provisioning Profile Name ,這個(gè) Name 在后面會有作用堤撵,另外如果是 Ad Hoc
的話实昨,在這一步可以選擇已經(jīng)添加的 Devices 的 UDID 。
5志电、完成配置后下載這個(gè) mobile provision
文件蛔趴,將它放到打包機(jī)器上的 /Users/你的賬號/Library/MobileDevice/Provisioning Profiles
目錄下鱼蝉,后面會需要用到它魁亦。
如果是 store 版本的就選擇
Distribution
-App Store
洁奈, 如果是 QA 版本的就選擇Distribution
-Ad Hoc
利术, 因?yàn)?App Store
打出來的包只能通過 Store 或者官方 TestFight 下載际跪,而Ad Hoc
打包的可以通過內(nèi)部自定義分發(fā)下載(通過添加測試設(shè)備的 UDID)姆打。
四幔戏、配置項(xiàng)目
完成了證書和描述文件的配置后闲延,接下來就是針對項(xiàng)目的配置。
首先將需要打包的項(xiàng)目 clone 到打包機(jī)器上(只是為了做測試配置)合愈,然后打開項(xiàng)目 ios/Runner.xcworkspace
目錄佛析,這時(shí)候可以看到項(xiàng)目因?yàn)闆]有開發(fā)者賬號寸莫,是如下圖所示的狀態(tài):
然后我們?nèi)∠x購 Automatically manage signing
桃纯, 然后選中我們前面放置的描述文件慈参,就可以看到 Xcode 會自動(dòng)匹配到鑰匙串里的證書,然后顯示正常的證書和描述文件配置了壮锻。
這里有一個(gè)需要注意的點(diǎn)猜绣,那就是項(xiàng)目在我們本地開發(fā)默認(rèn)使用的就是 Automatically manage signing
的方式,因?yàn)檫@樣比較方便伟阔,所以我們其實(shí)是需要在打包時(shí)讓它變成手動(dòng)簽名辣之,并且指定 mobile provision 文件的模式。
所以前面在打包機(jī)器上操作 Xcode 取消 Automatically manage signing
指定描述文件后皱炉,其實(shí)已經(jīng)修改了項(xiàng)目的 ios/Runner.xcodeproj/project.pbxproj
怀估,所以這時(shí)候你只需要通過 git diff
命令就可以導(dǎo)出一個(gè) patch
文件,這樣在項(xiàng)目被 clone 下來后合搅,通過 git apply
直接調(diào)整項(xiàng)目的描述文件多搀。
git diff >./release.patch
如果有多種編譯模式,比如一個(gè)項(xiàng)目打包多個(gè) bundleId 和描述文件(QA 灾部、Release)康铭, 那就可以生成多個(gè) .patch
文件。
?? 注意:第三方打包機(jī)器上每次打包都是
clone
一個(gè)新項(xiàng)目叠荠,打包后刪除該項(xiàng)目鳖孤,這樣可以保證每次打包的獨(dú)立和干凈平匈,而通過改生成不同的.patch
文件拧晕,我們可以指向不同的mobile provision
,從而加載不同的證書,甚至是同一個(gè)項(xiàng)目打包出不同的bundle id
。
五尽狠、開始打包
1沉馆、開發(fā)打包之前锌奴,需要先執(zhí)行 security unlock-keychain -p xxxxx
,解鎖下 keychain ,這里的 xxxxx 就是你 Mac 上的密碼。
2似忧、通過 flutter build ios --release
打包出 release 模式的 App.framework
和 Flutter.framework
饺著。
3缀雳、通過 xcodebuild
命令深碱,如下開始編譯 iOS 代碼了绞蹦,其中 $PWD 是所在工作目錄:
xcodebuild -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -configuration Release archive -archivePath $PWD/build/Runner.xcarchive
??這里有一個(gè)需要注意滨彻,那就是打包過程中如果出現(xiàn) .sh 腳本的相關(guān)報(bào)錯(cuò)辜羊,比如
xcode_backend.sh" embed_and_thin
或者PhaseScriptExecution Thin\ Binary /Users/xxxxx/Library/Developer/Xcode/DerivedData/
的錯(cuò)誤昔驱,推薦先在打包機(jī)上用 Xcode 執(zhí)行一次完整的Archive
流程,在首次執(zhí)行過程應(yīng)該會出現(xiàn)關(guān)于某些 sh 的授權(quán)執(zhí)行彈框,輸入密碼點(diǎn)始終完成,然后再重新執(zhí)行上述腳本亲茅。
4验残、執(zhí)行完 Archive
之后氨鹏,就可以進(jìn)入 export
階段镣丑,exportArchive
之前需要先準(zhǔn)備一個(gè) ExportOptions.plist
文件用戶指定到處的配置辽聊,模板類似:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>你的 bundleId </key>
<string>前面 provision 定義的 name</string>
</dict>
<key>signingCertificate</key>
<string>Apple Distribution</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>你的開發(fā)證書的 Team Id</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<false/>
</dict>
</plist>
其中
-
method
的數(shù)值如果是 store 就寫app-store
迹冤,如果是 QA 就寫ad-hoc
膜蠢; -
provisioningProfiles
的<dict>
需要bundleId
和前面provision
定義的name
; -
teamID
需要的是你的開發(fā)證書的Team Id
狂男; - 如果是 store 可以增加
uploadBitcode
和uploadSymbols
的配置,如果是 QA 則可以不指定澎剥,然后 QA 可以也指定thinning
模式绞佩;
接著通過指定命令 exportArchive
魄懂,指定 ExportOptions.plist ,如果是有不同 id 或者不同模式其监,一般需要配置 QA 和 Prod 兩種 ExportOptions.plist
,最終輸出到 package_path
這時(shí)候你就得到了一個(gè) ipa 文件。
xcodebuild -exportArchive -exportOptionsPlist ExportOptions.plist -archivePath $PWD/build/Runner.xcarchive -exportPath $package_path -allowProvisioningUpdates
最后如果是 store 模式的,接下來你只需要通過 Mac 的 Transporter
將 ipa 上傳到 App Store Connect拱撵,或者使用命令行工具將自己的應(yīng)用或內(nèi)容上傳至 App Store Connect 穷遂。
$ xcrun altool --validate-app -f file -t platform -u username [-p password] [--output-format xml]
$ xcrun altool --upload-app -f file -t platform -u username [-p password] [—output-format xml]
一般 altool 位于 /Applications/Xcode.app/Contents/Developer/usr/bin/altool 澡腾,更多可見 https://help.apple.com/asc/appsaltool/
如果你是 QA 模式蝶防,那么你需要先準(zhǔn)備一個(gè) html
文件,如下所示例子染苛,通過 a
標(biāo)簽配置 itms-service
指定一個(gè) DistributionSummary.plist
文件茶行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<a href=itms-services://?action=download-manifest&url=https://xxxx.xxxx.cn/aaaa/bbbbb/ios/DistributionSummary.plist>install</a>
</body>
</html>
然后在 DistributionSummary.plist
文件中指定 software-package
的 ipa 下載地址轮纫,這樣就可以完成 QA 的內(nèi)部自助分發(fā)了叽粹。(只能安裝 QA provision 里已經(jīng)配置了 UDID 那些機(jī)器)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>https://xxxx.xxxxxx.cn/xxxx/Runner.ipa</string>
</dict>
<dict>
<key>kind</key>
<string>full-size-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string>http://xxxx.xxxxxx.cn/assets/applog/icon.png</string>
</dict>
<dict>
<key>kind</key>
<string>display-image</string>
<key>needs-shine</key>
<true/>
<key>url</key>
<string>http://xxxx.xxxxxx.cn/assets/applog/icon.png</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>com.xxxx.demo</string>
<key>bundle-version</key>
<string>1.0.0</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>XXXX App download</string>
</dict>
</dict>
</array>
</dict>
</plist>
六状囱、多 Flutter 版本環(huán)境
如果需求有存在多個(gè)項(xiàng)目需要在一個(gè)機(jī)器打包,但是不同項(xiàng)目的 Flutter 等版本都不同倘是,那么對于 Mac 可以開啟多個(gè)不同的登陸用戶亭枷,這樣就可以得到不同的打包環(huán)境,當(dāng)然這里主要注意的是 CocoaPod 的版本問題搀崭,因?yàn)楸热?:
Flutter 1.22 版本默認(rèn)是使用 1.8.0 之類的 Pod 版本叨粘,如果在 Flutter 1.22 上使用 1.10.0 的 Pod 版本會導(dǎo)致 logo 錯(cuò)誤等問題;
Flutter 2.0 需要的是 1.10.0 的 Pod 版本瘤睹;
而在 Mac 上默認(rèn) CocoaPod 是安裝在 usr/local/bin
目錄升敲,這個(gè)目錄其實(shí)是多賬號共享,所以為了解決這個(gè)問題轰传,需要在每個(gè)賬戶環(huán)境下安裝 rvm
驴党,用于管理獨(dú)立的 CocoaPod 版本。
簡單地說:
- 1获茬、先通過 curl 安裝 rvm港庄;
curl -L get.rvm.io | bash -s stable && source ~/.rvm/scripts/rvm
- 2、通過
rvm install 2.5.5
安裝對應(yīng)的 ruby 版本恕曲,具體可以通過rvm list known
選中你想要需要的版本
這里需要注意
rvm install
可能會失敗鹏氧,一般和 brew 需要 update 還有網(wǎng)絡(luò)情況有關(guān)系;
- 3码俩、可以安裝多個(gè) ruby 版本,然后通過
rvm use <Version> --default
或者rvm use <Version>
來使用具體版本
不加
defalut
的話歼捏,下次啟動(dòng)命令行會變成原來的defalut
版本稿存;
- 4、在當(dāng)前 ruby 版本下安裝想要的 cocoapods 版本瞳秽,這樣當(dāng)使用
rvm use
切換版本時(shí)瓣履,cocoapods 版本也會跟著切換。
sudo gem install cocoapods -v <Version> -n /usr/local/bin
事實(shí)上在不同用戶下安裝了 rvm 之后练俐,彼此之間的 Pod 版本就已經(jīng)分割開了袖迎。
七、最后
說了那么多腺晾,其實(shí) Xcode 自動(dòng)打包確實(shí)舒服很多燕锥,但是通過整個(gè)配置過程,也可以幫助你了解到以前不知道的打包和認(rèn)證過程悯蝉。
這里最后額外補(bǔ)充一句归形,通過如下命令,在打包 Android 或者 iOS 時(shí)鼻由,可以通過 --dart-define
來指定不同的 dart 參數(shù).
flutter build ios --release --dart-define=CHANNEL=GSY --dart-define=LANGUAGE=Dart
在 dart 代碼里可以通過 String.fromEnvironment
獲取到對應(yīng)的自定義配置參數(shù)暇榴。
const CHANNEL = String.fromEnvironment('CHANNEL');
const LANGUAGE = String.fromEnvironment('LANGUAGE');