我正在参加「启航计划」

布景

关于 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 渠道紧密集成、易于装备、支撑多种语言和环境、供给丰厚的集成能力等。它能够帮助开发团队主动化构建和测验进程,进步代码质量和开发功率。

详细运转示例中如下图所示:

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

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 模版进行创立,如下所示:

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

在上面的图2里,这是官方给咱们的 根底Android CI 模版,详细的逻辑咱们下面再解说。图中箭头所指的是一些比较热门的 Action ,能够挑选其间一个,快速仿制(引用)到咱们自己的作业流中,即相当于增加一个新的子过程。

2. 本地创立

咱们以方才上面截图中的 Android CI 为示例,直接仿制到本地新建的 android.yml 中,如下所示:

创立作业流 作业流运转效果
写给Android工程师的 Github CI 快速指北
写给Android工程师的 Github CI 快速指北

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

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

作业流运转成果 作业流详细履行过程
写给Android工程师的 Github CI 快速指北
写给Android工程师的 Github CI 快速指北

左面的图表明这是本次的运转成果,以及一些工件的上传或许日志输出方位;而右图则代表这个作业流详细履行子过程列表,咱们也能够点击去查看每一个过程做的成果。

示例作业流剖析

如下所示,这是咱们上面过程创立的 Android CI 作业流,其意图是用于每次 push 代码后,履行一次 build ,详细代码如下:

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:

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

在详细运用时,如下所示:

- name: echo token
  run: |
    echo "-token: ${{ secrets.FIR_TOKEN }}"

这儿咱们尝试去打印一下 token ,可是成果必定是 [**] 。由于 Github Action 默许会对其进行躲藏,然后避免其被意外泄漏。

小操练

主动化打包

在开发中,咱们日常触摸最多的无非便是 [fix bug] & [create new bug] ,而假如每次假如都要手动打包,再转发给测验同学,无疑是一件及其糟蹋时刻和无聊的工作。

本末节示例代码见:Android CI

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

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

上述流程如下:

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

上面内部运用的 FIR_TOKEN 正是咱们上面在介绍环境变量部分时,自己界说的。

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

主动化版别号

本末节示例代码见:settings.gradle、release.yml

在日常发版其他进程中,咱们都有打 tag 的阅历,比方每周在发布新的版别之前,打 新版别tag ,一起打新的线上包。这个时分,假如每次都要去再改一次硬编码里的 versionName 以及 versionCode ,无疑有点烦人,并且人工操作,依然存在改错的问题。

此刻,常见的方法是运用 tag 作为 versionNamegit commit 数作为 versionCode 。这种方法固然好用,可是还是不行谨慎,关于常见的团队而言,一般有更一致的称号,如 版别名@版别号,示例:2.1.1@807,当然这都是后话了。

要完成上面的根底需求,需求咱们对 GradleGit 有一丢丢运用经历。比方,怎样获取 最新tag 呢?怎样获取 commit 数呢?

处理方法如下所示:

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

如上所示,咱们直接在 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传参 的方法传递到咱们本次编译中,然后完成主动化版别号。

如下所示:

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

咱们从头调整下上述的写法,每次优先获取外部传入的参数 versionName 以及 versionCode,一起对其check。假如没传递或许为null,则本地从头运用Git去获取,否则就运用指定的参数。

release.yml

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

在详细的作业流脚本这儿,咱们的触发机遇挑选为每次发布新的 release 时,此刻就去获取本次 release 对应的 tag_name,并在打包时,通过 Gradle 指令行传参的方法,将其传递给咱们本次的打包流程。

上面的 env:用于设置一个或多个环境变量

比方在这个示例里,咱们界说了一个名为 VERSION_NAME 的变量,其的值取自 本次release所对应的tag_name ,而 {{ xx }} 这种取值方法,则是 Github Action 中的一个规范。而在shell里,咱们能够不加 {{ }} ,直接 $xx

主动化发布release

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

每个版别发布 release 时,咱们一般都要去写一遍描绘,但假如每个版别都去写一遍,无疑非常呆。

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

回答必定是能够的,并且 Github 也供给了默许的方法,如下所示:

创立新的release 终究成果页
写给Android工程师的 Github CI 快速指北
写给Android工程师的 Github CI 快速指北

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


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

: 你最好别懒死。

当然是能够的,Github Action 作业流供给了许多触发机遇,所以咱们只需求设置触发机遇为 push tag 时,然后再去新建 release 即可。

示例代码如下:

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

这儿运用开源的 action,ncipollo/release-action,然后更简单的完成上述需求,当然也能够挑选运用 Github Api

效果如下所示:

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

一些经历共享

关于 Github Action,由于其自身上手难度很低,所以当咱们想处理某个问题时,只需求考虑清下面的几个问题:

  • 当时问题 到底 是什么?
  • 有没有开源的 Action ?
  • 假如问题比较复杂,那能不能拆解为多个过程呢?
  • Github API 能不能处理,能不能调配其他方法呢?如 shellpythonjargradle;

当然假如你想再探究一点,此刻能够考虑以下:

  • 作业流复用,作业流依靠履行,作业流成果传递,作业流并发等等。

常用的一些资料:

  • Github Action开源库查找;
  • Github Action文档;

总结

本篇,咱们从 CI/CD 是什么开端,叨叨絮絮,一直到处理常见开发中的一些问题。纵观这些问题或许场景,尽管并不是特别繁琐,但也构成了 CI 的基本运用单元。希望通过这些场景,能让大家关于 Github CI 有快速的了解及上手体会。

当然我本人也不是一个娴熟的 CI工程师 ,更多是个半吊子,所以文章里必定也有模糊不清的当地,此刻就建议大家多搜多试验,或许谈论区问我。但关于这些东西方面,我个人的准则一直是,会用即可。当然更好的是,当问题不能直线处理时,咱们能不能拆分过程去逐一处理。

咱们生在一个幸运的时代,许多工作,都能很简单的去处理,比方有问题问 GPT,不懂就翻翻源码,而关于一些繁琐的重复项作业,此刻无妨交给CI/CD 或许其他 主动化东西。

关于开发者的咱们而言,咱们只需求理解一个准则:当下要处理什么问题,即可

把时刻糟蹋在更有意思的工作上,真的 泰库辣 :)

见字如面,咱们下篇文章再会

参考

  • GitHub Actions文档

关于我

我是 Petterp ,一个 Android工程师。假如本文,你觉得写的还不错,无妨点个赞或许收藏,你的支撑,是我继续创造的最大鼓励!

欢迎关注我的 公众号(Petterp) ,等待与你一起行进 :)