前言
谈起 CI/CD,很多同学的表情可能是这样的?

怎么说,身为 Android 开发,很多同学对 CI/CD 的了解少之又少,其实借助 CI/CD,可以做不少有趣的事。
首先,我们得了解什么是 CI/CD?

CI/CD 包括 CI 和 CD。
CI(Continuous Interaction)是持续集成的意思,它是属于开发人员的自动化流程。在当今的 Android 开发模式中,多人同时维护一个项目已在所难免,所以,每次提交代码后,我们可以让机器去做些什么去维护我们项目的安全性和稳定性?其实上面的图中已经给出答案,当我们提交一份代码后,自动化工具就会触发构建、测试,这些都完成以后,再帮我们合并代码。
CD 通常有两层意思,一层是(Continuous Delivery),它代表着持续交付,当开发人员提交代码后,自动化工具会会自动进行错误测试并上传到存储库,最后由运维团队将其部署在实时的生产环境中。另一层是(Continuous Deployment),它代表着持续部署,是将存储库发布到生产环境。
听完上面的解释,各位 Android 开发仔能弄懂 CI/CD 吗?
显然不能啊,这有关系吗,没关系,反正是从入门到放弃, :)
CI 还比较好理解,用实际的 Android 经验来解释就是,持续交付就是将更新后的 Master 分支代码打包成各个应用市场需要的渠道包,持续部署就是将上一步打好的包,自动上传到对应的应用市场。
CI/CD对Android开发的意义
首先,CI 对于开发人员来说,可以及早的发现错误,每当我们提交一笔代码,自动化工具就会发起构建、自动化测试,最终会合入代码,保证我们项目的稳定性。别代码提上去了,编都编不过,还吭呲吭呲往下写!
其次,对于运维人员来说,CD 可以提升他们的工作效率,自动化工具可以减少他们不必要的工作量。
这个意义有点笼统,放到Android上来讲:
- 标准化管理Android项目
- 自动触发多渠道打包
- 自动上传到应用市场应该也是可以的,我看到一些平台可以把我们的app一键上传到各个应用市场。
- 我最近研究的组件化下自动上传AAR
- 以及组内大佬医生分享的精准化测试系列
- ...
CI/CD 工具帮了我们不少忙。

看这里,Bugly 也建议我们将符号表的上传放入持续集成中。
最常见的持续集成工具应该是 Jenkins,因为它开源并且免费,你想要的插件在开源社区几乎都可以找到,起点之前也是用的 Jenkins,也就最近,才开始从 Jenkins 迁移到腾讯内部的蓝盾。
不过,本次的主角并不是 Jenkins。
Github Action 简介
没错,本次的主角就是 Github Action,官方是这么介绍的:
在 GitHub Actions 的仓库中自动化、自定义和执行软件开发工作流程。 您可以发现、创建和共享操作以执行您喜欢的任何作业(包括 CI/CD),并将操作合并到完全自定义的工作流程中。
所以,Github Actions 是 Github 的持续集成服务,那么它和其他的持续集成工具有什么不同呢?
所有的一系列操作例如设置 JDK、运行 Shell 命令或者是执行自动化测试都被称之为 Action,像设置JDK Action 肯定通用的,所以就有了免费的 Github Action 市场,允许开发者将他们封装的 Action 上传到市场,其他开发者需要的时候引用即可。

可以看到,当前市场的 Action 已经快1W个了,能够满足开发的日常需求。
除此以外,Github Action 也为通用的一些项目提供了模板,当我们点击自己项目的 Action 的时候,它会给你推荐一些模板:

也允许你自己选择一些热门的模板。
语法简介
看到这儿,很多同学都已经开始跃跃欲试了,别急,我们再介绍一下语法!

理解了这些名词,我们再了解一下它们之间的关系:

一个 WorkFlow 由 Event 和 Jobs 组成,每个 Job 由 Runner 和 一系列 Step 组成,每个 Step 都由 Action 或者 Shell Command 组成。
有了一定的了解以后,我们看看语法。
Github Action 的配置文件叫做 WorkFlow 文件,存放在代码仓库的 .github/workflows 目录,该文件采用的 yaml 语法。
我们使用一个默认的 Android 模板,看看它是什么样子的:
name: Android CI # 设置 WorkFlow 名称
on: # 设置 Event,
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build: # 设置 Job 的名称
runs-on: ubuntu-latest # 设置服务器
steps:
- uses: actions/checkout@v2 # Action 获取当前分支的源码
- name: set up JDK 11 # Action 设置 JDK
uses: actions/setup-java@v2
with: # 参数由 Action 的创建者指定,需要查看创建者的文档
java-version: '11'
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew # Shell Command
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
从上往下解释:
名词 | 解释 |
---|---|
name |
设置 WorkFlow 的名字 |
on |
设置 Event,比如上面的 on.push.branches: [ master ],代表着触发的时机是 master 分支发生 push 的时候 |
jobs |
声明 Jobs,上述中 jobs 下的 build 字段,则是创建一个 Job,也是该 Job 的名称 |
runs-on |
声明 Runner,也就是服务器,支持 Windows、MacOS 和 Ubuntu,不过需要指定版本 |
steps |
声明 Steps |
steps.- |
- 代表着当前是一个 Step |
steps.name |
当前 Step 的名称 |
steps.uses |
使用市场中的 Action |
steps.run |
执行 Shell 命令 |
有了名词解释,我们就能明白上面的 Android 模板的大致过程。
Android 实践
我们有了一定的基础,借助这些,就可以做些有趣的东西的了!
试想一下,当我们往 master 分支合入代码的时候,是不是就要发版了,这个时候,持续集成可以帮我们构建项目,之后根据需要,使用生成好的Apk进行加固、多渠道打包、上传备份文件,最后发出成功或者失败的消息。
那同样的,我们可以在上面的示例中做一些修改,加入上传APK、将结果发送给邮件和钉钉的群。
以我的 AndroidGithubAction 为例,完整的教程是这样的:
1. 创建一个Action
选中 Github 项目中的 Action 栏,我们将会见到下面的内容:

