Repository navigation
Merge branch 'fix/ui-adjustments' #23
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Android Release | |
| # 打 Tag(如 v1.0.0)时触发:构建三个签名 Release APK | |
| # - universal : arm64-v8a + x86_64,含 arm + x86 两套容器镜像(通用,体积最大) | |
| # - armsolo : 仅 arm64-v8a,仅含 arm 镜像(真机首选,体积约为 universal 的一半) | |
| # - x86solo : 仅 x86_64,仅含 x86 镜像(x86 模拟器/Chromebook,体积约为 universal 的一半) | |
| # 创建 GitHub Release 并把三个 APK 作为资产发布。 | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| # 允许在 Actions 页面手动触发,便于验证构建链路。 | |
| workflow_dispatch: | |
| jobs: | |
| build: | |
| name: Build & Release | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # 创建 Release 与上传资产需要写权限 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| # versionCode 由 build.gradle.kts 的 gitCommitCount() 取 git rev-list --count HEAD, | |
| # 浅克隆(默认 depth=1)会得到 1,必须拉完整历史,否则 Release APK 版本号错乱。 | |
| fetch-depth: 0 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| - name: Set up Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| with: | |
| # 复用 Gradle 构建缓存,加速后续构建。 | |
| cache-read-only: ${{ github.ref != 'refs/heads/main' }} | |
| # 在构建前校验:tag 名的版本部分必须与 app/build.gradle.kts 的 versionName 一致。 | |
| # 防止"改了发版 tag 但忘记改 versionName"导致 Release 版本号与 APK 实际版本对不上。 | |
| # 仅打 tag 触发时校验;workflow_dispatch(手动,无 tag)跳过。 | |
| # 兼容 rc 后缀:v1.0.0 与 v1.0.0-rc2 都要求 versionName=1.0.0。 | |
| - name: Verify tag matches versionName | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| set -e | |
| ver_in_code=$(grep -E '^\s*versionName\s*=' app/build.gradle.kts | head -1 | sed -E 's/.*"([^"]+)".*/\1/') | |
| tag_ver="${GITHUB_REF_NAME#v}" # 1.0.0 / 1.0.0-rc2 / ... | |
| tag_base="${tag_ver%%-*}" # 1.0.0(去掉 -rc2 后缀) | |
| echo "build.gradle.kts versionName = $ver_in_code" | |
| echo "tag version part (without v) = $tag_ver" | |
| if [[ -z "$ver_in_code" ]]; then | |
| echo "::error::未能从 app/build.gradle.kts 解析出 versionName,检查格式是否为 versionName = \"x.y.z\"" | |
| exit 1 | |
| fi | |
| if [[ "$tag_base" != "$ver_in_code" ]]; then | |
| echo "::error::tag 版本部分 ($tag_base) 与 versionName ($ver_in_code) 不一致。请先在 app/build.gradle.kts 改好 versionName,再重新打 tag 或用正确的 tag。" | |
| exit 1 | |
| fi | |
| echo "✓ tag 与 versionName 一致: $ver_in_code(tag 后缀: ${tag_ver#$tag_base})" | |
| # 校验 versionCode 单调递增:versionCode = git commit count,rebase/squash 改写历史 | |
| # 可能让 commit count 变小,导致新版本 versionCode < 旧版本、升级判定失效。 | |
| # 取按提交时间倒序最近的非当前 tag,算其 commit count,要求当前 HEAD 不小于上个 Release。 | |
| # 允许相等:rc 转正式版时常在同一提交上打 tag,versionCode 相等是正常的。 | |
| # 仅打 tag 触发时校验;workflow_dispatch(手动,无 tag)跳过。 | |
| - name: Verify versionCode monotonic | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| set -e | |
| cur_vc=$(git rev-list --count HEAD) | |
| prev_tag=$(git tag --sort=-creatordate | grep -vE "^${GITHUB_REF_NAME}$" | head -1) | |
| if [[ -z "$prev_tag" ]]; then | |
| echo "无历史 tag,跳过 versionCode 单调校验(首次发版)" | |
| echo "当前 versionCode(commit count) = $cur_vc" | |
| exit 0 | |
| fi | |
| prev_vc=$(git rev-list --count "$prev_tag") | |
| echo "上个 Release($prev_tag) versionCode = $prev_vc" | |
| echo "当前(${GITHUB_REF_NAME}) versionCode = $cur_vc" | |
| if [[ "$cur_vc" -lt "$prev_vc" ]]; then | |
| echo "::error::versionCode 回退: 当前 $cur_vc < 上个 Release($prev_tag) $prev_vc。通常因 rebase/squash 改写历史使 commit count 变小。请确保新版本在旧 tag 之上线性推进。" | |
| exit 1 | |
| fi | |
| echo "✓ versionCode 单调递增: $prev_vc -> $cur_vc" | |
| - name: Restore release keystore | |
| env: | |
| # keystore 文件以 base64 存入 Secret,避免二进制文件被破坏。 | |
| # 还原到 app/aicode.jks,与 build.gradle.kts 期望的位置一致。 | |
| KEYSTORE_BASE64: ${{ secrets.AICODE_KEYSTORE_BASE64 }} | |
| run: | | |
| echo "$KEYSTORE_BASE64" | base64 -d > app/aicode.jks | |
| echo "keystore restored ($(stat -c%s app/aicode.jks) bytes)" | |
| - name: Generate keystore.properties | |
| env: | |
| KEYSTORE_PASSWORD: ${{ secrets.AICODE_KEYSTORE_PASSWORD }} | |
| KEY_ALIAS: ${{ secrets.AICODE_KEY_ALIAS }} | |
| KEY_PASSWORD: ${{ secrets.AICODE_KEY_PASSWORD }} | |
| run: | | |
| cat > app/keystore.properties <<EOF | |
| storeFile=aicode.jks | |
| storePassword=$KEYSTORE_PASSWORD | |
| keyAlias=$KEY_ALIAS | |
| keyPassword=$KEY_PASSWORD | |
| EOF | |
| echo "app/keystore.properties generated" | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x ./gradlew | |
| # 发版质量门禁:跑单元测试,失败即中止,避免发出回归包。 | |
| # 只跑 universal debug 变体:单测逻辑与 flavor 无关,跑全 flavor 6 变体是重复编译。 | |
| - name: Run unit tests | |
| run: ./gradlew :app:testUniversalDebugUnitTest | |
| # 一次性构建全部 flavor 的 release APK(universal/armsolo/x86solo) | |
| # --stacktrace:构建失败时输出完整调用栈,便于在 CI 定位 OOM/内存类根因。 | |
| - name: Build all flavor Release APKs | |
| run: ./gradlew assembleRelease --stacktrace | |
| # 给三个 APK 重命名为「包名-架构-版本」格式,架构用真实 ABI 名便于用户识别: | |
| # aicode-arm64-v1.0.0-rc1.apk (armsolo) | |
| # aicode-x86_64-v1.0.0-rc1.apk (x86solo) | |
| # aicode-universal-v1.0.0-rc1.apk (universal,含两套镜像) | |
| - name: Rename APKs by flavor | |
| run: | | |
| mkdir -p dist | |
| ver="${GITHUB_REF_NAME:-manual}" # tag 如 v1.0.0-rc1;手动触发时为 manual-N | |
| declare -A abi=( [armsolo]=arm64 [x86solo]=x86_64 [universal]=universal ) | |
| for f in universal armsolo x86solo; do | |
| src="app/build/outputs/apk/$f/release/app-$f-release.apk" | |
| if [[ -f "$src" ]]; then | |
| out="dist/aicode-${abi[$f]}-${ver}.apk" | |
| cp "$src" "$out" | |
| echo "packaged $out" | |
| else | |
| echo "::warning::missing $src" | |
| fi | |
| done | |
| ls -lh dist/ | |
| - name: Determine release name | |
| id: release | |
| run: | | |
| # 手动触发(无 tag)时用 run id 作为临时名,避免破坏 tag 发布。 | |
| if [[ "${{ github.ref }}" == refs/tags/* ]]; then | |
| echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "tag=manual-${GITHUB_RUN_NUMBER}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create GitHub Release & Upload assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.release.outputs.tag }} | |
| name: Release ${{ steps.release.outputs.tag }} | |
| generate_release_notes: true | |
| # 产物名约定 aicode-<abi>-<版本>.apk(见 Rename 步骤),故 glob 把 ABI 段放 aicode- 紧后、版本段用 * 兜底。 | |
| # 不要写成 aicode-*-<abi>.apk——那会把 abi 误放末段,与 aicode-<abi>-<ver> 永远匹配不上(rc3 翻车原因)。 | |
| files: | | |
| dist/aicode-universal-*.apk | |
| dist/aicode-arm64-*.apk | |
| dist/aicode-x86_64-*.apk | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |