Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

简单聊一聊 github action #24

Open
hacker0limbo opened this issue Sep 30, 2021 · 0 comments
Open

简单聊一聊 github action #24

hacker0limbo opened this issue Sep 30, 2021 · 0 comments
Labels
javascript 原生 JavaScript 笔记整理

Comments

@hacker0limbo
Copy link
Owner

简单聊一聊 github action

Github Action 是 Github 官方出的持续集成服务, 挺早之前就推出了, 这次正好遇到一点需求, 看了一下文档自己写了一个 workflowaction 脚本

文档还是很全的, 但是细节有点多, 写的时候不注意的话很容易踩坑, 而且这个东西无法在本地进行调试, 我只能每次更新了代码后手动 run 一次 workflow, 虽然有一个叫 act 的库貌似支持本地跑的, 但是又要用到 docker 啥的, 我电脑比较旧带不动, 就算了

基本概念

阮一峰老师写过一篇还挺清晰的教程介绍基本概念: GitHub Actions 入门教程. 最基本的几个概念为:

  • workflow
  • event
  • job
  • step
  • action

以官方给的一个 workflow 文件为例, 所有的 workflow 都存放在 .github/workflows 目录下:

# .github/workflows/learn-github-actions.yml
name: learn-github-actions
on: [push]
jobs:
  check-bats-version:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'

该 workflow 的含义为:

  • push 事件触发时激活该 workflow
  • 一共有一个 job, 名字为 check-bats-version, 该 job 跑在最新的 ubuntu 上
  • 该 job 一共有 2 个 step, 每个 step, 对应一个 action
  • 其中第二个 actions/setup-node@v2 action 运行时需要传递参数, 参数名 node-version, 参数值 14

触发 workflow 的事件还有很多, 可以是多个事件, 也可以手动触发, 也可以细分具体到一个事件的不同类型, 具体需要参考官方文档: Events that trigger workflows

关于 workflow 的语法具体也参考官方文档: Workflow syntax for GitHub Actions

需求

虽然 github marketplace 有很多已经写好的 action, 但是还是免不了自己有特质需求需要自定义 action. 比如我的需求是, 我的博客全部写在 issue 里, README.md 通常作为目录按照时间分类放置每篇文章的标题和对应到 issue 的链接, 大致格式是这样:

# 个人博客

## 2019
- [第一篇博客](https://github.com/owner/repo/issues/1)
- [第二篇博客](https://github.com/owner/repo/issues/2)

## 2020
...

但是每次更新 issue 之后就需要手动去更新 README, 我所希望的时候能有 workflow 能帮我自动处理这些事情, 在博客更新之后通过 github 提供的 API 手动提交一次 commit 更新我的 README 目录

实现

action.yml 和 workflow.yml

官方有关于自定义 action 的文档: About custom actions. 以及如何自定义 JavaScript action: Creating a JavaScript action 还算详细. 不过有一些细节需要注意:

  • action 命名一定是 action.yml 或者是 action.yaml
  • 自定义 action 的文件位置一般放在根目录下, 当然放在 .github 文件夹下也行, 不过在 workflow 的时候还需要 checkout 定位到 repo 上. 而且只有在根目录下的 action 才能发布到 github marketplace.

action.yml 的大致语法可以参考: Metadata syntax for GitHub Actions. 这里简略罗列一下我的:

name: 'Action Name'
description: 'Description for custom action'
inputs:
  repoToken:
    description: 'github access token'
    required: true
runs:
  using: 'node12'
  main: 'dist/index.js'

说明一下:

  • namedescript 分别是自定义 action 的名字和描述
  • inputs 是该 action 的输入源, 可以有多个. 由于我的需求需要用到 github 的 API, 使用过程需要 token 来 authentication. 这个 token 每次 workflow 运行时会自动生成, 不需要手动输入. 因此在编写 workflow 的时候要注明. 这里我给的输入源名字为 repoToken
  • runs 注明了使用 node12, 同时对应的 JavaSctipt action 文件为根目录下的 dist/index.js

关于 token, 官方有提到: Automatic token authentication. 如文中所述, 在编写 workflow 时, 需要使用 with 语法提供对应的参数, 这个参数就是自动生成的 token.

对应我的 workflow 文件内容如下:

name: My Workflow
on:
  workflow_dispatch:
  issues:
    types: [opened, edited, deleted]
jobs:
  sync-readme:
    runs-on: ubuntu-latest
    name: My job name
    steps:
      - name: use my custom action
        uses: owner/repo@master
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}

这里 on 监听了两个事件, 一个是 workflow_dispatch, 一个是 issues 具体的类型. workflow_dispatch 是为了允许手动跑 workflow, 方便线上调试. 具体可以看: Manually running a workflow. issues 我提供了三个类型分别在新增一条 issue, 编辑一条 issue 和删除一条 issue 时触发该 workflow.

最后是绑定自定义的 action, 注意这里 with 下的 repoToken 和上面 action.yml 中的 inputs 里对应

编写脚本

需要用到两个库:

第一个库主要用于获取输入输出, 比如上文提到的 token. 打日志. 第二个库提供了 Github API 的接口, 返回的是一个已经 auth 过的 Octokit REST 客户端

由于我的需求是根据在我更新完 issue 之后根据最新的 issue 信息更新我的 README. 所有思路也很简单, 发一次请求获取所有的 issue 信息, 然后再根据 issue 信息做一次 commit 更新 readme. 需要用到 3 个 API:

这里注意一下, 由于在使用第三个 api 即 createOrUpdateFileContents 时候, 如果是更新文件内容需要提供该文件的 sha 值, 所以只能先通过 getContent 获取到 sha 值之后再更新, 同时更新的内容必须使用 Base64 encoding 加密.

最后的文件大体思路为:

const core = require('@actions/core');
const github = require('@actions/github');
const { Buffer } = require('buffer');

function run() {
  const repoToken = core.getInput('repoToken');
  const octokit = github.getOctokit(repoToken);

  octokit.rest.issues
    .listForRepo({
      owner: 'xxx',
      repo: 'yyy',
    })
    .then(({ data: issues }) => {
      const content = contentFromIssues(issues)

      octokit.rest.repos
        .getContent({
          owner: 'xxx',
          repo: 'yyy',
          filePath: 'README.md'
        })
        .then(({ data }) => {
          const { sha } = data

          octokit.rest.repos
            .createOrUpdateFileContents({
              ...config,
              path: 'README.md',
              message: 'feat: xxx',
              content: Buffer.from(content).toString('base64'),
              sha,
            })
            .then((res) => core.info('success to update'))
            .catch(error => core.setFailed(error.response.data.message))
        })
        .catch(error => core.setFailed(error.response.data.message))
    })
    .catch(error => core.setFailed(error.response.data.message))
}

run()

最后, 官方文档建议使用 @vercel/nccaction 脚本进行打包, 这么做也是为了避免上传 node_modules. 或者另一种做法是在 workflow 跑的时候手动安装需要的依赖. 这里我选择前一种方法. 这里 npm i -g @vercel/ncc 后使用 ncc build action.js 即可打包文件到 dist/index.js 中. 遗憾的是貌似无法指定对应的文件名.

参考

@hacker0limbo hacker0limbo added the javascript 原生 JavaScript 笔记整理 label Sep 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
javascript 原生 JavaScript 笔记整理
Projects
None yet
Development

No branches or pull requests

1 participant