---
description: 自動編譯正式發行 Release APK、升級 Patch 版本號、並產出公司 OTA 平台必備之 Keyin 參考資料
---

# 生產環境發版打包工作流 (release-apk)

這個 Workflow 專門為應用程式「生產環境上線/發版」設計。呼叫 `/release-apk` 將自動完成嚴格的發行檢查、版號遞增、Release 簽章打包與發版資訊生成。

> [!IMPORTANT]
> **客戶專案（購買流程）已收斂為執行期 `shopping_mode`（雲端 B014 下發），不再用 flavor 分家。**
> 發版對象只剩 **硬體規格（hardware flavor）**：`S12XYU`（10 吋）/ `S9XYU`（21.5 吋 / Android 9）。
> 同一支 APK 上線後，現場機台由雲端 `shopping_mode` 決定購買流程。
> **部署鐵律**：發布統一版前，star-cloud 必須先為每台現役機設好 `shopping_mode`
> （晟崴=employee_card、中國醫=pickup_sheet），否則預設 basic 會跑錯流程。

> [!TIP]
> **參數 = 硬體規格：**
> * 預設（不帶參數）：打包 **`S12XYU`（10 吋）**。
> * `/release-apk S9`（或 `S9XYU` / `中國醫`）→ 打包 **`S9XYU`（21.5 吋）**。
> * `/release-apk S12`（或 `S12XYU` / `晟崴` / `通用`）→ 打包 **`S12XYU`（10 吋）**。

---

## 步驟零：發行檢查與版號自動遞增 (OTA 鐵律)
為了確保實體機台能夠順利觸發 OTA 自動下載覆蓋安裝，新編譯的 `versionCode` 必須嚴格大於現場的舊版號：
1. AI 助手將讀取 `app/build.gradle` 並自動將 `verPatch`（修補版號）**遞增 1**，確保 versionCode 順利變大。
2. 自動在 terminal 呈報即將打包的最新版本號資訊。

## 步驟一：清理快取並進行正式簽章編譯
採用單次 JVM 進程強制重新編譯，以確保代碼 100% 乾淨，並執行 Release 模式下的**代碼優化、混淆、與嚴格的 `lintVital` 審查**。

```bash
// turbo
# 終止殘留的 Gradle Daemon 程序，避免多個 Daemon 互搶資源，同時避免誤殺 VS Code Gradle 擴充功能
# 防呆：先優雅 --stop；僅在仍殘留時才升級終止（先 TERM 再 KILL），避免無差別強殺撞鎖導致 clean 編譯隨機失敗
#
# ⚠️ 關鍵：pgrep/pkill 的比對字串必須「拆開」拼接，不可在本檔字面寫出完整 class 名。
# 否則在背景任務（bash -c "...eval '整段腳本'..."）情境下，腳本自己的命令列會含有
# 完整的 GradleDaemon class 字串 → pgrep -f 比對到自己 → pkill 把跑腳本的 bash 自殺
# → exit 144，編譯根本還沒開始。再加 -vx $$ 排除自身 PID 雙重保險。
DAEMON_PAT='org.gradle.launcher.daemon.bootstrap''.GradleDaemon'   # 字面被拆開，pgrep 不會命中本腳本
daemon_alive() { pgrep -f "$DAEMON_PAT" 2>/dev/null | grep -vx "$$" | grep -q .; }
./gradlew --stop 2>/dev/null || true
sleep 2
if daemon_alive; then
    pkill -TERM -f "$DAEMON_PAT" 2>/dev/null || true
    sleep 3
    if daemon_alive; then
        pkill -9 -f "$DAEMON_PAT" 2>/dev/null || true
        sleep 2
    fi
fi

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai

# 取得目標硬體規格（預設 S12XYU）
RAW_INPUT="$1"
TARGET_HARDWARE="S12XYU"
if [ -n "$RAW_INPUT" ]; then
    LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]')
    case "$LOWER" in
        s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;;
        s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;;
        *) echo "⚠️ 無法識別「$RAW_INPUT」，沿用預設 S12XYU" ;;
    esac
fi

echo "📦 [Release] 開始編譯正式版變體: assemble${TARGET_HARDWARE}Release"

# 使用 clean 確保完整重新編譯，避開 incremental 快取問題
./gradlew clean :app:assemble${TARGET_HARDWARE}Release --no-daemon
```

