chore: 更新gitignore与CI工作流
- 更新.gitignore - 更新.github/workflows/README.md - 新增android-release.yml Android发布流程 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
109
.github/workflows/README.md
vendored
109
.github/workflows/README.md
vendored
@@ -204,3 +204,112 @@ env:
|
||||
- 工作流文件:`.github/workflows/docker-build-deploy.yml`
|
||||
- Dockerfile:`api/Dockerfile`
|
||||
- Docker Compose:`api/docker-compose.yml`
|
||||
|
||||
---
|
||||
|
||||
# Android Release 工作流
|
||||
|
||||
## 概述
|
||||
|
||||
自动构建生产环境签名 APK 并发布到 GitHub Releases:
|
||||
|
||||
1. **构建阶段**:编译 Release APK,使用生产签名配置
|
||||
2. **发布阶段**:创建 GitHub Release,将 APK 作为附件上传
|
||||
|
||||
## 工作流触发条件
|
||||
|
||||
- **Tag 推送**:推送 `v*` 格式的标签时自动触发(如 `v1.0.0`、`v1.2.3-beta`)
|
||||
- **手动触发**:通过 GitHub Actions 界面手动触发,可选指定版本号
|
||||
|
||||
## 配置步骤
|
||||
|
||||
### 1. 配置 GitHub Secrets
|
||||
|
||||
在 GitHub 仓库设置中添加以下 Secrets:
|
||||
|
||||
| Secret | 说明 | 示例 |
|
||||
|--------|------|------|
|
||||
| `ANDROID_KEYSTORE_BASE64` | keystore 文件的 Base64 编码 | 见下方生成方法 |
|
||||
| `ANDROID_KEY_ALIAS` | 密钥别名 | `suiyueshishu` |
|
||||
| `ANDROID_KEY_PASSWORD` | 密钥密码 | |
|
||||
| `ANDROID_STORE_PASSWORD` | keystore 密码 | |
|
||||
|
||||
#### 生成 ANDROID_KEYSTORE_BASE64
|
||||
|
||||
```bash
|
||||
# 在本地项目 app-android 目录下执行
|
||||
base64 -i release-keystore.jks | pbcopy # macOS,结果已复制到剪贴板
|
||||
|
||||
# Linux
|
||||
base64 -w 0 release-keystore.jks
|
||||
```
|
||||
|
||||
将输出内容粘贴到 GitHub Secrets 的 `ANDROID_KEYSTORE_BASE64` 中。
|
||||
|
||||
### 2. 版本号管理
|
||||
|
||||
- **Tag 触发**:从 tag 名自动提取版本号(如 `v1.0.0` → `versionName = "1.0.0"`)
|
||||
- **手动触发**:使用输入的 `version_name`,或使用 `build.gradle.kts` 中的默认值
|
||||
- **versionCode**:自动使用 `GITHUB_RUN_NUMBER` 递增
|
||||
|
||||
### 3. 发布流程
|
||||
|
||||
#### 方式一:通过 Tag 自动发布
|
||||
|
||||
```bash
|
||||
# 打标签并推送
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
|
||||
# 或者一步完成
|
||||
git tag v1.0.0 && git push origin v1.0.0
|
||||
```
|
||||
|
||||
工作流将自动:
|
||||
1. 构建签名 Release APK
|
||||
2. 上传 APK 为 GitHub Artifact
|
||||
3. 创建 GitHub Release(附带 APK 和自动生成的更新日志)
|
||||
|
||||
#### 方式二:手动触发
|
||||
|
||||
1. 进入仓库的 Actions 标签页
|
||||
2. 选择 "Android Release Build" 工作流
|
||||
3. 点击 "Run workflow" 按钮
|
||||
4. (可选)填写版本号
|
||||
5. 点击 "Run workflow"
|
||||
|
||||
手动触发时仅构建 APK 并上传为 Artifact,不会创建 GitHub Release。
|
||||
|
||||
## 工作流执行流程
|
||||
|
||||
1. 检出代码(完整历史)
|
||||
2. 确定版本号(Tag / 手动输入 / build.gradle.kts 默认值)
|
||||
3. 设置 JDK 17 + Gradle 缓存
|
||||
4. 解码签名 keystore + 生成 keystore.properties
|
||||
5. 覆盖 build.gradle.kts 中的版本号
|
||||
6. 执行 `./gradlew assembleRelease`
|
||||
7. 上传 APK 为 Artifact(保留 30 天)
|
||||
8. (Tag 触发时)生成 Release Notes 并创建 GitHub Release
|
||||
|
||||
## APK 命名规则
|
||||
|
||||
APK 文件命名格式:`岁月史书_v{版本号}_release.apk`
|
||||
|
||||
示例:`岁月史书_v1.0.0_release.apk`
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 1. 签名失败
|
||||
|
||||
- 检查 `ANDROID_KEYSTORE_BASE64` 是否正确生成(不能有换行或空格)
|
||||
- 确认 `ANDROID_KEY_ALIAS`、`ANDROID_KEY_PASSWORD`、`ANDROID_STORE_PASSWORD` 与 keystore 匹配
|
||||
|
||||
### 2. 构建失败
|
||||
|
||||
- 检查 Actions 日志中的 Gradle 错误输出
|
||||
- 确认本地 `./gradlew assembleRelease` 可以成功
|
||||
|
||||
### 3. Release 创建失败
|
||||
|
||||
- 确认工作流有 `contents: write` 权限
|
||||
- 检查 Tag 名称是否以 `v` 开头
|
||||
|
||||
207
.github/workflows/android-release.yml
vendored
Normal file
207
.github/workflows/android-release.yml
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
name: Android Release Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*' # 推送 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. 覆盖版本号(如果从 Tag 或手动输入获取)
|
||||
# -----------------------------------------------------------------------
|
||||
- name: Update version in build.gradle.kts
|
||||
working-directory: app-android
|
||||
run: |
|
||||
VERSION_NAME="${{ steps.version.outputs.version_name }}"
|
||||
VERSION_CODE="${{ steps.version.outputs.version_code }}"
|
||||
|
||||
echo "更新版本: versionName=${VERSION_NAME}, versionCode=${VERSION_CODE}"
|
||||
|
||||
sed -i "s/versionCode = [0-9]*/versionCode = ${VERSION_CODE}/" app/build.gradle.kts
|
||||
sed -i "s/versionName = \"[^\"]*\"/versionName = \"${VERSION_NAME}\"/" app/build.gradle.kts
|
||||
|
||||
echo "验证更新后的版本信息:"
|
||||
grep -E 'versionCode|versionName' app/build.gradle.kts
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 7. 构建 Release APK
|
||||
# -----------------------------------------------------------------------
|
||||
- name: Build Release APK
|
||||
working-directory: app-android
|
||||
run: |
|
||||
echo "开始构建 Release APK..."
|
||||
chmod +x gradlew
|
||||
./gradlew assembleRelease --no-daemon
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 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 }}
|
||||
Reference in New Issue
Block a user