Flutter 搭建 iOS 命令行服務(wù)打包發(fā)布全保姆式流程


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ā)信息的配置過程耀石。

image

但是我個(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)生成就可以了谎替。

image

2钱贯、在蘋果官方的 developer 上點(diǎn)擊創(chuàng)建證書秩命,上傳步驟 1 中的 CertificateSigningRequest.certSigningRequest 文件弃锐,然后下載 .cer 證書文件霹菊。

image

3券敌、這里需要注意不能直接把這個(gè) .cer 證書文件安裝到打包服務(wù)上柳洋,而是把這個(gè) .cer 先安裝到上面第 1 步中生成的 CertificateSigningRequest.certSigningRequest 的機(jī)器上熊镣,然后通過導(dǎo)出證書生成帶有密碼的 p12 證書文件,這個(gè)文件才是可以安裝到打包機(jī)器上的證書文件莹捡。

image

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 的模式。

image

2眯分、選擇需要支持的 App Id 弊决,也就是 bundle Id 。

image

3昆稿、選擇前面生成的 Distribution 證書 溉潭,這里主要一定要選擇同意同一個(gè)喳瓣。

image

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):

image

然后我們?nèi)∠x購 Automatically manage signing 桃纯, 然后選中我們前面放置的描述文件慈参,就可以看到 Xcode 會自動(dòng)匹配到鑰匙串里的證書,然后顯示正常的證書和描述文件配置了壮锻。

image

這里有一個(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.frameworkFlutter.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 可以增加 uploadBitcodeuploadSymbols 的配置,如果是 QA 則可以不指定澎剥,然后 QA 可以也指定 thinning 模式绞佩;
image

接著通過指定命令 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');
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厚棵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔼紧,更是在濱河造成了極大的恐慌婆硬,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奸例,死亡現(xiàn)場離奇詭異彬犯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)哩至,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門躏嚎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菩貌,你說我怎么就攤上這事卢佣。” “怎么了箭阶?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵虚茶,是天一觀的道長。 經(jīng)常有香客問我仇参,道長嘹叫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任诈乒,我火速辦了婚禮罩扇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怕磨。我一直安慰自己喂饥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布肠鲫。 她就那樣靜靜地躺著员帮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪导饲。 梳的紋絲不亂的頭發(fā)上捞高,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音渣锦,去河邊找鬼硝岗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛袋毙,可吹牛的內(nèi)容都是我干的辈讶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼娄猫,長吁一口氣:“原來是場噩夢啊……” “哼贱除!你這毒婦竟也來了生闲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤月幌,失蹤者是張志新(化名)和其女友劉穎碍讯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扯躺,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捉兴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了录语。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倍啥。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖澎埠,靈堂內(nèi)的尸體忽然破棺而出虽缕,到底是詐尸還是另有隱情,我是刑警寧澤蒲稳,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布氮趋,位于F島的核電站,受9級特大地震影響江耀,放射性物質(zhì)發(fā)生泄漏剩胁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一祥国、第九天 我趴在偏房一處隱蔽的房頂上張望昵观。 院中可真熱鬧,春花似錦舌稀、人聲如沸啊犬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椒惨。三九已至缤至,卻和暖如春潮罪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背领斥。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工嫉到, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人月洛。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓何恶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嚼黔。 傳聞我的和親對象是個(gè)殘疾皇子细层,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容