根据需要选择自定义方式或者模板方式,这里我们选择 Android 模板。
2. 修改模版
模板的内容我在上面已经发过了,修改一下:
- 最外层的
name
可改可不改。 - Event 修改为 push 的时候触发即可。
- 修改一下 Gradle 命令。
修改完的内容:
name: Android CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2 # 拉取源代码
- name: set up JDK 1.8 # 设置JDK
uses: actions/setup-java@v2
with:
java-version: 8.0.232+9.1
distribution: 'adopt'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle # 执行编译命令
run: ./gradlew clean && ./gradlew app:assembleDebug
3. 上传APK
先说明一下,编写 WorkFlow 时,左边是编写 WorkFlow 的面板,右边就可以直接在市场上搜索你想使用的Action。

选中想使用的 Action,就会跳出详细的说明文档。

下面这个就是我们想使用的 Action。

有了它,我们可以上传编译完的 apk,可以在多个 job 之间共享,或者等持续集成运行结束的时候,可以下载生成的产物。

引入代码:
- name: Upload Apk
uses: actions/upload-artifact@v1
with:
name: qd-info
path: app/build/outputs/apk/debug/app-debug.apk
4. 上传APK到第三方平台
很多情况下需要将产物上传到第三方平台,以蒲公英为例:
- name: Upload File to Pgyer
run: curl -F 'file=@app/build/outputs/apk/debug/app-debug.apk' -F "_api_key=${{ secrets.API_KEY }}" -F "uKey=${{ secrets.PGYER_USER_KEY }}" -F "buildInstallType=3" https://www.pgyer.com/apiv2/app/upload
这里面涉及了两个知识点,一是 curl 工具的使用,二是 Github Action 中隐私信息的用法。
curl 是用来请求Web服务器的命令行工具,curl 使用可参考阮一峰的文章:
对于一些关键的 key,我们可能不太想将 key 暴露在 Github Action 的 workflow 文件中,Github 推荐我们将这些 key 存在 Secrets 中,添加入口:

5. 获取构建人
上传完成后,我们会将编译信息、版本信息、Commit日志发送到企业微信、钉钉或者邮箱。
首先是获取构建人信息,默认的环境变量提供如下:

但是我发现在 curl 命令中有点问题,报了识别不了的错误:

于是就把触发人写在了全局变量中:
# 获取发送者
- name: Sender
run: echo "sender=$GITHUB_ACTOR" >> $GITHUB_ENV
之后通过${{ env.sender }}
获取。
6. 获取版本信息
版本信息我是通过 grep
命令在 app 目录下的 build.gradle 文件获取

如果你有更好的方法,欢迎评论区交流~
同样是写到全局变量中:
# step: 获取apk版本号
- name: APK Version
id: apk_version
run: |
versionCode=`grep "versionCode" app/build.gradle | awk -F " " '{print $2}'`
versionName=`grep "versionName" app/build.gradle | awk -F ''\"'' '{print $2}'`
echo "versionResult=$versionName.$versionCode" >> $GITHUB_ENV
在 Github Action 中,如果需要运行多条命令,需要在 run:
后面加上一个 |
。
7. 获取提交日志
获取提交日志就比较简单了,通过 git log
就能获取:
# 获取git log
- name: Get git log
id: git_log
run: |
updateLog=`git log --pretty=format:"%s" -1`
echo "updateLog=$updateLog" >> $GITHUB_ENV
该命令是获取最近的提交记录的日志,以指定格式输出。
8. 发送到钉钉群中的小机器人
钉钉小机器人的使用说明就不介绍了,直接看官网,命令如下:
# 向钉钉发送消息
- name: dingtalk
run: |
curl '${{ secrets.DINGDING_WEBHOOK }}' -H 'Content-Type: application/json' -d '{"msgtype": "text", "text": {"content":"吃饭! \n触发人:${{ env.sender }} \n版本:${{ env.versionResult }} \n提交内容:${{ env.updateLog }} \n下载地址:${{ secrets.PGY_URL }}"}}'
构建成功后发送到群里的截图:

9. 发送邮件
通常情况下,触发人编译失败后是会收到失败邮件的,如果不够,我们可以再写一个发送邮件的 Action
。
我们这里引用其他人开源的 Action:
# 发送邮件
- name: Send mail
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.qq.com
server_port: 465
username: ${{ secrets.MAILUSERNAME }}
password: ${{ secrets.MAILPASSWORD }}
subject: Github Actions job result
to: 2556536414@qq.com
from: TeaOf
secure: true
body: "吃饭! \n触发人:${{ env.sender }} \n版本:${{ env.versionResult }} \n提交内容:${{ env.updateLog }} \n下载地址:${{ secrets.PGY_URL }}"
这一步做完,差不多就结束了。
完整代码地址:https://github.com/mCyp/AndroidGithubAction/blob/main/.github/workflows/android.yml
总结
对于开源用户来说,Github Action 还有一些比较常见的使用场景。
比如利用 Github Action 自动部署 Hexo 博客,还记得以前使用 Github Page 建立个人博客的时候,每次新增一篇文章以后,要输入一串命令行,有了 Github Action,这些都可以自动执行。
还有,很多人的 Github Porfile 是依赖 Github Action 自动更新的,像这位外国老哥:

他的 Profile 每三个小时会自动获取一下最新的个人文章和社交媒体,展示在自己的个人简介上,这个效果确实很赞!最近有时间,打算也来搞一套。
网友评论