Files
life-echo/.github/workflows/app-expo-deploy.yml
2026-03-22 18:17:43 +08:00

211 lines
7.1 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# App ExpoCI 内生成 Android 签名 APKexpo prebuild + Gradle assembleRelease
# 使用 app-expo/plugins/withAndroidReleaseSigning在 android/app 放置 keystore.properties + jks 后打 release 包。
#
# 环境映射(按触发源自动推断):
# main → dev (开发 + 内部测试)
# v*.*.* → prod (正式发布 + GitHub Release 附带 APK)
#
# 手动触发workflow_dispatch 可选择 dev / stage / prod
#
# Repository secrets与 android-release.yml 共用同一套即可):
# ANDROID_KEYSTORE_BASE64 / ANDROID_STORE_PASSWORD / ANDROID_KEY_ALIAS / ANDROID_KEY_PASSWORD
name: App Expo Deploy
on:
push:
branches: [main]
tags: ['v*.*.*']
paths:
- "app-expo/**"
- ".github/workflows/app-expo-deploy.yml"
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
type: choice
options:
- dev
- stage
- prod
default: dev
version:
description: '版本号 (prod 手动发版时使用,如 1.0.0)'
required: false
type: string
concurrency:
group: app-expo-deploy-${{ github.ref }}
cancel-in-progress: true
env:
APP_NAME: app-expo
jobs:
deploy:
name: "Android APK"
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: ${{ startsWith(github.ref, 'refs/tags/') && '0' || '1' }}
- name: Determine environment
id: env
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
echo "env=prod"
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "env=${{ github.event.inputs.environment }}"
else
echo "env=dev"
fi >> $GITHUB_OUTPUT
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: app-expo/package-lock.json
- name: Install dependencies
working-directory: app-expo
run: npm ci
- name: Quality checks (dev only)
if: steps.env.outputs.env == 'dev'
working-directory: app-expo
run: |
npm run format:check
npm run lint
npm run test:ci
- name: Set API environment
working-directory: app-expo
run: |
case "${{ steps.env.outputs.env }}" in
prod) node scripts/use-env.js production ;;
stage) node scripts/use-env.js staging ;;
*) node scripts/use-env.js development ;;
esac
- name: Determine version
id: version
working-directory: app-expo
run: |
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
TAG_NAME="${GITHUB_REF#refs/tags/}"
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ -n "${{ github.event.inputs.version }}" ]]; then
VERSION="${{ github.event.inputs.version }}"
TAG_NAME="v${VERSION}"
else
VERSION=$(node -p "require('./package.json').version")
TAG_NAME="v${VERSION}"
fi
VERSION_CODE="${GITHUB_RUN_NUMBER}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
echo "version_code=${VERSION_CODE}" >> $GITHUB_OUTPUT
echo "apk_name=${{ env.APP_NAME }}-v${VERSION}-release.apk" >> $GITHUB_OUTPUT
echo "版本名: ${VERSION}, versionCode: ${VERSION_CODE}, tag: ${TAG_NAME}"
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
- name: Expo prebuild (Android)
working-directory: app-expo
run: npx expo prebuild --platform android --clean
- name: Decode keystore
working-directory: app-expo/android/app
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > release-keystore.jks
cat > keystore.properties <<EOF
storeFile=release-keystore.jks
storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}
keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}
keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}
EOF
sed -i 's/^[[:space:]]*//' keystore.properties
- name: Build release APK
working-directory: app-expo/android
env:
GRADLE_OPTS: '-Xmx4g -Dorg.gradle.daemon=false'
run: |
chmod +x gradlew
./gradlew assembleRelease --no-daemon \
-PversionName="${{ steps.version.outputs.version }}" \
-PversionCode="${{ steps.version.outputs.version_code }}"
- name: Locate APK
id: apk
working-directory: app-expo/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_path=${FINAL_PATH}" >> $GITHUB_OUTPUT
du -h "$FINAL_PATH"
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.version.outputs.apk_name }}
path: app-expo/android/${{ steps.apk.outputs.apk_path }}
retention-days: ${{ steps.env.outputs.env == 'prod' && '90' || (steps.env.outputs.env == 'dev' && '14' || '30') }}
- name: Generate Release Notes (prod)
if: steps.env.outputs.env == 'prod' && startsWith(github.ref, 'refs/tags/')
id: release_notes
run: |
TAG_NAME="${{ steps.version.outputs.tag_name }}"
PREV_TAG=$(git tag --sort=-creatordate | grep '^v' | sed -n '2p')
if [ -n "$PREV_TAG" ]; then
CHANGES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges)
else
CHANGES=$(git log --pretty=format:"- %s (%h)" --no-merges -20)
fi
{
echo "notes<<EOF"
echo "## ${{ env.APP_NAME }} ${TAG_NAME}"
echo ""
echo "### 更新内容"
echo ""
echo "${CHANGES}"
echo ""
echo "---"
echo "构建编号: #${GITHUB_RUN_NUMBER}"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Create GitHub Release (prod)
if: steps.env.outputs.env == 'prod'
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 || 'Release' }}
draft: false
prerelease: false
files: app-expo/android/${{ steps.apk.outputs.apk_path }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}