213 lines
8.5 KiB
YAML
213 lines
8.5 KiB
YAML
name: Android Release Build
|
||
|
||
on:
|
||
push:
|
||
tags:
|
||
- 'v*.*.*' # SemVer tag(如 v1.0.0)时自动触发
|
||
workflow_dispatch: # 支持手动触发
|
||
inputs:
|
||
version_name:
|
||
description: '版本名(如 1.0.1),留空则使用 build.gradle.kts 中的默认值'
|
||
required: false
|
||
type: string
|
||
|
||
env:
|
||
APP_NAME: 岁月时书
|
||
|
||
jobs:
|
||
build-release-apk:
|
||
name: Build Release APK
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: write # 需要 write 权限以创建 Release
|
||
|
||
steps:
|
||
# -----------------------------------------------------------------------
|
||
# 1. 检出代码
|
||
# -----------------------------------------------------------------------
|
||
- name: Checkout code
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0 # 完整历史,用于生成 Release Notes
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 2. 确定版本号
|
||
# -----------------------------------------------------------------------
|
||
- name: Determine version
|
||
id: version
|
||
run: |
|
||
if [ "${{ github.event_name }}" = "push" ] && [[ "${{ github.ref }}" == refs/tags/v* ]]; then
|
||
# Tag 触发:从 tag 名提取版本号(去掉 v 前缀)
|
||
VERSION_NAME="${GITHUB_REF#refs/tags/v}"
|
||
TAG_NAME="${GITHUB_REF#refs/tags/}"
|
||
elif [ -n "${{ github.event.inputs.version_name }}" ]; then
|
||
# 手动触发且提供了版本号
|
||
VERSION_NAME="${{ github.event.inputs.version_name }}"
|
||
TAG_NAME="v${VERSION_NAME}"
|
||
else
|
||
# 手动触发但未提供版本号,从 build.gradle.kts 读取
|
||
VERSION_NAME=$(grep 'versionName' app-android/app/build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||
TAG_NAME="v${VERSION_NAME}"
|
||
fi
|
||
|
||
# versionCode 使用 GitHub Run Number 自增
|
||
VERSION_CODE="${GITHUB_RUN_NUMBER}"
|
||
|
||
echo "version_name=${VERSION_NAME}" >> $GITHUB_OUTPUT
|
||
echo "version_code=${VERSION_CODE}" >> $GITHUB_OUTPUT
|
||
echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
|
||
echo "apk_name=${APP_NAME}_v${VERSION_NAME}_release.apk" >> $GITHUB_OUTPUT
|
||
|
||
echo "版本名: ${VERSION_NAME}"
|
||
echo "版本号: ${VERSION_CODE}"
|
||
echo "Tag: ${TAG_NAME}"
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 3. 设置 JDK
|
||
# -----------------------------------------------------------------------
|
||
- name: Set up JDK 17
|
||
uses: actions/setup-java@v4
|
||
with:
|
||
java-version: '17'
|
||
distribution: 'temurin'
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 4. 设置 Gradle 缓存
|
||
# -----------------------------------------------------------------------
|
||
- name: Setup Gradle
|
||
uses: gradle/actions/setup-gradle@v4
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 5. 解码签名文件
|
||
# -----------------------------------------------------------------------
|
||
- name: Decode keystore
|
||
working-directory: app-android
|
||
run: |
|
||
echo "解码 keystore 文件..."
|
||
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > release-keystore.jks
|
||
|
||
echo "生成 keystore.properties..."
|
||
cat > keystore.properties <<EOF
|
||
storeFile=../release-keystore.jks
|
||
storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}
|
||
keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
|
||
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
|
||
EOF
|
||
|
||
# 去除每行开头的空格(heredoc 缩进导致)
|
||
sed -i 's/^[[:space:]]*//' keystore.properties
|
||
|
||
echo "签名配置就绪"
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 6. 记录版本注入参数(通过 Gradle project properties 传入,避免修改 Kotlin DSL)
|
||
# -----------------------------------------------------------------------
|
||
- name: Log version build inputs
|
||
working-directory: app-android
|
||
run: |
|
||
VERSION_NAME="${{ steps.version.outputs.version_name }}"
|
||
VERSION_CODE="${{ steps.version.outputs.version_code }}"
|
||
|
||
#region agent log
|
||
echo "[agent][H3] 使用 Gradle 属性注入版本号,避免 sed 修改 build.gradle.kts"
|
||
echo "[agent][H3] versionName=${VERSION_NAME}"
|
||
echo "[agent][H3] versionCode=${VERSION_CODE}"
|
||
echo "[agent][H3] build.gradle.kts 当前仍保留变量引用:"
|
||
grep -E 'versionCode = appVersionCode|versionName = appVersionName' app/build.gradle.kts
|
||
#endregion
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 7. 构建 Release APK
|
||
# -----------------------------------------------------------------------
|
||
- name: Build Release APK
|
||
working-directory: app-android
|
||
run: |
|
||
echo "开始构建 Release APK..."
|
||
chmod +x gradlew
|
||
VERSION_NAME="${{ steps.version.outputs.version_name }}"
|
||
VERSION_CODE="${{ steps.version.outputs.version_code }}"
|
||
#region agent log
|
||
echo "[agent][H3] 执行: ./gradlew assembleRelease --no-daemon -PversionName=${VERSION_NAME} -PversionCode=${VERSION_CODE}"
|
||
#endregion
|
||
./gradlew assembleRelease --no-daemon -PversionName="${VERSION_NAME}" -PversionCode="${VERSION_CODE}"
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 8. 查找并重命名 APK
|
||
# -----------------------------------------------------------------------
|
||
- name: Locate APK
|
||
id: apk
|
||
working-directory: app-android
|
||
run: |
|
||
APK_PATH=$(find app/build/outputs/apk/release -name "*.apk" | head -1)
|
||
if [ -z "$APK_PATH" ]; then
|
||
echo "错误:未找到 Release APK"
|
||
exit 1
|
||
fi
|
||
|
||
FINAL_NAME="${{ steps.version.outputs.apk_name }}"
|
||
FINAL_PATH="app/build/outputs/apk/release/${FINAL_NAME}"
|
||
mv "$APK_PATH" "$FINAL_PATH"
|
||
|
||
echo "APK 路径: ${FINAL_PATH}"
|
||
echo "APK 大小: $(du -h "$FINAL_PATH" | cut -f1)"
|
||
|
||
echo "apk_path=${FINAL_PATH}" >> $GITHUB_OUTPUT
|
||
echo "apk_name=${FINAL_NAME}" >> $GITHUB_OUTPUT
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 9. 上传 APK 为 Artifact(始终执行,手动触发时也可下载)
|
||
# -----------------------------------------------------------------------
|
||
- name: Upload APK artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: ${{ steps.apk.outputs.apk_name }}
|
||
path: app-android/${{ steps.apk.outputs.apk_path }}
|
||
retention-days: 30
|
||
|
||
# -----------------------------------------------------------------------
|
||
# 10. 创建 GitHub Release(仅 Tag 触发时执行)
|
||
# -----------------------------------------------------------------------
|
||
- name: Generate Release Notes
|
||
id: release_notes
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
run: |
|
||
TAG_NAME="${{ steps.version.outputs.tag_name }}"
|
||
|
||
# 获取上一个 tag
|
||
PREV_TAG=$(git tag --sort=-creatordate | grep '^v' | sed -n '2p')
|
||
|
||
if [ -n "$PREV_TAG" ]; then
|
||
echo "生成 ${PREV_TAG}..${TAG_NAME} 之间的变更记录"
|
||
CHANGES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges)
|
||
else
|
||
echo "首次发布,生成全部提交记录"
|
||
CHANGES=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
|
||
fi
|
||
|
||
# 写入多行输出
|
||
{
|
||
echo "notes<<EOF"
|
||
echo "## ${APP_NAME} ${TAG_NAME}"
|
||
echo ""
|
||
echo "### 更新内容"
|
||
echo ""
|
||
echo "${CHANGES}"
|
||
echo ""
|
||
echo "---"
|
||
echo "构建编号: #${GITHUB_RUN_NUMBER}"
|
||
echo "EOF"
|
||
} >> $GITHUB_OUTPUT
|
||
|
||
- name: Create GitHub Release
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
uses: softprops/action-gh-release@v2
|
||
with:
|
||
tag_name: ${{ steps.version.outputs.tag_name }}
|
||
name: "${{ env.APP_NAME }} ${{ steps.version.outputs.tag_name }}"
|
||
body: ${{ steps.release_notes.outputs.notes }}
|
||
draft: false
|
||
prerelease: false
|
||
files: app-android/${{ steps.apk.outputs.apk_path }}
|
||
env:
|
||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|