YiBi Dev Docs
架構決策 (ADR)

Google Play AAB 上傳採 keyless WIF(r0adkll/upload-google-play + google-github-actions/auth),不建 SA JSON key

Context

Keith 規劃調整 mobile 打包流程,補上 Android App Bundle(AAB)→ Google Play 這條發布路徑, 並提到需要「一個可以上傳到 Google Play 的 service account」。需先確認此設計與既有架構約束是否一致。

現狀(查證自 cd-mobile.ymlinfra/terraform/modules/{iam,wif}、ADR-0017):

面向現狀
Android 產出cd-mobile.ymlandroid-beta job 只產 APK 丟 Firebase App Distribution;無 AAB、無 Google Play 對接、無 fastlane
CI → GCP 認證一律 Workload Identity Federationmodules/wif/main.tf):GitHub OIDC 冒充 github-actions-deployer SA,無下載式金鑰
SA JSON keyGCP org policy constraints/iam.disableServiceAccountKeyCreation 禁止建立(見 docs/runbook/deployment/stage-runbook-prereq.md
相關決策ADR-0017(Firebase 治理)Decision 表確立「Deployer SA / WIF → 無下載式金鑰」;但 ADR-0017 範圍是 Firebase project myyibi未涵蓋 Google Play(Play Console 是與 Firebase 完全獨立的系統)

關鍵約束:傳統 Play 上傳工具(fastlane supply、Gradle Play Publisher、r0adkll/upload-google-play預設要餵一把 SA JSON key——而本專案 org policy 禁止建 key。因此需決定:能否全程無 key 上傳 Play, 以及用哪個工具。此外,Google Play 的授權是兩層:GCP IAM(SA 存在 + 可換 token)與 Play Console 端(SA email 必須被邀進 Console 授權),後者活在 GCP IAM/Terraform 之外。

Decision

已裁決(accepted,2026-06-24):keith 於 PR #591 review、howie 裁決下方四項決定(原 Open Questions)。

結論:Google Play AAB 上傳採 keyless Workload Identity Federation,沿用既有 google-github-actions/auth + WIF 機制,不建立任何 SA JSON key;上傳工具採 r0adkll/upload-google-play。四項落地決定:

  1. SA:新建專用 play-publisher SA,由 Terraform 管 SA 本體(modules/iam/main.tf)+ WIF 綁定(modules/wif/main.tf);Play Console 邀請為手動步驟。
  2. Track:CI 自動上傳到 internal track;production 留人工 promote(對齊現行 beta 自動化到測試層、正式發布留人工關卡的精神)。
  3. App Signing啟用 Google Play App Signing(發 AAB 的強制前提,2021-08 起新 app 預設且強制);現有 ANDROID_KEYSTORE_BASE64 keystore 降格為 upload key,真正的發布金鑰由 Google 代管。注意:Play App Signing 與 GCP Secret Manager 無關(前者管 app 簽章金鑰,後者管後端 secret)。
  4. 工具r0adkll/upload-google-play(官方 README 將 keyless WIF 列為推薦路徑)。

機制要點(查證 2026-06-24,來源見 Links):

  • keyless 的接法不是餵裸 access token,而是餵 ADC credential config 檔—— google-github-actions/auth@v2credentials_file_path 輸出(external_account 型別、無 key)。 三大工具都吃這條,但都不設計來吃 token_format: access_token 的裸 token,故 auth step 用預設的 credentials_file_path不要token_format: access_token
  • r0adkll/upload-google-play 在官方 README 把此 keyless WIF 路徑列為「more secure, recommended」, 直接文件化 serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }},與本專案現有 WIF 架構無縫對接、零 key、無版本不確定性。
  • workflow 四步:(1) permissions: id-token: write;(2) google-github-actions/auth@v2 用既有 workload_identity_provider + service_account;(3) r0adkll/upload-google-play@v1serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }};(4) 該 SA email 先在 Google Play Console「Users and permissions」邀請並授權

落地需補五件事(其中第 3、4 件 Terraform 管不到):

關鍵澄清:Play 發布權限不是 GCP IAM role。Google Play 對 SA 的授權完全在 Play Console 授予(Users and permissions),沒有對應的 roles/androidpublisher.* GCP IAM role 可在 iam module 設定。Terraform 在 GCP 端只負責「API 啟用」與「WIF/impersonation 綁定」,負責 Play 發布授權本身。

