Files
opencode/skill/gitea/workflow-templates/android-app.md
Voson 5a05d5ab53 chore: 重构 OpenCode 命令和技能文档体系
- 新增:统一的 git 命令文档(add/commit/push/pull 等)
- 新增:整合的 Gitea 技能文档(API、运行器、工作流等)
- 新增:工作流模板(Android、Go、Node.js 等)
- 移除:已弃用的旧命令脚本和发布脚本
- 改进:.gitignore 添加敏感文件保护规则
- 改进:AGENTS.md 完善了开发规范和示例

此次重组统一了命令和技能的文档结构,便于后续维护和扩展。
2026-01-13 00:27:21 +08:00

12 KiB
Raw Blame History

Android 应用 Workflow 模板

适用于 Android 应用的 CI/CD workflow支持 APK 构建、签名和发布。

适用场景

  • Kotlin / Java Android 应用
  • Jetpack Compose 项目
  • 需要签名发布的 APK
  • 需要创建 Release 的项目

环境要求

依赖 Runner 要求
JDK 17+ Runner 主机已安装
Android SDK Runner 主机已安装
Gradle 由 wrapper 管理

重要:必须使用 darwin-arm64 标签的 macOS Runner因为 Google 不提供 Linux ARM64 版本的 Android SDK。

Workflow 骨架模板

name: Android - Build & Release APK

on:
  push:
    paths:
      - 'android/**'                        # 修改为实际目录
      - '.gitea/workflows/android.yml'
    tags:
      - 'android-*'                         # 修改为实际 tag 前缀

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  SERVICE_PREFIX: android                   # 修改为实际服务名
  SERVICE_DIR: android                      # 修改为实际目录名
  APP_NAME: myapp-android                   # 修改为实际应用名

