---
# System prepended metadata

title: Android CI/CD with Jenkins & Fastlane
tags: [CICD, Android]

---

# Android CI/CD with Jenkins & Fastlane
> 本文件僅描述技術流程與設計思路，所有名稱與範例皆為示意用途。

---

## 📌 專案說明

本專案使用 **Jenkins（macOS Agent）** 搭配 **Expo、Gradle、Fastlane**，  
實作 **Android App Bundle（AAB）** 的自動建置與上傳至  
**Google Play Console（Internal Testing）**。

所有敏感資訊（如 **Keystore、Google Play Service Account**）  
皆由 **Jenkins Credentials** 統一管理，**不進入版本控制系統**，  
確保 CI/CD 流程的安全性。

---

## 🎯 專案目標

- 自動化 Expo Android 專案建置流程  
- 在 CI 環境中安全注入 Android Release Signing  
- 產生 Android App Bundle（`.aab`）  
- 使用 Fastlane 自動上傳至 Google Play Console  
- 建立一套：
  - ✅ 可重現
  - ✅ 可維護
  - ✅ 可交接  
  的 CI/CD 流程

---

## 🏗️ CI 架構概述

### Jenkins Controller
- Pipeline 定義
- 任務調度與執行管理

### macOS Agent（MacBook / Mac mini）
- 執行 Expo、Gradle、Fastlane
- 使用本地安裝環境：
  - Android SDK
  - Java
  - Node.js

---

## 🧰 使用工具

- **Jenkins**：CI Pipeline 管理  
- **Expo**：React Native Android Prebuild  
- **Gradle**：Android AAB 建置  
- **Fastlane（supply）**：Google Play 上傳  
- **Google Play Service Account**：Google Play Developer API 存取  

---

## 📁 專案目錄結構

```text
.
├─ Jenkinsfile
├─ fastlane/
│  └─ Fastfile
├─ scripts/
│  └─ patch-android-signing.js
├─ android/
│  └─ app/
│     └─ build/
│        └─ outputs/
│           └─ bundle/
│              └─ release/
│                 └─ app-release.aab
├─ app/
├─ ios/
└─ package.json
```

## 說明

- `android/`、`ios/` 由 **Expo Prebuild** 動態產生  
- `.aab` 為 **CI 產物**，不進入版本控制  
- `Jenkinsfile` 置於 **Repository Root**

---

## Jenkins Pipeline 流程

### 1. 環境設定 (Environment Setup)

Pipeline 啟動時設定以下環境變數：

- `ANDROID_HOME`：Android SDK 路徑  
- `PATH`：包含 Node.js 與 Android `platform-tools`  
- `GRADLE_USER_HOME`：指向 Jenkins Workspace  
- `CI=true`：確保工具以 CI 模式執行  

---

### 2. 相依套件安裝 (Dependencies)

- Checkout 原始碼  
- 使用 Yarn 安裝 JavaScript 套件  
- 使用 `--frozen-lockfile` 鎖定套件版本  

---

### 3. Expo Android Prebuild

執行指令：

```bash
yarn expo prebuild --platform android --clean
```
## 
- 每次 CI 皆重新產生 乾淨的 Android 原生專案
- 確保 build.gradle 與相關設定為最新狀態
---
### 4. Android Release Signing 注入

CI 執行期間完成以下動作：

- 從 **Jenkins Credentials** 注入 Keystore  
- 動態產生 `keystore.properties`  
- 執行自製 Script：
  - `scripts/patch-android-signing.js`
- 自動 Patch：
  - `android/app/build.gradle`
- 確保 Release Build 使用正確的 Signing Config  

---

### 5. Android AAB 建置

執行 Gradle 指令：

```bash
./gradlew bundleRelease
```
產出檔案
```bash
android/app/build/outputs/bundle/release/app-release.aab
```
---

### 6. AAB 定位與上傳準備

Jenkins 在上傳前進行以下處理：

- 固定工作目錄為 **Repository Root**  
- 僅搜尋 `bundle/release/` 目錄  
- 將 AAB 路徑轉為 **絕對路徑**

並設定以下環境變數：

- `AAB_PATH`
- `SUPPLY_JSON_KEY`

---

### 7. applicationId 自動取得

Jenkins 從 `android/app/build.gradle` 解析：

```gradle
applicationId "com.example.app"
```

並設定環境變數：
```gradle
PACKAGE_NAME=com.example.app
```
確保 Fastlane 使用正確的 Package Name。

---

🚀 Fastlane 設計說明
- Fastlane 僅負責上傳
- 不參與建置流程

Fastfile 範例
```
default_platform(:android)

platform :android do
  lane :play_internal do
    upload_to_play_store(
      aab: ENV["AAB_PATH"],
      track: "internal",
      package_name: ENV["PACKAGE_NAME"],
      json_key: ENV["SUPPLY_JSON_KEY"],
      skip_upload_metadata: true,
      skip_upload_images: true,
      skip_upload_screenshots: true,
      skip_upload_changelogs: true
    )
  end
end
```
## 安全性設計

- 所有機密資料皆由 **Jenkins Credentials** 管理  
- Repository **不包含**：
  - Keystore
  - Service Account JSON
  - Build Outputs
- 可直接移轉至其他 macOS Jenkins Agent（如 Mac mini）

---

## 結論

此 CI/CD 流程透過明確的責任分離：

- **Jenkins**
  - 建置流程控制
  - 環境準備
  - 參數推導

- **Fastlane**
  - Google Play 上傳

成功建立一套 **穩定、安全、可維護** 的 Android AAB 自動化流程，  
適合長期運行與團隊交接使用。

