前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >写给Android工程师的 Github CI 快速指北

写给Android工程师的 Github CI 快速指北

作者头像
Petterp
发布2023-05-27 14:11:51
5910
发布2023-05-27 14:11:51
举报
文章被收录于专栏:JetPackJetPack

背景

关于 CI/CD ,在2023年的今天,基本所有技术团队或多或少都会使用,其很大程度上减轻了我们的冗余重复工作,从而简化我们的工作流程。

不过对于大多数客户端工程师而言,其实 CI 这个词还是比较陌生。当然并不是说,CI/CD 有什么高大上或者门槛很高,因为毕竟不是所有人会去维护开源库或者搞基建,或者说少有场景去接触到。但对于一个 工程师 而言,这严格意义上其实属于 基本技能点 ,或者说在现在这个时代,这种小技能应该没有太多边界之分。

相应的,如果使用 Github Action,这个难度就更低了,其相比传统的 Jenkins ,容易上手了更多,简化了 环境配置 等等。并且,在这个过程中,我们也将逐步接触到一些 cmdpython 等其他工具使用方式或者语法,从而探索出更多可玩性。

写在开始

注意: 本篇不会讲 Github Action 基础语法,这些官网有更详细的文档,没有意义去做二次搬运🙅🏻‍♂️。

本篇更多是围绕要解决的实际问题进行分析,并在中间对关键语法进行解释,从而便于更快的使用 Github CI

个人建议对于这种工具类的技能,不需要学的很详细,只需要了解基本原则:自己要解决什么问题,即可。

故此,学完本篇,你将学会 Github CI 的基础使用,以及一些常见的实用操作,如:

  • 自动化打包以及上传;
  • 自动化版本号与code;
  • 自动化发布release;
  • 逐步解开传统思维陷阱,体会 CI 在日常开发中的妙用;

好了,让我们开始吧 ! 🏃🏻

什么是 CI/CD?

CI/CD 是指持续集成(Continuous Integration)和持续部署/交付(Continuous Deployment/Delivery)的缩写。

  • 持续集成(CI)是一种软件开发实践,指的是将代码集成到主干分支中并进行构建和测试的过程,以便尽早发现和解决问题。CI 工具可以自动执行这个过程,例如 1、Travis CI、CircleCI 等。每次提交代码时,CI 工具会自动构建和运行测试,并给出构建和测试结果的反馈。
  • 持续部署/交付(CD)是指自动化地将代码部署到生产环境或发布到应用商店的过程。持续部署/交付可以让开发团队更加快速和可靠地将新功能交付给用户。CD 工具可以自动化执行部署和发布过程,例如 AnsibleKubernetesDocker 等。在持续部署/交付的过程中,需要进行自动化测试、版本控制、持续监控等操作,以确保代码质量和应用稳定性。

CI/CD 的优点包括加速软件开发、提高代码质量、降低风险、提高工作效率 等。从而可以让开发团队更加专注于代码编写,而不必花费大量时间进行 手动构建测试部署 等重复性工作。

什么是 Github CI?

因为本篇,主要是讲 Github CI 的使用,故还是要简单说一下 Github CI 的简介及功能。

GitHub CI(GitHub Actions)是 GitHub 提供的一项自动化工具,用于 构建测试部署 GitHub 上托管的代码仓库。GitHub CI 提供了一种定义自动化工作流程的方式,可以根据代码仓库的变化自动触发工作流程。一组工作流程可以包括多个步骤,例如编译代码、运行测试、构建镜像、部署应用等。其优点包括与 GitHub 平台紧密集成、易于配置、支持多种语言和环境、提供丰富的集成能力等。它可以帮助开发团队自动化构建和测试过程,提高代码质量和开发效率。

具体运行示例中如下图所示:

image-20230515221921780
image-20230515221921780

CI 可以做什么?

几乎可以简化任何我们能在本地做的所有 人工 操作,甚至自动编码。

为了更好的便于理解,我们切换到 Android工程师 视角,使用一个示例来说明。

比如我们现在有个 下厨房 Android工程,如果在没有 CI 时,我们最基础的流程通常如下:

  • 开发: 本地开发、调试、push;
  • 测试:本地打包、发给测试同学;
  • 打包: 改版本号、打tag、本地打包、发给运营同学;

上面的流程看上去似乎没有什么问题,对于本地开发而言,浓缩下来也就上面三步,这也是小团队常见流程。

