[Learning] Github Actions Android持续集成(自动化打包发布)示例

1,873 阅读5分钟

前言

时针回拨到2018年,微软宣布75亿美金收购Github,随后便发布了Github Actions 持续集成服务。我们可以使用它进行项目构建、测试、打包、部署、发布等一系列操作,能够大大简化工作的流程。他还提供了整套的服务器环境,包含Ubuntu、Windows、macOS等。最近一段时间一直在试用感觉十分强大,Github Actions 的文档有中文版本查询起来也比较方便。这是个简单的Android Github Actions使用示例,展示使用Github Actions自动发布一个Android应用到蒲公英,提交版本更新记录到服务器上并发送本次的应用更新结果到钉钉群中。

配置文件结构

要想写一个构建脚本, 首先我们要看看一个基本的脚本是什么样子的.

Github Action基本脚本

从中我们可以看到几个词:

  • Workflow ( 工作流 ): 表示一次持续集成的过程, 为.github/workflows下的一个脚本执行的过程

  • on: 触发构建所需要的条件。<push|pull>.<branchs|tags> 都是触发构建的条件, 详情请看: 触发工作流程的事件

  • job ( 任务 ): 表示脚本中的任务(如上图中 build) ,一个 workflow 可以有多个jobs 构成。意味一个工作流中可以并发执行多个任务。所有的任务结束就相当于一次持续集成完成

  • step ( 步骤 ): 表示一个任务可以有很多的步骤,任务(job)依次每一个步骤(step)。

  • action ( 动作 ): 表示每个步骤所需要完成的行为, 比如一次或多次输出等

    一个动作中输出了多条文本信息

阮一峰老师有篇文章专门介绍 Github Actions的相关概念。 GitHub Actions 入门教程

用一个新东西,首先我们先看看效果!

看来有好戏看了

效果展示

[示例仓库]: github.com/lumotime/Le…

  • 钉钉通知

    First测试应用版本更新通知

  • 制品库

    制品库效果

Android Github 持续集成示例

下面是示例 yml 脚本: package_task.yml

# 工作流程的名称, 显示在 ${github repo url}/actions/Workflows的列表
name: package_task

# 触发workflow的条件, 当前配置为: 当推送如 'v1.0.0' 格式的 tag 时
# 构建条件配置文档: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows
on:
  push:
    tags:
      - v[1-9]+.[0-9]+.[0-9]+

