前言

Github Action 上线有一阵子了,大概两周前我的所有账号也是都陆陆续续开发了beta测试的权限。然后就来研究了下这个新的 CI 系统是怎么回事。看介绍,和之前碰到的一些CI系统不太一样的地方是,Github是做了一个商店的功能。这样大家就可以自己定义自己的Action,然后方便别人复用。同时也可以统一自己的或者组织在构建过程中的一些公共流程。

目前 Github Action 的文档还是不是特别的齐全,有些东西找起来比较费劲,还要参考下别人写的成品来理解。我是先试着用 Github Action 来发布我的blog,然后想给 xresloader 的自动发布也改成 Github Action ,但是找了一圈发现没有合适的已有的Action,就自己写了一个。功能类似 travis-ci 的 Github Release Deployment,发布到了商店: https://github.com/marketplace/actions/uploadtogithubrelease

Workflow

Github Action 的workflow开始都是以仓库里的 .github/workflow/*.yml 为准。我实际测试了多个yml配置相同的name,在实际执行中都是会执行的,不过到 Actions 页面看的时候就会多个一样的名字。Github Action 附带了显示 badge 的功能,地址是 https://github.com/[仓库所有者名字]/[仓库名]/workflows/[encodeUri(workflow名)]/badge.svg ,但是目前为止,这个地址不包含分支名,所以它必须显示的是主分支的状态。这就有点不爽,比如我把自己的Action发布到v1分支,如果我不把主分支设置成v1,就只能显示master分支的构建状态。

执行环境

看到文档里说 Github Action 里有 ubuntu 、 macOS 和 Windows,里面自带的工具也是蛮齐全了。但是还是避免不了需要安装其他工具的。 Windows里带有chocolatey,macOS里带有homebrew,应该都还比较方便。我试了下ubutnu, sudo apt update -y && sudo apt upgrade -y 会导致一大坨更新,然后失败了。 直接 sudo apt update -y && sudo apt install 包 也失败了,不知道是不是依赖包版本的问题, sudo snap install 仍然会失败。 然后我看了下官方和别人的setup的action。都是直接去下载预编译包手动部署的。还是非常麻烦。

事件和上下文

Github Actionon 属性里的事件开始的。 Github Action 比较强大的地方是,除了常规的 *push定时事件 外,他还支持茫茫多的 github hook 事件。个人感觉这是它自己深度集成后对其他第三方服务的优势所在。不过有一些事件是可以带参数的,我看了老半天才看懂 https://help.github.com/en/articles/events-that-trigger-workflows 里说的 types 参数是怎么回事儿。截至我写这篇文章的时间,前面贴的链接里的文档已经更新了一版,现在应该对types参数更容易理解一些,里面也附带了一些sample,但是比如 push 事件是有 branches 参数和 tags 参数的,至少到目前为止这里还没有详细说明,也不知道是否有其他参数可以用, 而只有在 https://help.github.com/en/articles/workflow-syntax-for-github-actions#on 才有提及这两个参数的用法。

Github Action 的上下文的数据访问是结构化的,详见 https://help.github.com/en/articles/contexts-and-expression-syntax-for-github-actions 。这个感觉比较好,以后扩展也会比较容易。其中 matrix 功能目测是对每个变量 foreach 的组合都会跑一遍, 而不是像 travis 或是 appveyor 那种是一个列表然后只是可以多个参数的模式。

Action

Action 的使用

Github Action 的实际执行单元是里面的一个个的 action ,如果不use其他的action的话,就是普通的跑个脚本,像这样:

name: "find env"
  run: |
    echo "Hello World!"
    set | grep GITHUB_ | grep -v GITHUB_TOKEN

如果使用其他的action的话,按照现在的结构来看大约这样:

uses: xresloader/upload-to-github-release@v1
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
  file: "*.md"
  tags: true
  draft: true

这里的 use 的是对应github仓库的 owner/repo 名字然后 @分支 。 环境变量可以通过 env 传入,插件参数通过 with 传入。不过按开发文档看,参数只能是字符串类型。

Action 开发

商店里已有的Action 满足不了需求的时候,还可以自己开发action用。其实 Github Action 自带的 actions 和我们自己没太大区别,不过就是官方带的可能用了一些不可言表的插件和奇奇怪怪的内部功能。

到目前位置, Github Action 的插件有两种,一种是 基于docker 的, 另一种是 基于nodejs 的。 我看了一些插件的实现,大多是 基于docker 的。不知道这个对跨平台是怎么实现的,比如运行环境是 Windows 但是 action是基于 ubuntu的docker, 毕竟docker不能跨内核。还是说上层虚拟机那层做了目录映射。

我只研究了下 基于nodejs 的action开发。插件的开发流程比较简单, 先是写一个 action.yml 文件描述插件,详情 https://help.github.com/en/articles/metadata-syntax-for-github-actions 里面有。完了以后github的仓库主页里就直接会提示你是否要把action publish到Marketplace里,也会多一个按钮说是可以一键使用到你的仓库里。插件本身带有输入参数和输出参数。输出参数会输出到 steps.<step id>.outputs.<output name> 的上下文里,方便后续其他action使用。

基于nodejs 的action,官方提供了一些 工具npm包 用于方便action,感觉还是停方便实用的。 我一开始想直接用Github v4的graphql API 来写插件的,但是目前的graphql API好像还没法上传文件。而我本来就是因为现有没有特别合适的上传release文件到Github Release里想开发个上传文件的action。 而 Github v3的rest APIGithub v4的graphql API 混用反而徒增麻烦,所以目前还是先用 v3 了,所幸action功能比较简单,以后万一升 Github v4的graphql API 也比较容易。

好久没写nodejs,顺便试了试nodejs的Promise、await。用起来各种爽啊,还试了下 yarn 。前端的工具真的是日新月异呀。目前action的调试非常麻烦,我还是新建了一个测试仓库,通过不断触发来log调试的。官方给的调试方法目前也只有这个。不知道是否哪天 Github 会提供个什么调试环境让这个过程更容易一些。前面也说了,按开发文档看,参数只能是字符串类型。但是我需要上传多个文件,所以对于需要数组的我都是采用了 用;分割的字符串 的形式。代码里拆成数组。

Action - Upload To Github Release

最后广告一波我写的插件啦。功能和 travis 的 Deployment to Github Release 差不多,基本上就是后续我一些项目的自动发布迁移后的替代品。

商店地址: https://github.com/marketplace/actions/uploadtogithubrelease 仓库地址: https://github.com/xresloader/upload-to-github-release

完整语法及默认参数:

uses: xresloader/upload-to-github-release@v1
env:
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}   # (必须)token,每个仓库的workflow会自动带一个 ${{ secrets.GITHUB_TOKEN }} ,直接用这个即可
with:
  file: ""                                    # (必须)要上传的文件路径,支持通配符,多个用分号(;)隔开
  overwrite: false                            # (可选)如果有同名文件是否覆盖
  tags: false                                 # (可选)是否只处理有tag的事件
  draft: true                                 # (可选)如果需要创建新Release,是否是草稿Release
  prerelease: false                           # (可选)如果需要创建新Release,是否是Prerelease
  branches: ""                                # (可选)是否只处理执行分支的事件,多个用分号(;)隔开

输出参数:

  • release_name: Release的名字
  • release_url: Release的URL地址
  • release_tag_name: Release绑定的tag名字
  • release_commitish: Release的提交号

测试用的仓库里有一些例子说不定可以用来参考: https://github.com/xresloader/upload-to-github-release-test