但仔细观察的话,其中很多步骤都是冗余,比如每次 本地打包改版本号打tag发给指定人 ,这些步骤都显得很机械(或者比较呆),特别是如果是在 bug fix 阶段,更是繁琐。

那如果借助 CI,我们应该如何优化上述步骤呢?

  • 当我们每次提一个 PR 或者 push 时,就自动去打测试包,并执行一些我们自定义的一些 check,如 代码检查包大小检查自动化测试 等等,并将最后打出的 apk 上传到 fir 或者其他地方。并借助 webhook ,从而实现 飞书钉钉 等方式通知相关同学;
  • 发布新的 release版本 前,改版本号时,也可以支持自动化版本号。比如可以利用 git tag 作为版本号,commit 记录作为code,并与 CI 联动,实现动态指定;
  • 而当我们每次发布 release 版本时,通常情况下,我们都会打一个 tag ,然后 push 。所以我们也可以利用 CI ,发现有新的 tag 时,则触发工作流执行,从而去自动发布一个 release 版本,并且执行一遍打包,将相关产物上传到我们指定的位置;并根据项目的规则总结出相应的 release 更改信息,并更新描述,最后再将版本信息通知到相关运营同学;
  • 在应用包上传的过程中,人工必不可少会出现传错包的情况,此时也可以借助 CI 实现打包完成后自动上传应用商店,比如 华为、小米、Gogole 目前就支持 api 上传

如果上述步骤,你们团队都已经实现了,那就证明对于 Android工程 而言,你们的基础 CI/CD 设施已经做的很不错了,不妨为自己点点赞。

换个角度而言,CI 几乎可以完成大多数重复项工作,从而为我们节约时间。而使用 Github Action 实现上述步骤,如虎添翼,更为方便。

快速入门教程

对于 GitHub Action 而言,官方规定了工作流文件必须存储在代码仓库的 .github/workflows 目录中,文件名必须以 .yml.yaml 结尾,从而便于 Github 识别这是一个工作流程。

创建新的工作流

要创建一个工作流,有两种方式:

  • 在线创建:Github-Reposity-actions 里去创建,创建过程中可以随时添加别的工作流;
  • 本地创建: 在项目目录里创建 .github/workflows 文件夹,并在其中创建你的工作流文件,Github 会自动按照规则识别;
1. 在线创建

我们直接去相应的 Github 仓库底下,点击 Actions ,此时有两种选择:

  • 在现有的工作流模版上进行创建;
  • 新创建自己的工作流文件;

比如下面的示例中,我们搜索 Android ,并选择 Android CI 模版进行创建,如下所示:

在上面的图2里,这是官方给我们的 基础Android CI 模版,具体的逻辑我们下面再解释。图中箭头所指的是一些比较热门的 Action ,可以选择其中一个,快速复制(引用)到我们自己的工作流中,即相当于添加一个新的子步骤。

2. 本地创建

我的 Android CI 为示例,直接复制到本地新建的 android.yml 中,如下所示:

将相应的工作流 push 之后,如图所示,我们会发现,我们新 push 的工作流已经被触发了多次(原因下面解释),而列表最顶部的,也是最新的,即正在运行的工作流。

点击进去看一下,如下图所示:

左边的图表示这是本次的运行结果,以及一些工件的上传或者日志输出位置;而右图则代表这个工作流具体执行子步骤列表,我们也可以点击去查看每一个步骤做的结果。

示例工作流分析

如下所示,这是我们上面步骤创建的 Android CI 工作流,其目的是用于每次 push 代码后,执行一次 build ,具体代码如下:

代码语言:javascript
复制
name: Android CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: gradle

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      - name: Build with Gradle
        run: ./gradlew build

相应的,其内部的含义如下所示:

  • name: 表示当前工作流的名字是什么。
  • on: 表示当前工作流在什么情况下被触发。 比如这个示例中,我们触发的时机有两个,即 push || pr 时,并且限制了必须是在 main分支 。再解释的通俗点就是: 当我们在main分支push了代码,或者提了一个新的PR,要合并到main分支时,此时就会触发这个工作流。 ps: 这也是为什么我们上面的截图里,创建一个新的工作流后,为什么会出现运行多次(首次创建时main分支触发+push行为触发)。
  • jobs: 指的是当前任务列表。比如上述示例中我们只有一个任务,名为 build,当然也可以使用 name: 进行重命名。
  • runs-on: 指定当前任务运行的环境。比如上述示例中任务是在 ubuntu-latest 系统下运行。
  • steps: 指定当前任务的步骤列表。比如当前示例中,我们有多个任务,分别如下:
    • -拉代码;
    • -设置 jdkGradle 环境;
    • -为 Gradle 设置运行权限;
    • -执行 Gradle 命令: build;

    uses: 用于导入开源的 Action 或者自定义的 Action ,从而在自己的工作流中进行使用; run:shell 环境中执行一段命令,常用于执行 cmd 命令;

