在 GitHub Action 中获取改动文件
在 GitHub Action 中可以通过下面的两种方式来获得变更的文件:
- 在仓库下通过使用
git
命令对比两个分支。 - 借助 GitHub 提供的 API 来查询变更的文件。
这篇文章主要介绍第二种方法,我采用第二种方式实现了获取 Pull Request 中的变更文件,针对变更的文件进行单元测试。
GitHub 提供了 /repos/{owner}/{repo}/pulls/{pull_number}/files
API 用于查询 Pull Request 的相关内容。
想要使用此 API ,我们需要提供 owner、repo、pull number 这三个参数的内容。owner、repo 和 pull number 可以通过 GitHub Action 中的上下文来获取:
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Get PR info
run: |
echo "PR Message github.event.number: ${{ github.event.number }}"
echo "PR Message github.repository: ${{ github.repository }}"
这里给出了一段 GitHub Action 的脚本,在 Get PR info
任务中,借助 linux 的 echo 命令,打印来 github.event.number 和 github.repository。想要对这段脚本进行测试,可将其提交到 .github/workflows/test.yml
中,然后通过其他分支向目标分支创建 PR。(备注:github.event.number
来自Webhook 的共有属性)
通过执行上述代码,我们可以看到输出的 PR 的 ID 和 repository。接下来要使用他们调用 GitHub 提供的 API。这里我采用了 curl 来进行 GitHub API 的调用:
pr_number=${{ github.event.number }}
files_url="https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number/files"
files_response=$(curl -sSL -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" $files_url)
在这段代码中即存在 ${{ xx }}
又存在 $xx
,我们需要先区分什么时候使用什么形式。${{}}
是 GitHub 提供的插值表达式,在 GitHub Actions 执行的时候会对这种表达式进行求值。简单理解的话,就是将表达式计算出的结果填到脚本中,然后在执行脚本。$xx
是 Linux shell 的变量调用的方式,所以在 GitHub Actions 中我们实际执行的主要是 Linux shell 脚本。
在这段代码中,声明变量的方式形如:
shellVariable=1
调用变量的方式形如:
echo "$shellVariable"
到这里我们已经获取到了 GitHub API 返回的结果,接下来需要对结果进行处理,即提取变更文件。
files_changed=$(echo "$files_response" | jq -r '.[].filename')
echo "PR files: $files_changed"
component=$(echo "$files_changed" | grep -E -i 'src\/packages\/([a-z]+)(\/[a-z_\.]*)*' | sed -E 's/src\/packages\/([a-z]+)(\/[a-z_\.]*)*/\1/i' | awk 'END{print}')
npm test -- $component
因为 GitHub API 返回的是 JSON 格式:
[
{
"sha": "bbcd538c8e72b8c175046e27cc8f907076331401",
"filename": "src/packages/button/button.tsx",
"status": "added",
"additions": 103,
"deletions": 21,
"changes": 124,
"blob_url": "https://github.com/octocat/Hello-World/blob/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
"raw_url": "https://github.com/octocat/Hello-World/raw/6dcb09b5b57875f334f61aebed695e2e4193db5e/file1.txt",
"contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/file1.txt?ref=6dcb09b5b57875f334f61aebed695e2e4193db5e",
"patch": "@@ -132,7 +132,7 @@ module Test @@ -1000,7 +1000,7 @@ module Test"
}
]
所以我们通过 jq 从 GitHub API 返回的结果中提取我们需要的数据,即第一行代码中的 jq -r '.[].filename
,这句代码的意思是取 filename
字段的值,然后以原始文本形式返回。对于上面的 JSON 数据,jq 的处理结果是:src/packages/button/button.tsx
。(jq 介绍)
获取变更文件后,还需要将src/packages/button/button.tsx
中的 button 提取出来,以便传递给 npm 脚本。对于变更文件的处理,主要借助 Linux 中的 grep、sed、awk。
grep -E -i
表示使用正则表达式,并且忽略大小写。
sed -E
同样表示使用正则表达式,但是它的忽略大小写是写到后面的参数中。
通过这些步骤,就可以提取 PR 中的变更文件,对文件进行过滤,然后针对特定文件执行特定的处理。
总的来说,GitHub 提供了 Actions 来帮助我们做自动化的处理,并且允许我们通过 yml 格式的脚本来进行配置,针对 yml 的脚本,GitHub 提供了内置的变量,通过插值方式提供给开发者使用,而且 run 命令提供给开发者使用 shell 的便捷性。