# 构建过程所执行的任务
jobs:
  # 因为打包可能需要打多个应用的包, 也就是同一个项目下有多个 application时需要先把代码拉下来
  build_project:
    # 该 job 运行的系统环境, Github托管的运行器支持 ubuntu/windows/macOS
    # 详细支持的版本请查看 https://docs.github.com/cn/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions
    runs-on: ubuntu-latest

    # 以下该 clone_project 任务的多个步骤
    steps:
      # 打印工作流程中的信息,定义: https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables
      - name: Run a one-line script
        run: |
          echo ID ${GITHUB_RUN_ID}, REF ${GITHUB_REF}
          echo SHA ${GITHUB_SHA}, ACTOR ${GITHUB_REPOSITORY}, GITHUB_REPOSITORY ${GITHUB_REPOSITORY}

      # 克隆当前项目, 默认为 ${GITHUB_REPOSITORY}
      - name: clone project code
        uses: actions/checkout@v2
      
      # 克隆私有的密钥库, 用于打包时签名. 该仓库为私有库
      - name: clone project keystore
        uses: actions/checkout@v2
        with:
          repository: lumotime/lumotimeKeystore
          path: keystore
          token: ${{secrets.GIT_TOKEN}}
      
      # 设置 JDK 环境为 JDK1.8
      - name: set up JDK 1.8
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
      
      # 使用 gradlew assembleRelease 打 release 包
      - name: Build with Gradle generate modules apks
        run: chmod +x gradlew &&./gradlew clean assembleRelease --stacktrace

      # 上传 first apk 到 github actions 的制品库
      - name: upload first app to github Actions
        uses: actions/upload-artifact@v2
        with:
          name: example-first_app-release
          path: example/build/outputs/apk/first_app/release/example-first_app-release.apk

      # 上传 last apk 到 github actions的制品库
      - name: upload last app to github Actions
        uses: actions/upload-artifact@v2
        with:
          name: example-last_app-release
          path: example/build/outputs/apk/last_app/release/example-last_app-release.apk
    
  upload_first_app:
    runs-on: ubuntu-latest
    needs: build_project
    steps:

      # 从制品库中拉取得 first apk 并进行后续操作
      - name: download first apk
        uses: actions/download-artifact@v2
        with:
          name: example-first_app-release
			
			# 获取触发构建的 tag 信息
      - name: fetch github ref
        id: REF
        run: echo "::set-output name=ref::${GITHUB_REF}"

      # 自定义的 action用于获取apk中的信息, 使用请看 https://github.com/lumotime/get-apk-info-action.
      - name: get first app info
        id: app_info
        uses: lumotime/get-apk-info-action@v1.0.0
        with:
          apkPath: example-first_app-release.apk

      # 上传 apk 到 蒲公英. secrets 配置请看下面 私密信息配置
      # 蒲公英接口参数请看: https://www.pgyer.com/doc/view/api#paramInfo 上传 App接口部分
      # action 使用示例请看  https://github.com/lumotime/upload-pgyer-apk-file
      - name: upload first app to Pgyer
        id: upload_pgyer
        uses: lumotime/upload-pgyer-apk-file@v1.0.0
        with: 
          forms: '{"_api_key":"${{secrets.pgyer_key}}","buildInstallType":2, "buildPassword": "${{secrets.pgyer_install_password}}"}'
          fileForms: '{"file":"example-first_app-release.apk"}'

      - name: dingtalk notification
        uses: lumotime/webrequest-action@v1.2.4
        with: 
          url: ${{secrets.dingtalk_webhook}}
          method: POST
          payload: '{"msgtype":"markdown","markdown":{"title":"应用版本更新","text":"#### **FIRST测试应用版本更新** \n\n**应用名称**: ${{ steps.app_info.outputs.name }} \n\n **包名**:${{ steps.app_info.outputs.applicationId }} \n\n**版本编号**:${{ steps.app_info.outputs.versionCode }} \n\n**版本名称**:${{ steps.app_info.outputs.versionName }} \n\n**最小版本要求**:${{ steps.app_info.outputs.minSdkVersion }} \n\n**编译目标版本**:${{ steps.app_info.outputs.targetSdkVersion }} \n\n**安装包名称**:${{ steps.upload_pgyer.outputs.filename }} \n\n**安装包大小**:  ${{ steps.upload_pgyer.outputs.fileSize }}\n\n**更新时间**: ${{ steps.upload_pgyer.outputs.updatedTime }}\n\n**触发**: ${{steps.REF.outputs.ref}} \n\n**邀请函**: \n\n ![邀请函](${{ steps.upload_pgyer.outputs.invitationQRCode }})"}}'
          headers: '{"Content-Type": "application/json"}'
  
  upload_last_app:
    runs-on: ubuntu-latest
    needs: build_project
    steps:

      # 从制品库中拉取得 last apk 并进行后续操作
      - name: download last apk
        uses: actions/download-artifact@v2
        with:
          name: example-last_app-release

      - name: fetch github ref
        id: REF
        run: echo "::set-output name=ref::${GITHUB_REF}"

      # 自定义的 action用于获取apk中的信息, 使用请看 https://github.com/lumotime/get-apk-info-action.
      - name: get last app info
        id: app_info
        uses: lumotime/get-apk-info-action@v1.0.0
        with:
          apkPath: example-last_app-release.apk

      # 上传 apk 到 蒲公英. secrets 配置请看下面 私密信息配置
      # 蒲公英接口参数请看: https://www.pgyer.com/doc/view/api#paramInfo 上传 App接口部分
      # action 使用示例请看  https://github.com/lumotime/upload-pgyer-apk-file
      - name: upload last app to Pgyer
        id: upload_pgyer
        uses: lumotime/upload-pgyer-apk-file@v1.0.0
        with: 
          forms: '{"_api_key":"${{secrets.pgyer_key}}","buildInstallType":2, "buildPassword": "${{secrets.pgyer_install_password}}"}'
          fileForms: '{"file":"example-last_app-release.apk"}'

      - name: dingtalk notification
        uses: lumotime/webrequest-action@v1.2.4
        with: 
          url: ${{secrets.dingtalk_webhook}}
          method: POST
          payload: '{"msgtype":"markdown","markdown":{"title":"应用版本更新","text":"#### **LAST测试应用版本更新** \n\n**应用名称**: ${{ steps.app_info.outputs.name }} \n\n **包名**:${{ steps.app_info.outputs.applicationId }} \n\n**版本编号**:${{ steps.app_info.outputs.versionCode }} \n\n**版本名称**:${{ steps.app_info.outputs.versionName }} \n\n**最小版本要求**:${{ steps.app_info.outputs.minSdkVersion }} \n\n**编译目标版本**:${{ steps.app_info.outputs.targetSdkVersion }} \n\n**安装包名称**:${{ steps.upload_pgyer.outputs.filename }} \n\n**安装包大小**:  ${{ steps.upload_pgyer.outputs.fileSize }}\n\n**更新时间**: ${{ steps.upload_pgyer.outputs.updatedTime }}\n\n**触发**: ${{steps.REF.outputs.ref}} \n\n**邀请函**: \n\n ![邀请函](${{ steps.upload_pgyer.outputs.invitationQRCode }})"}}'
          headers: '{"Content-Type": "application/json"}'

