去年 Fabric 確定合併回 Firebase 後就決定要另外找地方放出測試版給 QA
在內網架了一個簡單的網站
讓 QA 可以用手機打開網頁直接下載測試版 app
但因為 OTA 機制需要 Https
存放 manifest.plist 跟 ipa 的地方就選擇了 Dropbox
選擇的原因讓我後面再詳述
然後因為手動上傳檔案跟 manifest 的修改過程太繁雜了,實在不是給人用的
簡單敘述就是
包好 ipa
上傳 ipa 到 Dropbox
分享 ipa 的鏈結
將分享鏈結填入 manifest.plist
將 manifest.plist 上傳至 Dropbox
分享 manifest.plist 的鏈結
將 manifest.plist 的鏈結的內容從 dropbox.com
修改成下載用鏈結 dl.dropboxusercontent.com
依照 itms-service 協議的格式將修改好的 manifest.plist 的下載鏈結放入網站
光寫完這一串就累了 @@
所以就決定寫點 Script 幫忙處理上面那一大串雜事吧
因為太多了這篇就只處理從 ipa 上傳到 dropbox 到可以下載的部分
首先是選擇 Dropbox 的理由
首先是 OTA 需要下載位置支援 https
自己架在內網內的 Server 不太想弄
所以目標就轉移至簡單使用的雲端儲存服務
看了一下網路上也很多人分享使用 Dropbox 的文章
實作起來並不困難
最重要的是我在 Dropbox 的文件上找到了我需要的功能
就是能利用 POST 進行檔案的上傳 以及前面提到的分享鏈結的產生
這樣目標就明確了
在 ipa 打包完成之後,執行 script 去執行上面那一串工作
首先打開 Dropbox 文件
Let’s go ~
上傳 API 找到 upload
這隻 API 的說明
1 2 3 4 5 curl -X POST https://content.dropboxapi.com/2/files/upload \ --header "Authorization: Bearer <get access token>" \ --header "Dropbox-API-Arg: {\"path\": \"/Homework/math/Matrices.txt\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \ --header "Content-Type: application/octet-stream" \ --data-binary @local_file.txt
其中需要修改的位置有三個
首先是在 Bearer
後面要換成自己 Dropbox 帳號的 token
Dropbox 也很貼心的把 弄成一個按鈕
登入之後點下去就會顯示自己的 token 了
第二個是設定 Dropbox 上要儲存的路徑
也就是範例中 /Homework/math/Matrices.txt
的部分
最後是要上傳的本地檔案位置
也就是最後一行的 local_file.txt
取得分享鏈結位置 一樣找到文件裡面 create_shared_link_with_settings
的說明
1 2 3 4 curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings \ --header "Authorization: Bearer <get access token>" \ --header "Content-Type: application/json" \ --data "{\"path\": \"/Homework/math/Matrices.txt\"}"
這隻 API 也是設定好 token
然後指定 Dropbox 上要分享的文件位置
對這次要達成的目標而言,重點在這隻 API 的 response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 { ".tag": "file", "url": "https://www.dropbox.com/s/2sn712vy1ovegw8/Prime_Numbers.txt?dl=0", "name": "Prime_Numbers.txt", "link_permissions": { "can_revoke": false, "resolved_visibility": { ".tag": "public" }, "revoke_failure_reason": { ".tag": "owner_only" } }, "client_modified": "2015-05-12T15:50:38Z", "server_modified": "2015-05-12T15:50:38Z", "rev": "a1c10ce0dd78", "size": 7212, "id": "id:a4ayc_80_OEAAAAAAAAAXw", "path_lower": "/homework/math/prime_numbers.txt", "team_member_info": { "team_info": { "id": "dbtid:AAFdgehTzw7WlXhZJsbGCLePe8RvQGYDr-I", "name": "Acme, Inc." }, "display_name": "Roger Rabbit", "member_id": "dbmid:abcd1234" } }
會回傳一個 json 格式的資料
其中 url
這個欄位所帶回來的值就是我們這次的目標了
現在有了這兩隻 API ,就可以達成上傳檔案並拿到分享鏈結的目標了
manifest.plist 接下來要處理的就是讓 app 能正確被手機下載所需要的 manifest.plist 了
最簡單的取得方式就是直接使用 Xcode 打包一次 development 的版本發佈
裡面就會有產生 manifest.plist 的選項了
這裡給出來的是使用 Development 打包發佈 ipa 的版本(因為是給自家 QA 測試)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?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://dl.dropboxusercontent.com/s/dropbox_shared_link/demo.ipa</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>{your_bundle_identifier}</string> <key>bundle-version</key> <string>now_build_number</string> <key>kind</key> <string>software</string> <key>platform-identifier</key> <string>com.apple.platform.iphoneos</string> <key>title</key> <string>demo</string> </dict> </dict> </array> </dict> </plist>
這裡面需要修改兩個地方
一個是用自己 app 的 bundle identifier
直接取代掉 {your_bundle_identifier}
另外是將 url
, title
兩個 key 底下的 demo 換成要發佈的 ipa名稱 與 app 名稱即可
manifest.plist 中還有兩個地方是等等我們在寫 script
的時候希望能自動幫我們取代的東西
一個是 dropbox_shared_link
另一個就是 bundle-version
這樣以後執行時帶入取得的下載位置連結跟版本號就輕鬆多了
這裡為何只需要修改 dropbox_shared_link
我們文章最後一起討論
目標細節 & 檔案配置 作為一個減少人工作業的配置,當然需要多將一些繁雜的工作考慮進去
ipa path 首先是 ipa 檔案打包好之後存放的位置跟上傳之後在 Dropbox 上的位置
希望可以簡單表達出這是哪一個版本,哪一個環境
因此在建置機器的本地端會將檔案輸出到
/{project_name}/{build_version}({build_number}){env}
的目錄底下
並且上傳到 Dropbox 上相同的資料夾位置
因此假設我的專案名稱叫做 demo
這版本是 staging 環境的 1.0.1(345) 版本
打包好我就把它 export 到 /demo/1.0.1(345)staging
這個資料夾底下
這樣經過一段時間回來也能方便找到對的版本
manifest.plist 重複利用 作為大多內容不需要改動的 manifest.plist 檔案
上面範例檔案的參數就是希望能不需要每次都手動重新配制 manifest.plist
因此確認寫好的 manifest.plist 正確放置在建置機器下的路徑位置,並記錄下來
這樣等一下寫 script 的時候我們就知道該指定檔案的位置讓系統找到它了
如此一來,只要執行完再利用改動將檔案回復為使用前的狀態
就可以重複利用這個 manifest.plist 的範例檔案了
設定好一次就可以不理他囉
BASH SCRIPT 準備工作都完成了,開始來寫關鍵的執行腳本吧
重新回顧一下前面的步驟,並搭配我們的工具
可以重新整理出這個 script 實際執行需要的配置與動作如下
接收三個外部參數 {version_number}, {build_number}, {env}
上傳 ipa 檔案到 Dropbox
取得 ipa Dropbox 分享鏈結
處理回傳資訊並拿出需要的鏈結位置資訊
將位置資訊以及 {version_number} 寫入 manifest.plist
上傳 manifest.plist
將本地 manifest.plist 回復為原本的設定
取得 manifest.plist Dropbox 分享鏈結
處理回傳資訊取得可以用來下載 app 的 url
如下範例檔案
upload_dropbox_share_tamplate.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 # Step1 Get parameter echo $1 echo "version number" echo $2 echo "build number" echo $3 echo "env" # prod/preprod/staging/dev echo "<your_file_location>/demo$1($2)$3" # Step2 upload ipa to dropbox curl -X POST https://content.dropboxapi.com/2/files/upload \ --header "Authorization: Bearer <access_token>" \ --header "Dropbox-API-Arg: {\"path\": \"/demo/$1($2)$3/demo.ipa\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \ --header "Content-Type: application/octet-stream" \ --data-binary "@<your_file_location>/demo$1($2)$3/demo.ipa" #--------- # Step3 Create ipa file shared_link response=$(curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings \ --header "Authorization: Bearer <access_token>" \ --header "Content-Type: application/json" \ --data "{\"path\": \"/demo/$1($2)$3/demo.ipa\"}" ) # Step4 Handle response # replce response string ex: "https://www.dropbox.com/s/tcw9p8uxrlwzw09/xxx.ipa?dl=0....." echo $response # cut dl=0 left=${response%?dl=0*} # cut /xxx.ipa ipaName=${left%/*} # cut https://www.dropbox.com/s/ link=${ipaName#*s/} echo $link # Step5 Set manifest file # dropbox_shared_link write into manifiest.plist sed -i '' "s/dropbox_shared_link/${link}/g" <your_file_location>/manifest.plist sed -i '' "s/now_build_number/$1/g" <your_file_location>/manifest.plist # Step6 Upload manifest.plist curl -X POST https://content.dropboxapi.com/2/files/upload \ --header "Authorization: Bearer <access_token>" \ --header "Dropbox-API-Arg: {\"path\": \"/demo/$1($2)$3/manifest.plist\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \ --header "Content-Type: application/octet-stream" \ --data-binary "@<your_file_location>/manifest.plist" # Step7 Reset dropbox_shared_link in manifiest.plist sed -i '' "s/${link}/dropbox_shared_link/g" <your_file_location>/manifest.plist sed -i '' "s/$1/now_build_number/g" <your_file_location>/manifest.plist # Step8 share_menifest.plist response_manifest=$(curl -X POST https://api.dropboxapi.com/2/sharing/create_shared_link_with_settings \ --header "Authorization: Bearer <access_token>" \ --header "Content-Type: application/json" \ --data "{\"path\": \"/demo/$1($2)$3/manifest.plist\"}" ) # Step9 Handle response # replce response string ex: "https://www.dropbox.com/s/tcw9p8uxrlwzw09/manifest.plist?dl=0" echo $response_manifest # cut dl=0 left_m=${response_manifest%?dl=0*} # cut /manifest.plist ipaName_m=${left_m%/*} # cut https://www.dropbox.com/s/ link_m=${ipaName_m#*s/} echo $link_m
使用上非常簡單,依照上方範例
修改好裡面檔案路徑位置之後
在 terminal 下移動到存放這個 upload_dropbox_share_tamplate.sh 的位置
執行
1 sh upload_dropbox_share_tamplate.sh {version_number} {build_number} {env }
執行完會回覆一組字串
將這組字串組好 Dropbox 下載鏈結並放入網站中
點擊連結就可以下載 app 拉
關於 OTA 原理與 Dropbox 下載鏈結 實作完了我們就來看看前面沒有提到的部分吧
簡單來說就我理解到的 OTA(Over-the-Air) 安裝
是瀏覽器利用 itms-service 協議讓使用者的手機取得 manifest.plist 裡的資訊
app 名稱,版本,下載位置等等,並觸發下載流程
Over-the-Air Documentation
因此在 manifest.plist 中需要明確指定 ipa 的下載位置
而網站裡也需要有 manifest.plist 的下載位置讓瀏覽器去進行下載動作
為此我們必須將上傳至 Dropbox 並且取得可以用來直接 下載的鏈結
依照 Dropbox 的說明,想要直接下載時
可以使用 dl.dropboxusrecontent
的路徑來取得檔案
並且可以發現下載鏈結的格式都可以統一成
https://dl.dropboxusercontent.com/s/{dropbox_shared_link}/filename
而取得分享鏈結的 api 回傳的 url 欄位格式會是
https://www.dropbox.com/s/{dropbox_shared_link}/filename
所以在處理下載鏈結時,只需要取出中間 {dropbox_shared_link} 部分的字串就可以了
BASH 檔案裡的 Step 3, Step 8 我就直接針對 response 的字串結果去處理
直接把前後的字通通摳掉,就可以得到我需要的 {dropbox_share_link} 字串了
再將這字串做需要的處理即可,像是 Step 4 塞入 manifest.plist
範例檔案最後只有將 manifest.plist 的下載需要的 {dropbox_share_link}
這一串字印出來而已
其實還可以做後續的處理
像我是把它組成正確的下載鏈結,然後傳到 slack 跟更新到網站上
這樣搭配 CI + fastlane 就能完成 push code 完成就自動打包好送到 QA 手上拉
又朝著薪水小倫的目標更近一步了呢 0.0b
有空再來寫寫打包的故事 emmm …