需要注意: yml 文件,严格控制段落间的缩进,所以如果 IDE 提示异常,或者排列不齐时,经常会出现工作流运行时报错。

常用的环境变量

在使用 Github Action 时,我们常常会遇到需要使用一些环境变量的情况,比如最常用的 Github.token 等等,对于每一个工作流,默认提供了以下环境变量用于使用:

  • GITHUB_ACTION 当前运行的操作的名称,或id步骤的名称;
  • GITHUB_RUN_ID 当前运行的工作流ID,这个 id 是固定的;
  • 更多环境变量见文档

当然,我们也可以自定义一些环境变量,从而将其保存到 Github Action 里的 secrets 中,从而实现安全的存储与使用,而非硬编码的方式。

如下所示,我们将fir.im 的api token保存到secrets里,并取名为FIR_TOKEN:

image-20230516195702703
image-20230516195702703

在具体使用时,如下所示:

代码语言:javascript
复制
- name: echo token
  run: |
    echo "-token: ${{ secrets.FIR_TOKEN }}"

这里我们尝试去打印一下 token ,但是结果肯定是 [**] 。因为 Github Action 默认会对其进行隐藏,从而防止其被意外泄漏。

小练习

自动化打包

在开发中,我们日常接触最多的无非就是 [fix bug] & [create new bug] 😂,而如果每次如果都要手动打包,再转发给测试同学,无疑是一件及其浪费时间和无聊的事情。

本小节示例代码见:Android CI

这里我们以打包并上传fir 为例,如下所示:

image-20230517224017277
image-20230517224017277

上述流程如下:

  • 拉代码;
  • 安装 && 配置gradle环境;
  • 打debug包;
  • 安装 fir-cli & 上传apk;

上面内部使用的 FIR_TOKEN 正是我们上面在介绍环境变量部分时,自己定义的。

当然做的更详细点,这里还可以加上打包成功后webhook到飞书或者钉钉等,以飞书为例,可以使用我司另一个小伙伴写的这个 xiachufang/action-feishu,碍于篇幅,这里就不做解释了。

自动化版本号

本小节示例代码见:settings.gradlerelease.yml

在日常发版本的过程中,我们都有打 tag 的经历,比如每周在发布新的版本之前,打 新版本tag ,同时打新的线上包。这个时候,如果每次都要去再改一次硬编码里的 versionName 以及 versionCode ,无疑有点烦人,而且人工操作,依然存在改错的问题😶。

此时,常见的方式是利用 tag 作为 versionNamegit commit 数作为 versionCode 。这种方式固然好用,但是还是不够严谨,对于常见的团队而言,一般有更统一的名称,如 版本名@版本号,示例:2.1.1@807,当然这都是后话了。

要实现上面的基础需求,需要我们对 GradleGit 有一丢丢使用经验。比如,怎么获取 最新tag 呢?怎么获取 commit 数呢?

解决方式如下所示:

image-20230517224617923
image-20230517224617923

如上所示,我们直接在 settings.gradle 中新增了以下代码。目的是当 Gradle 加载完当前项目信息之后,此时就利用 cmd 去获取一下当前的 最新tagcommit数,并将其设置给 ext,从而便于我们在其他地方引用。

外部使用方式:rootProject.versionCode

此时 build 我们的项目, build 日志部分就会打印下面语句:

versionName:xxx,versionCode:x

ps: 如果你的 versionName 是空,请注意是否打过 tag 🙃。 [git tag xxx]


上面的方法看着似乎没什么问题,但是如果你实际用几次就会发现,如果你 两个tag之间并没有任何变化的话,此时 gitVersionTag() 取出的 tag永远不是最新的那一条(不知道该怎么解决)😅。

这个时候,我们就可以利用 Github Action,获取最新 release.tag,然后将其以 gradle传参 的方式传递到我们本次编译中,从而实现自动化版本号。