secrets.GIT_TOKEN: 访问github私有仓库需要使用的个人访问密钥

secrets.dingtalk_webhook:钉钉自定义机器人的 webhook地址

secrets.pgyer_key: 蒲公英的 api 密钥

secrets.pgyer_install_password:蒲公英邀请的安装密码

其中有几点需要注意的地方

  • 配置Secrets (保证一些私密信息不会在公开库中显示)

    官方文档: docs.github.com/cn/free-pro…

    配置Secrets

  • 触发工作流程的事件

    # 触发workflow的条件, 当前配置为: 当推送如 'v1.0.0' 格式的 tag 时
    # 构建条件配置文档: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows
    on:
      push:
        tags:
          - v[1-9]+.[0-9]+.[0-9]+
    
  • 打包所使用的签名使用私有仓库存储

    1. 使用 checkout action 克隆仓库到文件目录中

      # 克隆私有的密钥库, 用于打包时签名. 该仓库为私有库
      - name: clone project keystore
        uses: actions/checkout@v2
        with:
        	# 仓库的名称
          repository: lumotime/lumotimeKeystore
          # 仓库克隆到的位置
          path: keystore
          # 克隆私有仓库所使用的 token 
          token: ${{secrets.GIT_TOKEN}}
      

      lumotimeKeystore 目录结构:

      私有密钥库目录结构

      lumotime.properties 配置文件:

      image-20201029131938313

    2. 修改 app build.gradle通过配置文件加载密钥

      [配置示例]: github.com/lumotime/Le…

      // 加载仓库的密钥配置
      def keyProps = new Properties()
      def keyPropsFile = rootProject.file('keystore/lumotime.properties')
      if (keyPropsFile.exists()) {
          keyProps.load(new FileInputStream(keyPropsFile))
      }
      
      android {
      	   // 配置打包使用的密钥
          signingConfigs {
              lumotime {
                  keyAlias keyProps['keyAlias']
                  keyPassword keyProps['keyPassword']
                  storeFile keyProps['storeFile'] ? file(keyProps['storeFile']) : null
                  storePassword keyProps['storePassword']
              }
          }
      
          buildTypes {
              release {
                  signingConfig signingConfigs.lumotime
                  minifyEnabled false
                  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
              }
      
              debug {
                  signingConfig signingConfigs.lumotime
                  minifyEnabled false
                  proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
              }
          }
      }
      
  • 多个 job 间传递数据

    官方文档: [在工作流程中作业之间传递数据]

    jobs:
    	build_project:
    		...
    	first_job:
    		needs: build_project
    		...
    	last_job:
    		needs: build_project
    		...
    

    注意: 多个任务的运行器是隔离的, 需要在通过制品库进行数据的交互。例如 build_project.upload first app to github Actions 和 upload_first_app.download first apk.

  • 自定义 Github Action

    支持两种 Action的自定义方式

    [创建 JavaScript 操作]: docs.github.com/cn/free-pro…

    [创建 Docker 容器操作]: docs.github.com/cn/free-pro…

    一些自定义的Action:

    [webrequest-action]: 发送网络请求

    [get-apk-info-action]: 获取 apk 的信息

    [upload-pgyer-apk-file]: 上传 apk 到蒲公英

  • 钉钉机器人配置

    文档: ding-doc.dingtalk.com/document#/o…

    钉钉机器人配置

参考和文档

[GIthub Action 官方文档]

[钉钉机器人接口文档]

[Github Actions 使用指南和Android 持续集成示例]

[github应用程序_使用GitHub Actions对Android应用程序进行签名]

博客地址: blog.j3dream.top/archives/le…