jobs:
  build:
    name: Build Release APK
    runs-on: darwin-arm64                   # macOS ARM64必须
    permissions:
      contents: read
      packages: write
    
    outputs:
      version_name: ${{ steps.vars.outputs.version_name }}
      version_code: ${{ steps.vars.outputs.version_code }}
      apk_name: ${{ steps.vars.outputs.apk_name }}
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Verify Java environment
        run: |
          java -version
          echo "JAVA_HOME=$JAVA_HOME"

      - name: Setup Android SDK
        run: |
          if [ -n "$ANDROID_HOME" ] && [ -d "$ANDROID_HOME" ]; then
            echo "Using ANDROID_HOME: $ANDROID_HOME"
          else
            for SDK_PATH in ~/Library/Android/sdk ~/android-sdk /opt/homebrew/share/android-commandlinetools; do
              if [ -d "$SDK_PATH" ]; then
                export ANDROID_HOME=$SDK_PATH
                echo "Found Android SDK: $SDK_PATH"
                break
              fi
            done
          fi
          
          if [ -z "$ANDROID_HOME" ] || [ ! -d "$ANDROID_HOME" ]; then
            echo "ERROR: Android SDK not found"
            exit 1
          fi
          
          echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV
          echo "$ANDROID_HOME/cmdline-tools/latest/bin" >> $GITHUB_PATH
          echo "$ANDROID_HOME/platform-tools" >> $GITHUB_PATH

      - name: Get gradle-hashfiles
        id: hash-gradle
        working-directory: ${{ env.SERVICE_DIR }}
        run: |
          HASH=$(sha256sum gradle/libs.versions.toml app/build.gradle.kts build.gradle.kts settings.gradle.kts 2>/dev/null | sha256sum | awk '{print $1}' | head -c 16)
          echo "hash=${HASH}" >> "$GITHUB_OUTPUT"

      - name: Cache Gradle
        uses: https://github.com/actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: gradle-${{ env.SERVICE_PREFIX }}-${{ steps.hash-gradle.outputs.hash }}
          restore-keys: gradle-${{ env.SERVICE_PREFIX }}-

      - name: Set variables
        id: vars
        run: |
          # 从 build.gradle.kts 提取版本信息(按需修改路径和正则)
          version_name=$(grep 'versionName' ${{ env.SERVICE_DIR }}/app/build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/')
          version_code=$(grep 'versionCode' ${{ env.SERVICE_DIR }}/app/build.gradle.kts | head -1 | sed 's/[^0-9]*//g')
          git_tag=$(git describe --tags --abbrev=0 --always)
          apk_name="${{ env.APP_NAME }}-${version_name}"
          
          {
            echo "version_name=${version_name}"
            echo "version_code=${version_code}"
            echo "git_tag=${git_tag}"
            echo "apk_name=${apk_name}"
          } >> $GITHUB_ENV
          
          echo "version_name=${version_name}" >> $GITHUB_OUTPUT
          echo "version_code=${version_code}" >> $GITHUB_OUTPUT
          echo "apk_name=${apk_name}" >> $GITHUB_OUTPUT

      - name: Setup signing
        env:
          KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
          KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
          KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
        run: |
          # 创建 local.properties
          echo "sdk.dir=$ANDROID_HOME" > ${{ env.SERVICE_DIR }}/local.properties
          
          # 配置签名(如果提供了 keystore
          if [ -n "$KEYSTORE_BASE64" ]; then
            echo "$KEYSTORE_BASE64" | base64 -d > ${{ env.SERVICE_DIR }}/release.keystore
            {
              echo "KEYSTORE_FILE=../release.keystore"
              echo "KEYSTORE_PASSWORD=$KEYSTORE_PASSWORD"
              echo "KEY_ALIAS=$KEY_ALIAS"
              echo "KEY_PASSWORD=$KEY_PASSWORD"
            } >> ${{ env.SERVICE_DIR }}/local.properties
            echo "Signing configured"
          else
            echo "WARNING: No signing key provided, building unsigned APK"
          fi

      - name: Build Release APK
        working-directory: ${{ env.SERVICE_DIR }}
        run: |
          chmod +x gradlew
          
          # ============================================
          # 用户自定义构建步骤(按项目需求修改)
          # ============================================
          
          # 清理(按需启用)
          # ./gradlew clean
          
          # 单元测试(按需启用)
          # ./gradlew test
          
          # 构建 Release APK必须
          ./gradlew assembleRelease --no-daemon

      - name: Rename APK
        run: |
          mkdir -p dist
          cp ${{ env.SERVICE_DIR }}/app/build/outputs/apk/release/app-release.apk dist/${{ env.apk_name }}.apk
          cd dist
          sha256sum ${{ env.apk_name }}.apk > ${{ env.apk_name }}.apk.sha256

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: ${{ env.SERVICE_PREFIX }}-apk
          path: dist/

      - name: Notify
        if: always()
        continue-on-error: true
        env:
          WEBHOOK_URL: ${{ vars.WEBHOOK_URL }}
        run: |
          status="${{ job.status }}"
          [ "$status" = "success" ] && status_text="Build Success" || status_text="Build Failed"
          
          curl -s -H "Content-Type: application/json" -X POST -d \
            "{\"msg_type\":\"text\",\"content\":{\"text\":\"${{ env.APP_NAME }} ${status_text}\\nVersion: ${{ env.version_name }}\"}}" \
            "$WEBHOOK_URL" || true

  release:
    name: Create Release
    runs-on: darwin-arm64
    needs: build
    if: startsWith(github.ref, 'refs/tags/')
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: ${{ env.SERVICE_PREFIX }}-apk
          path: dist

      - name: Create Release
        env:
          GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
          VERSION_NAME: ${{ needs.build.outputs.version_name }}
          APK_NAME: ${{ needs.build.outputs.apk_name }}
        run: |
          git_tag=$(git describe --tags --abbrev=0)
          api_url="${{ github.server_url }}/api/v1"
          repo="${{ github.repository }}"
          
          # 创建 Release
          release_id=$(curl -s -X POST \
            -H "Authorization: token $GITEA_TOKEN" \
            -H "Content-Type: application/json" \
            -d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION_NAME})\"}" \
            "${api_url}/repos/${repo}/releases" | jq -r '.id')
          
          # 上传 APK
          curl -s -X POST \
            -H "Authorization: token $GITEA_TOKEN" \
            -F "attachment=@dist/${APK_NAME}.apk" \
            "${api_url}/repos/${repo}/releases/${release_id}/assets"
          
          # 上传校验和
          curl -s -X POST \
            -H "Authorization: token $GITEA_TOKEN" \
            -F "attachment=@dist/${APK_NAME}.apk.sha256" \
            "${api_url}/repos/${repo}/releases/${release_id}/assets"

