test.yml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. name: Test Build
  2. on:
  3. push:
  4. branches:
  5. - main
  6. paths:
  7. - '**'
  8. # - '!**/assets/**'
  9. # - '!**.md'
  10. - '!**/ISSUE_TEMPLATE/**'
  11. - '!**/modules/web/**'
  12. pull_request:
  13. paths-ignore:
  14. - '**/modules/web/**'
  15. workflow_run:
  16. workflows: [Build Web]
  17. branches: [master]
  18. types:
  19. - completed
  20. workflow_dispatch:
  21. permissions:
  22. contents: write
  23. concurrency:
  24. group: ${{ github.workflow }}-${{ github.ref }}
  25. cancel-in-progress: true
  26. jobs:
  27. prepare:
  28. runs-on: ubuntu-latest
  29. if: ${{ !startsWith(github.event.head_commit.message, 'Merge pull request') }}
  30. outputs:
  31. versionL: ${{ steps.set-ver.outputs.full_version_name }}
  32. version: ${{ steps.set-ver.outputs.current_version }}
  33. version_code: ${{ steps.set-ver.outputs.version_code }}
  34. latest_tag: ${{ steps.set-ver.outputs.last_tag }}
  35. is_release: ${{ steps.set-ver.outputs.is_release }}
  36. steps:
  37. - uses: actions/checkout@v4
  38. with:
  39. fetch-depth: 0
  40. - name: 计算版本号和构建号
  41. id: set-ver
  42. run: |
  43. PROPERTIES_FILE="./app/version.properties"
  44. if [[ ! -f "$PROPERTIES_FILE" ]]; then
  45. echo "错误:version.properties 文件不存在"
  46. exit 1
  47. fi
  48. # --- 1. 读取基础配置 ---
  49. MAJOR=$(grep 'VERSION_MAJOR' "$PROPERTIES_FILE" | cut -d'=' -f2 | tr -d '[:space:]')
  50. MINOR=$(grep 'VERSION_MINOR' "$PROPERTIES_FILE" | cut -d'=' -f2 | tr -d '[:space:]')
  51. PATCH=$(grep 'VERSION_PATCH' "$PROPERTIES_FILE" | cut -d'=' -f2 | tr -d '[:space:]')
  52. RELEASE_FLAG=$(grep 'VERSION_SUFFIX' "$PROPERTIES_FILE" | cut -d'=' -f2 | tr -d '[:space:]')
  53. CURRENT_VERSION="$MAJOR.$MINOR.$PATCH"
  54. # --- 2. 计算 Android versionCode ---
  55. TOTAL_COMMITS=$(git rev-list --count HEAD)
  56. VERSION_CODE=$((TOTAL_COMMITS))
  57. # --- 3. 确定版本类型和 Beta 序列号 ---
  58. IS_RELEASE="false"
  59. VERSION_SUFFIX_NAME=""
  60. if [[ "${RELEASE_FLAG}" == "0" ]]; then
  61. # 正式版逻辑
  62. IS_RELEASE="true"
  63. # 日志起点:找上一个不带 beta 的 tag
  64. LAST_TAG_FOR_LOG=$(git tag --list --sort=-committerdate | grep -v 'beta' | head -n 1)
  65. else
  66. # 预发布版逻辑 (计算 beta.X)
  67. # 查找匹配当前主版本号的最新 beta tag
  68. LAST_BETA_TAG=$(git tag --list "$CURRENT_VERSION-beta.*" --sort=-committerdate | head -n 1)
  69. if [[ "$LAST_BETA_TAG" =~ -beta\.([0-9]+) ]]; then
  70. BETA_NUM=${BASH_REMATCH[1]}
  71. NEW_BETA_NUM=$((BETA_NUM + 1))
  72. else
  73. NEW_BETA_NUM=1
  74. fi
  75. VERSION_SUFFIX_NAME="-beta.$NEW_BETA_NUM"
  76. # 日志起点:上一个任意 tag
  77. LAST_TAG_FOR_LOG=$(git tag --sort=-committerdate | head -n 1)
  78. fi
  79. # 如果完全没找到 Tag,则从第一个 commit 开始
  80. if [ -z "$LAST_TAG_FOR_LOG" ]; then
  81. LAST_TAG_FOR_LOG=$(git rev-list --max-parents=0 HEAD)
  82. fi
  83. FULL_VERSION_NAME="${CURRENT_VERSION}${VERSION_SUFFIX_NAME}"
  84. # --- 4. 设置输出 ---
  85. echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
  86. echo "full_version_name=$FULL_VERSION_NAME" >> $GITHUB_OUTPUT
  87. echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT
  88. echo "last_tag=$LAST_TAG_FOR_LOG" >> $GITHUB_OUTPUT
  89. echo "is_release=$IS_RELEASE" >> $GITHUB_OUTPUT
  90. echo "-------------------------------"
  91. echo "Full Name: $FULL_VERSION_NAME"
  92. echo "Version Code: $VERSION_CODE"
  93. echo "Is Release: $IS_RELEASE"
  94. echo "Log Start: $LAST_TAG_FOR_LOG"
  95. echo "-------------------------------"
  96. build:
  97. needs: prepare
  98. strategy:
  99. matrix:
  100. product: [ app ]
  101. type: [ release ]
  102. fail-fast: false
  103. runs-on: ubuntu-latest
  104. env:
  105. product: ${{ matrix.product }}
  106. type: ${{ matrix.type }}
  107. # 使用 prepare job 的输出
  108. VERSION: ${{ needs.prepare.outputs.version }}
  109. VERSIONL: ${{ needs.prepare.outputs.versionL }}
  110. BUILD_NUMBER: ${{ needs.prepare.outputs.version_code }}
  111. steps:
  112. - uses: actions/checkout@v5
  113. with:
  114. fetch-depth: 0
  115. - name: Set up JDK 21
  116. uses: actions/setup-java@v5
  117. with:
  118. distribution: 'temurin'
  119. java-version: 21
  120. - name: Clear 18PlusList.txt
  121. run: |
  122. echo "清空18PlusList.txt"
  123. echo "">$GITHUB_WORKSPACE/app/src/main/assets/18PlusList.txt
  124. - name: Release Apk Sign
  125. run: |
  126. echo "${{ secrets.SIGNING_KEY }}" | base64 -d > $GITHUB_WORKSPACE/app/my-release-key.jks
  127. echo "RELEASE_STORE_FILE=my-release-key.jks" >> $GITHUB_WORKSPACE/gradle.properties
  128. echo "RELEASE_STORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_WORKSPACE/gradle.properties
  129. echo "RELEASE_KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> $GITHUB_WORKSPACE/gradle.properties
  130. echo "RELEASE_KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> $GITHUB_WORKSPACE/gradle.properties
  131. - name: Set up Gradle
  132. uses: gradle/actions/setup-gradle@v4
  133. - name: Build With Gradle
  134. run: |
  135. echo "开始${{ env.product }}${{ env.type }}构建"
  136. chmod +x gradlew
  137. ./gradlew assemble${{ env.product }}Release
  138. env:
  139. BUILD_NUMBER: ${{ env.BUILD_NUMBER }}
  140. APP_VERSION_NAME: ${{ env.VERSIONL }}
  141. - name: Move Missing Rules Files
  142. run: |
  143. echo "移动missing_rules.txt文件"
  144. MAPPING_DIR="${{ github.workspace }}/mapping"
  145. mkdir -p "$MAPPING_DIR"
  146. # 使用 find 代替 ls,避免参数列表过长
  147. find ${{ github.workspace }}/app/build/outputs/mapping/ -name "missing_rules.txt" -exec mv {} "$MAPPING_DIR/missing_rules.txt" \;
  148. - name: Upload Missing Rules File To Artifact
  149. uses: actions/upload-artifact@v4
  150. with:
  151. name: legado.${{ env.product }}.${{ env.type }}.mapping.missing_rules
  152. if-no-files-found: ignore
  153. path: ${{ github.workspace }}/mapping/missing_rules.txt
  154. - name: Check Build production
  155. run: |
  156. echo "检查 APK 输出目录"
  157. APK_PATH="$GITHUB_WORKSPACE/app/build/outputs/apk"
  158. # 检查是否有任何 APK 文件生成
  159. if [ -z "$(find "$APK_PATH" -name "*.apk" -print -quit)" ]; then
  160. echo "Build production not found! Check gradle logs."
  161. exit 1
  162. fi
  163. - name: Upload universal APK
  164. uses: actions/upload-artifact@v4
  165. with:
  166. name: universal-apk
  167. path: app/build/outputs/apk/app/release/legado_app_*[0-9].apk
  168. - name: Upload arm64-v8a APK
  169. uses: actions/upload-artifact@v4
  170. with:
  171. name: arm64-v8a-apk
  172. path: app/build/outputs/apk/app/release/*arm64-v8a.apk
  173. - name: Upload armeabi-v7a APK
  174. uses: actions/upload-artifact@v4
  175. with:
  176. name: armeabi-v7a-apk
  177. path: app/build/outputs/apk/app/release/*armeabi-v7a.apk
  178. - name: Upload Mapping File To Artifact
  179. uses: actions/upload-artifact@v4
  180. with:
  181. name: legado.${{ env.product }}.${{ env.type }}.mapping
  182. if-no-files-found: ignore
  183. path: ${{ github.workspace }}/mapping/mapping.txt
  184. test_Branch:
  185. needs: [ prepare, build ]
  186. runs-on: ubuntu-latest
  187. if: ${{ github.event_name != 'pull_request' && github.actor == 'gedoor' }}
  188. steps:
  189. - uses: actions/checkout@v5
  190. - uses: actions/download-artifact@v5
  191. with:
  192. path: apk/
  193. - working-directory: apk/
  194. run: |
  195. # 使用 find 安全地移动文件
  196. find . -type f -name "*.apk" -exec mv {} . \;
  197. # 清理子目录
  198. find . -type d -mindepth 1 -exec rm -rf {} +
  199. - name: Push To "test" Branch
  200. run: |
  201. cd $GITHUB_WORKSPACE/apk/
  202. git init
  203. git checkout -b test
  204. git config --global user.name "github-actions[bot]"
  205. git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
  206. git remote add origin "https://${{ github.actor }}:${{ secrets.ACTIONS_TOKEN }}@github.com/${{ github.actor }}/release"
  207. git add *.apk
  208. git commit -m "${{ needs.prepare.outputs.versionL }}"
  209. git push -f -u origin test
  210. create_release:
  211. needs: [ prepare, build ]
  212. # 触发条件:只有在 main 分支推送,且构建号大于 0(针对 beta 版) 或 明确标记为正式版时才执行
  213. if: |
  214. github.event_name == 'push' &&
  215. github.ref == 'refs/heads/main' &&
  216. (needs.prepare.outputs.build_number > 0 || needs.prepare.outputs.is_release == 'true') &&
  217. success()
  218. runs-on: ubuntu-latest
  219. steps:
  220. - uses: actions/checkout@v4
  221. with:
  222. fetch-depth: 0
  223. #
  224. # 获取 Commit 记录并格式化为 Release Body
  225. #
  226. - name: 获取 Commit 记录
  227. id: get_commits
  228. run: |
  229. # prepare Job 中计算出的日志起点 Tag (LAST_TAG_FOR_LOG)
  230. LAST_TAG="${{ needs.prepare.outputs.latest_tag }}"
  231. # 获取从上一个 Tag 到当前 HEAD 的所有 Commit 消息
  232. # --no-merges: 排除合并提交
  233. COMMIT_LOG=$(git log "$LAST_TAG"..HEAD --pretty=format:"* %s" --no-merges)
  234. if [ -z "$COMMIT_LOG" ]; then
  235. RELEASE_BODY=$(cat <<EOF
  236. ### 更新内容
  237. - 没有新的 Commit 记录.
  238. EOF
  239. )
  240. else
  241. RELEASE_BODY=$(cat <<EOF
  242. ### 更新内容
  243. $COMMIT_LOG
  244. EOF
  245. )
  246. fi
  247. # 设置 step 输出
  248. echo "release_body<<EOF" >> $GITHUB_OUTPUT
  249. echo "$RELEASE_BODY" >> $GITHUB_OUTPUT
  250. echo "EOF" >> $GITHUB_OUTPUT
  251. echo "生成的 Release Body:"
  252. echo "$RELEASE_BODY"
  253. - name: 创建 Tag
  254. run: |
  255. # 检查 Tag 是否已存在,避免重复创建
  256. if ! git tag -l ${{ needs.prepare.outputs.versionL }} | grep -q ${{ needs.prepare.outputs.versionL }}; then
  257. git tag ${{ needs.prepare.outputs.versionL }}
  258. git push origin ${{ needs.prepare.outputs.versionL }}
  259. else
  260. echo "Tag ${{ needs.prepare.outputs.versionL }} 已存在,跳过创建。"
  261. fi
  262. env:
  263. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  264. - name: 创建 GitHub Release
  265. id: create_release
  266. uses: softprops/action-gh-release@v2
  267. with:
  268. tag_name: ${{ needs.prepare.outputs.versionL }}
  269. name: ${{ needs.prepare.outputs.is_release == 'true' && format('正式版 {0}', needs.prepare.outputs.versionL) || format('测试版 {0}', needs.prepare.outputs.versionL) }}
  270. body: ${{ steps.get_commits.outputs.release_body }}
  271. prerelease: ${{ needs.prepare.outputs.is_release != 'true' }}
  272. files: |
  273. artifacts/legado-*.apk
  274. env:
  275. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  276. - name: 下载所有 APK Artifacts
  277. uses: actions/download-artifact@v4
  278. with:
  279. pattern: '*-apk'
  280. path: artifacts
  281. merge-multiple: true
  282. - name: 整理并重命名 APK 文件
  283. id: organize_apks
  284. run: |
  285. VERSIONL="${{ needs.prepare.outputs.versionL }}"
  286. echo "正在查找并重命名 APK 文件..."
  287. # 查找 Universal APK (文件名不含 ABI)
  288. find artifacts -maxdepth 1 -type f -name '*app_*.apk' \
  289. ! -name '*arm64-v8a.apk' \
  290. ! -name '*armeabi-v7a.apk' \
  291. -exec mv {} "artifacts/legado-${VERSIONL}.apk" \;
  292. # 查找 arm64-v8a APK
  293. find artifacts -maxdepth 1 -type f -name '*arm64-v8a.apk' \
  294. -exec mv {} "artifacts/legado-${VERSIONL}-arm64-v8a.apk" \;
  295. # 查找 armeabi-v7a APK
  296. find artifacts -maxdepth 1 -type f -name '*armeabi-v7a.apk' \
  297. -exec mv {} "artifacts/legado-${VERSIONL}-armeabi-v7a.apk" \;
  298. # 检查文件是否成功找到并重命名,如果找不到则退出
  299. if [ ! -f "artifacts/legado-${VERSIONL}.apk" ]; then
  300. echo "错误:未找到 Universal APK。请检查 build 作业的命名规则。"
  301. # 允许继续,但发出警告,因为其他 ABI 仍可能存在
  302. # exit 1 # 移除硬性退出,以防万一
  303. fi
  304. echo "文件重命名完成,准备上传:"
  305. ls -l artifacts/
  306. - name: 上传 Universal APK
  307. uses: actions/upload-release-asset@v1
  308. env:
  309. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  310. with:
  311. upload_url: ${{ steps.create_release.outputs.upload_url }}
  312. asset_path: artifacts/legado-${{ needs.prepare.outputs.versionL }}.apk
  313. asset_name: legado-${{ needs.prepare.outputs.versionL }}.apk
  314. asset_content_type: application/vnd.android.package-archive # APK MIME 类型
  315. - name: 上传 arm64-v8a APK
  316. uses: actions/upload-release-asset@v1
  317. env:
  318. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  319. with:
  320. upload_url: ${{ steps.create_release.outputs.upload_url }}
  321. asset_path: artifacts/legado-${{ needs.prepare.outputs.versionL }}-arm64-v8a.apk
  322. asset_name: legado-${{ needs.prepare.outputs.versionL }}-arm64-v8a.apk
  323. asset_content_type: application/vnd.android.package-archive
  324. - name: 上传 armeabi-v7a APK
  325. uses: actions/upload-release-asset@v1
  326. env:
  327. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  328. with:
  329. upload_url: ${{ steps.create_release.outputs.upload_url }}
  330. asset_path: artifacts/legado-${{ needs.prepare.outputs.versionL }}-armeabi-v7a.apk
  331. asset_name: legado-${{ needs.prepare.outputs.versionL }}-armeabi-v7a.apk
  332. asset_content_type: application/vnd.android.package-archive