#要做什麼在哪裡Terraform 管得到?
1啟用 androidpublisher.googleapis.com APIinfra/terraform/bootstrap/main.tfgoogle_project_service.apis 集合)
2(僅當 OQ#1 採專用 play-publisher SA 時) 在 iam module 建該 SA,並在 WIF module 授其 roles/iam.workloadIdentityUser(現只綁 deployer SA),否則 google-github-actions/auth 無法替新 SA 產 credential fileinfra/terraform/modules/iam/main.tf + modules/wif/main.tf
3把上傳 SA email 邀進 Google Play Console 並授 release 權限(這就是 Play 發布授權的唯一來源Play Console 網頁手動❌(runbook 記錄,比照 ADR-0017 不可 IaC 項目)
4(一次性,全新 package only — Keith 補充) 人工在 Play Console 建立 app 並上傳第一版 APK/AABPlay Console 網頁手動
5cd-mobile.ymlflutter build appbundle 產 AAB + 上傳 step.github/workflows/cd-mobile.yml

Google Play API 硬限制(bootstrap):Android Publisher API 不接受全新 package 的第一版—— 必須先人工在 Play Console 建立 app 並上傳一次 APK/AAB,API 才認得這個 package。第 4 步是 CI(第 5 步)能跑的前置條件,且僅全新 package 需要一次;既有 package 後續版本全走 API/CI。

若沿用既有 github-actions-deployer SA(OQ#1 的另一選項),第 2 步不需要——既有 WIF 綁定已涵蓋 deployer SA,僅需在 Play Console 邀請它(第 3 步)。

與 ADR-0017 的關係:本決策正交且可獨立執行。ADR-0017 處理 Firebase project myyibigoogle-beta provider、import app、config 改存 Secret Manager、per-env Firebase project), 本決策處理 Google Play Console + androidpublisher,兩者唯一交集是共用既有 WIF/deployer SA 機制與 cd-mobile.yml。ADR-0017 的 Firebase 治理工作不依賴本決策,可先行;本決策亦不需等 ADR-0017 落地。

Alternatives Considered

  • 方案 A:SA JSON key + fastlane supply(傳統做法) — 考慮過,因為是最普遍的範例; 但牴觸 org policy iam.disableServiceAccountKeyCreation(建不出 key)與 ADR-0017 「無下載式金鑰」原則,沒有選。
  • 方案 B(推薦):keyless WIF + r0adkll/upload-google-play — 唯一在官方 README 把 keyless WIF 列為推薦並文件化的工具,與既有 WIF 架構無縫、零 key、無版本風險。選為預設。
  • 方案 C:keyless WIF + fastlane supply(走 ADC) — 可行(fastlane 官方文件列出支援 WIF; 推測經 ADC(GOOGLE_APPLICATION_CREDENTIALS 指向 config 檔)即可省略 json_key,惟官方未逐字 載明此參數可完全省略,需 CI 實測確認);且舊版有 missing client_email bug(fastlane issue #29491,external_account 憑證被誤判為 service_account), 需確認 gem 版本已修。功能較全,列為備選。
  • 方案 D:keyless WIF + Gradle Play Publisher(useApplicationDefaultCredentials = true — 可行(走 ADC、serviceAccountCredentials 非必填);但官方 README 未明寫支援 WIF,需自行 CI 實測。備選。
  • 方案 E:維持 Play Console 網頁手動上傳 — 考慮過(零 CI 改動);但不可重現、無版本紀錄、 不符 CI 自動化發布目標,沒有選。

關鍵事實:沒有任何工具「無論如何都需要 JSON key」——三者皆有 keyless 路徑,且全都依賴 「ADC credential config 檔」這條,而非裸 access token。

Consequences

  • 全程無 SA JSON key,符合 org policy 與 ADR-0017「無下載式金鑰」原則;CI 認證機制與既有部署完全一致。
  • Play 發布 CI 可重現、可版本控制(workflow + AAB build step)。
  • 與 ADR-0017 Firebase 治理工作正交,兩條可獨立並行推進。

壞 / 代償

  • Play Console 的 SA 授權是 Console 手動步驟,Terraform / gcloud / WIF 全碰不到—— 必須寫進 docs/runbook/deployment/,比照 ADR-0017 對「不可 IaC 項目」的處理。
  • 需新增 androidpublisher API 啟用 +(採專用 SA 時)WIF 綁定 + Play Console 授權 + cd-mobile.yml 的 AAB build/上傳 step。
  • 首次須完成 Google Play App Signing 註冊與 release track 設定(一次性)。
  • 全新 package 的第一版必須人工在 Play Console 上傳一次(Android Publisher API 不接受全新 package 的首版),CI 自動化只能從第二版起接手——這是 Play API 的硬限制,無法繞過(見落地清單第 4 步)。
  • credentials_file_path vs access_token 是易錯點:用錯(餵裸 token)會認證失敗。

中性

  • 上傳工具最終選 B/C/D 皆為 keyless ADC 路徑;選型差異在維護成本與版本確定性,不影響「無 key」結論。

Resolved Decisions(原 Open Questions,2026-06-24 裁決)

四項皆已裁決,內容見上方 Decision 段落 1–4:

  1. 上傳 SA:新建專用 play-publisher SA(least-privilege,避免發布權限與部署權限混在同一身分),Terraform 管 SA + WIF 綁定。採專用 SA 必須在 modules/wif/main.tf 為其加 roles/iam.workloadIdentityUser(見落地清單第 2 步),否則 keyless auth 換不到 credential file。
  2. 上傳 track:CI 自動上 internal,production 人工 promote。
  3. Play App Signing:啟用 Google 代管(發 AAB 強制前提);現有 keystore 降格為 upload key。與 GCP Secret Manager 無關。
  4. 工具r0adkll/upload-google-play(C fastlane 功能更全但有 missing client_email 版本不確定性;D GPP 官方未明寫 WIF 需實測——均未選)。

後續實作(非本 ADR 範圍)

本 ADR 只定方向;實際 cd-mobile.yml AAB build/upload step、Terraform SA + WIF 變更、Play Console 一次性 bootstrap(建 app + 首版手動上傳 + 邀請 SA)的執行,另開 PR / runbook 落地。

On this page