签名配置

1. 生成签名密钥

keytool -genkey -v -keystore release.keystore \
  -alias myapp \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000

2. Base64 编码

base64 -i release.keystore -o keystore.base64
# 将 keystore.base64 内容复制到 ANDROID_KEYSTORE_BASE64 secret

3. build.gradle.kts 签名配置

android {
    signingConfigs {
        create("release") {
            val props = Properties()
            val propsFile = rootProject.file("local.properties")
            if (propsFile.exists()) {
                props.load(propsFile.inputStream())
                val keystoreFile = props.getProperty("KEYSTORE_FILE", "")
                if (keystoreFile.isNotEmpty()) {
                    storeFile = file(keystoreFile)
                    storePassword = props.getProperty("KEYSTORE_PASSWORD", "")
                    keyAlias = props.getProperty("KEY_ALIAS", "")
                    keyPassword = props.getProperty("KEY_PASSWORD", "")
                }
            }
        }
    }
    
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
            signingConfig = signingConfigs.getByName("release")
        }
    }
}

缓存配置

缓存路径

path: |
  ~/.gradle/caches       # Gradle 依赖缓存
  ~/.gradle/wrapper      # Gradle wrapper

Key 计算

HASH=$(sha256sum gradle/libs.versions.toml app/build.gradle.kts build.gradle.kts settings.gradle.kts | sha256sum | awk '{print $1}' | head -c 16)

Secrets 配置

Secret 用途
ANDROID_KEYSTORE_BASE64 Base64 编码的 keystore 文件
ANDROID_KEYSTORE_PASSWORD keystore 密码
ANDROID_KEY_ALIAS 密钥别名
ANDROID_KEY_PASSWORD 密钥密码
RELEASE_TOKEN Gitea API 令牌(创建 Release

Gradle 优化

gradle.properties

# CI 环境优化
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configureondemand=true

# Android 配置
android.useAndroidX=true
android.nonTransitiveRClass=true

国内镜像(可选)

# gradle-wrapper.properties
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip

版本提取

build.gradle.kts 提取版本信息:

# versionName
version_name=$(grep 'versionName' app/build.gradle.kts | head -1 | sed 's/.*"\(.*\)".*/\1/')

# versionCode
version_code=$(grep 'versionCode' app/build.gradle.kts | head -1 | sed 's/[^0-9]*//g')

常见 Gradle 任务

任务 说明
./gradlew assembleRelease 构建 Release APK
./gradlew assembleDebug 构建 Debug APK
./gradlew bundleRelease 构建 AABApp Bundle
./gradlew test 运行单元测试
./gradlew lint 运行 Lint 检查
./gradlew clean 清理构建产物

使用步骤

  1. 复制上方 workflow 模板到 .gitea/workflows/android.yml
  2. 修改 SERVICE_PREFIXSERVICE_DIRAPP_NAME 为实际值
  3. 修改 pathstags 触发条件
  4. 配置 build.gradle.kts 签名(参考上方示例)
  5. 配置 Secretskeystore、密码等
  6. 推送代码触发 workflow

版本发布

# 创建并推送 tag
git tag android-1.0.0
git push origin android-1.0.0