## 步驟二：導出正式 APK 並產出後台 Keyin 派送參考資料
將封裝完畢、具備正式簽章的 APK 導出至最外層 `outputs/` 資料夾，並自動計算產出公司 OTA 派送後台所需的全部對照資料。

```bash
// turbo
cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai
mkdir -p outputs

RAW_INPUT="$1"
TARGET_HARDWARE="S12XYU"
if [ -n "$RAW_INPUT" ]; then
    LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]')
    case "$LOWER" in
        s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;;
        s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;;
    esac
fi

if [ "$TARGET_HARDWARE" = "S9XYU" ]; then
    FORMATTED_HW="S_9_XY_U"; MAJOR=21
else
    FORMATTED_HW="S_12_XY_U"; MAJOR=10
fi

# 動態尋找編譯產出的最新正式版 APK 檔案（檔名已無 project 段）
APK_SOURCE=$(find app/build/outputs/apk/${TARGET_HARDWARE}/release/ -name "app-${FORMATTED_HW}-*-release.apk" | head -n 1)

if [ -n "$APK_SOURCE" ]; then
    APK_NAME=$(basename "$APK_SOURCE")
    APK_DEST="outputs/$APK_NAME"
    cp "$APK_SOURCE" "$APK_DEST"
    echo "=================================================="
    echo "✅ 正式版 APK 封裝成功，已導出至：$APK_DEST"
    echo "=================================================="

    MINOR=$(grep "def verMinor" app/build.gradle | awk '{print $4}')
    PATCH=$(grep "def verPatch" app/build.gradle | awk '{print $4}')
    VERSION_NAME="${MAJOR}_0${MINOR}_${PATCH}_R"
    VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH))

    echo "🚀 實體販賣機 OTA 後台新增版本 Keyin 參考資料："
    echo "--------------------------------------------------"
    echo "1. 硬體規格 (hardware)        : $TARGET_HARDWARE（${MAJOR} 吋）"
    echo "2. 版本代碼 (versionCode)     : $VERSION_CODE"
    echo "3. 版本名稱 (versionName)     : $VERSION_NAME"
    echo "4. 正式版 APK 檔名             : $APK_NAME"
    echo "5. 購買流程                   : 由雲端 shopping_mode 決定（須先於後台設好）"
    echo "--------------------------------------------------"
    echo "📢 [溫馨提醒] 請將此 APK 上傳至 OTA 平台（依硬體分版），並確認該機 shopping_mode 已設定。"
    echo "=================================================="
else
    echo "❌ 找不到編譯產出的正式版 APK 檔案，封裝失敗！"
    exit 1
fi
```

## 步驟三：自動化 Git 發版標記引導
為了落實公司發版規範第 5 條「必須在主分支合併節點打上 Git Tag，以利日後追溯特定現場機台的版本程式碼」，AI 助手會自動計算出對應的版本 Tag 標籤與提示：

```bash
// turbo
MINOR=$(grep "def verMinor" app/build.gradle | awk '{print $4}')
PATCH=$(grep "def verPatch" app/build.gradle | awk '{print $4}')
TAG_NAME="v1.${MINOR}.${PATCH}"

echo "=================================================="
echo "🔔 Git 發版追溯標記指南"
echo "--------------------------------------------------"
echo "請在專案合併回 main 分支後，執行以下指令打上發版 Tag："
echo "git tag -a $TAG_NAME -m \"TaiwanStar 發版 - 版本 $TAG_NAME\""
echo "git push origin $TAG_NAME"
echo "=================================================="
```