如下所示:

image-20230516225353898
image-20230516225353898

我们重新调整下上述的写法,每次优先获取外部传入的参数 versionName 以及 versionCode,同时对其check。如果没传递或者为null,则本地重新利用Git去获取,否则就使用指定的参数。

release.yml

image-20230516225440888
image-20230516225440888

在具体的工作流脚本这里,我们的触发时机选择为每次发布新的 release 时,此时就去获取本次 release 对应的 tag_name,并在打包时,通过 Gradle 命令行传参的方式,将其传递给我们本次的打包流程。

上面的 env:用于设置一个或多个环境变量。 比如在这个示例里,我们定义了一个名为 VERSION_NAME 的变量,其的值取自 本次release所对应的tag_name ,而 {{ xx }} 这种取值方式,则是 Github Action 中的一个规范。而在shell里,我们可以不加 {{ }} ,直接 $xx

自动化发布release

本小节示例代码见:create_release.yml

每个版本发布 release 时,我们一般都要去写一遍描述,但如果每个版本都去写一遍,无疑非常呆。

所以,那能不能把两个版本之间的 PR 自动收集下来,然后写到 release 的描述里呢?🤔

回答肯定是可以的,而且 Github 也提供了默认的方式,如下所示:

看着效果还不错,省事不少。👏

那能不能我每次 push tag 时,就自动触发 release创建 呢?

🤖: 你最好别懒死。😂

当然是可以的,Github Action 工作流提供了很多触发时机,所以我们只需要设置触发时机为 push tag 时,然后再去新建 release 即可。

示例代码如下:

image-20230517234135411
image-20230517234135411

这里使用开源的 action,ncipollo/release-action,从而更简单的实现上述需求,当然也可以选择使用 Github Api

效果如下所示:

一些经验分享

关于 Github Action,因为其本身上手难度很低,所以当我们想解决某个问题时,只需要考虑清下面的几个问题:

  • 当前问题 到底 是什么?
  • 有没有开源的 Action ?
  • 如果问题比较复杂,那能不能拆解为多个步骤呢?
  • Github API 能不能解决,能不能搭配其他方式呢?如 shellpythonjargradle;

当然如果你想再探索一点,此时可以考虑以下:

  • 工作流复用,工作流依赖执行,工作流结果传递,工作流并发等等。

常用的一些资料:

总结

本篇,我们从 CI/CD 是什么开始,叨叨絮絮,一直到解决常见开发中的一些问题。纵观这些问题或者场景,虽然并不是特别繁琐,但也构成了 CI 的基本使用单元。希望通过这些场景,能让大家对于 Github CI 有快速的了解及上手体验。

当然我本人也不是一个熟练的 CI工程师 ,更多是个半吊子,所以文章里肯定也有模糊不清的地方,此时就建议大家多搜多试验,或者评论区问我。但对于这些工具方面,我个人的原则一直是,会用即可。当然更好的是,当问题不能直线解决时,我们能不能拆分步骤去逐个解决。

我们生在一个幸运的时代,很多事情,都能很简单的去解决,比如有问题问 GPT,不懂就翻翻源码,而对于一些繁琐的重复项工作,此时不妨交给CI/CD 或者其他 自动化工具。

对于开发者的我们而言,我们只需要明白一个原则:当下要解决什么问题,即可

把时间浪费在更有意思的事情上,真的 泰库辣 😃

见字如面,我们下篇文章再见 👋

参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-05-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 写在开始
  • 什么是 CI/CD?
  • 什么是 Github CI?
  • CI 可以做什么?
  • 快速入门教程
    • 创建新的工作流
      • 1. 在线创建
      • 2. 本地创建
    • 示例工作流分析
      • 常用的环境变量
      • 小练习
        • 自动化打包
          • 自动化版本号
            • 自动化发布release
            • 一些经验分享
            • 总结
            • 参考
            相关产品与服务
            持续部署
            CODING 持续部署(CODING Continuous Deployment,CODING-CD)用以管理软件在经过构建之后的发布和部署交付过程,可以无缝对接上游 Git 仓库、制品仓库实现全自动化部署,同时支持 Webhook 等外部对接能力,方便集成各种开发、运维工具。在配以合适的技术架构、运维工具的基础上,可以方便地实现蓝绿发布、灰度发布(金丝雀发布)、滚动发布、快速回滚等功能。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档