为你的学习轨道设置持续集成(CI)非常重要,因为它有助于发现错误。
Exercism 的代码仓库(包括学习轨道仓库)使用 GitHub Actions 来运行 CI。 GitHub Actions 基于_工作流_,工作流定义了在特定事件发生时(例如推送提交)自动运行的脚本。 想进一步了解 GitHub Actions 工作流,请查看工作流文档。
学习轨道预装了许多工作流,其中大多数你都_不应该_修改(它们被称为_共享工作流_)。
不过有一个工作流你_应该_修改,那就是 test.yml工作流。
test.yml工作流的目标是验证学习轨道中的练习是否状态正常。
当向main分支或拉取请求的分支推送时,该工作流会自动运行(用 GitHub Actions 的术语说,就是被_触发_)。
工作流本身不应做太多事情,只需要:
如前所述,练习是通过一个脚本来验证的,即 bin/verify-exercises(bash)脚本。
这个脚本_差不多_完成了,它会做以下事情:
unskip_tests函数,你可以在其中取消跳过测试文件中的测试(可选)run_tests函数,你需要在其中运行测试(必需)run_tests和 unskip_tests函数是你唯一需要实现的东西。
如果你的学习轨道支持跳过测试,我们必须确保在验证练习的示例/范例解答时没有测试被跳过。 一般来说,学习轨道支持“取消跳过”测试的方式有两种:
test.skip改为 test。SKIP_TESTS=false。如果跳过测试是基于文件的(上面提到的第一种方式),就编辑 unskip_tests函数来修改测试文件(现有代码已经处理了测试文件的遍历)。
unskip_test函数运行在练习目录的副本上,所以你可以随意修改文件。
Arturo 学习轨道的 bin/verify-exercises file 使用 sed 来取消跳过测试文件中的测试:
unskip_tests() {
jq -r '.files.test[]' .meta/config.json | while read -r test_file; do
sed -i 's/test.skip/test/g' "${test_file}"
done
}
如果取消跳过测试需要设置环境变量,请确保在 run_tests函数中设置了它。
run_tests函数负责运行练习的测试。
调用该函数时,示例/范例文件已经复制到(存根)解答文件,所以你只需要调用正确的命令来运行测试。
如果所有测试都通过,函数必须返回退出码 0,否则返回非零的退出码。
run_tests函数运行在练习目录的副本上,所以你可以随意修改文件。
验证练习脚本的默认方案是使用语言本身的工具(SDK/二进制文件等),大多数学习轨道都采用这种方式。 每个学习轨道运行测试的方式各不相同,但通常只是一条命令。
Arturo 学习轨道的 bin/verify-exercises file 修改 run_tests函数,让它直接在测试文件上调用 arturo 命令:
run_tests() {
arturo tester.art
}
第二种方案是通过运行学习轨道的测试运行器来验证练习。 这当然取决于该学习轨道是否有一个可用的测试运行器。
如果你的学习轨道还没有测试运行器,你可以:
需要对默认的 bin/verify-exercises脚本做出以下修改:
docker 命令可用docker run 在每个练习上运行测试运行器 Docker 镜像jq 验证 Docker 容器返回的 results.json文件表明所有测试都通过unskip_test函数以及对该函数的调用这种方式的主要好处是,它能最大程度地模拟测试在生产环境(网站上)的运行方式。 采用这种方式,在 CI 中通过但在生产环境中失败的情况会更少。 这种方式的缺点是通常更慢,因为需要拉取 Docker 镜像,还有 Docker 本身的开销。
Unison 学习轨道的 bin/verify-exercises file 添加了检查,以确认 docker 命令也已安装:
required_tool docker
然后,它拉取该学习轨道的测试运行器镜像:
docker pull exercism/unison-test-runner
接着,它修改 run_tests函数,使用 docker run 在当前练习(位于工作目录中)上运行测试运行器,然后用 jq 命令检查状态是否正确:
run_tests() {
local slug
slug="${1}"
docker run \
--rm \
--network none \
--mount type=bind,src="${PWD}",dst=/solution \
--mount type=bind,src="${PWD}",dst=/output \
--tmpfs /tmp:rw \
exercism/unison-test-runner "${slug}" "/solution" "/output"
jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}
最后,我们需要修改 run_tests 命令的调用方式,因为它现在需要 slug 参数:
run_tests "${slug}"
现在 verify-exercises脚本已经完成,是时候最终确定 test.yml工作流了。
具体怎么做,取决于为 verify-exercises脚本选择了哪种实现方案。
如果 verify-exercises脚本直接使用语言本身的工具,测试工作流就需要安装:
完成这些之后,verify-exercises应该就能按预期工作了,你也成功设置好了 CI!
示例请见 Arturo 学习轨道的 test.yml 工作流:
name: Test
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
ci:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev
- name: Install Arturo
run: bin/install-arturo
env:
GH_TOKEN: ${{ github.token }}
- name: Verify all exercises
run: bin/verify-exercises
第二种方案是通过运行学习轨道的测试运行器来验证练习。 这个方案需要满足两个条件:
verify-exercises脚本使用测试运行器 Docker 镜像来运行练习的测试如果你的学习轨道还没有测试运行器,你可以:
这种方式有几个优点:
主要的缺点是它可能更慢,因为需要拉取 Docker 镜像,还有 Docker 本身的开销。
拉取测试运行器 Docker 镜像有几种方式:
verify-exercises文件中下载镜像。
Unison 学习轨道采用的就是这种方式。那么该用哪种方式呢?
我们建议_至少_实现第 1 种方案,让 verify-exercises脚本能够_独立运行_。
如果你的镜像特别大,那么同时实现方案 3 可能会有好处,它会把构建好的 Docker 镜像存入 GitHub Actions 缓存。
之后的运行就可以直接从缓存读取 Docker 镜像,而不必下载,这对性能可能更好(请自行测量确认)。
第三种备选方案是前两种方案的混合。
这里我们同样使用测试运行器 Docker 镜像,只不过这次是在_该 Docker 镜像内部_运行 verify-exercises脚本。
要启用这个方案,我们需要把工作流的容器设置为测试运行器:
container:
image: exercism/vimscript-test-runner
然后我们就可以跳过依赖和工具的安装步骤(它们已经在测试运行器 Docker 镜像中安装好了),直接运行 bin/verify-exercises脚本。
vimscript 学习轨道的 test.yml 工作流采用了这个方案:
name: Verify Exercises
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
ci:
runs-on: ubuntu-24.04
container:
image: exercism/vimscript-test-runner
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
- name: Verify all exercises
run: bin/verify-exercises