学习一下Github仓库的工作流语法,希望在Github仓库中自动执行测试。 重点关注两类项目所需要的基本测试工作流,分别是跨平台CMake项目和LaTex项目。

概述

Github仓库提供工作流来自动化执行应用的部署,测试和发布等流程。工作流的实质是在Github服务器上提供了几个临时的虚拟环境(Docker),让用户在提交或其他Git行为后触发工作流,然后在虚拟机环境中自动执行相关的指令。

例如,可以使用一个工作流来构建和测试拉取请求,使用另一个工作流在每次创建版本时部署应用程序, 还有另一个工作流程在每次有人打开新问题时添加标签。

注意:

  • Github仓库支持多个工作流,它们可以同时触发或分别触发。
  • Github对公开仓库提供的这类服务是无限制的,但是对私有仓库是受限的,每个月提供免费的时间额度和存储额度,超额需要付费。
  • 如果在仓库中触发的一个工作流运行失败了,Github可能通过邮件通知。

一个典型的工作流(workflow)包含:

  • 触发工作流的一个或多个事件
  • 一个或多个作业(job),每个作业都将在运行器机器上执行并运行一系列的一个或多个步骤(step),每个步骤都可以运行指定的脚本或操作(action)

触发工作流的事件通常是:

  • Git仓库中发生的事件,例如推送到默认分支时、创建版本时
  • 定时触发或手动触发
  • 其他Github支持的触发行为

工作流的配置文件是yaml格式文件,存储在.github/workflows/文件夹中,语法规则如下

示例如下

learn-github-actions.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
name: learn-github-actions
run-name: ${{ github.actor }} is learning GitHub Actions
on: [push]
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install -g bats
- run: bats -v

这个工作流会在代码推送事件发生时自动触发。它定义了一个名为 "check-bats-version" 的任务,将在最新的 Ubuntu 环境上运行,具体行为依次为:

  • Checkout:将代码从你的仓库检出到运行环境中。
  • Setup Node:指定 Node.js 的版本为20。
  • Install Bats:使用 npm 全局安装 Bats,它是一个用于测试 Bash 脚本的测试框架。
  • Run Bats:最后执行 bats -v 命令。这个命令用于显示系统中安装的 Bats 版本。

触发工作流

常见的工作流触发逻辑是推送到main或者release,以及合并请求时触发,例如

1
2
3
4
5
on:
push:
branches: [ main, release/** ]
pull_request:
branches: [ main, release/** ]

有时我们只需要在某些路径或文件发生更改时,才触发工作流,例如

1
2
3
4
5
6
on:
push:
branches:
- 'main'
paths:
- 'src/utils/**'

这里同时使用了 branches 筛选器和 paths 筛选器,只在这两个筛选器都满足条件时触发工作流。

有时我们只需要在推送形如v1.**的标签时,触发工作流,例如

1
2
3
4
on:
push:
tags:
- v1.**

有时我们只是进行文档的更新,可以使用下面的格式来避免对应的修改触发测试工作流

1
2
3
4
5
6
on:
push:
paths-ignore:
- 'doc/**'
- 'docs/**'
- '**.md'

Cpp 项目

CMake跨平台工作流

下面是Github官方推荐的,针对跨平台CMake项目,在最新系统的三大编译器环境中进行编译测试的工作流配置 (基于系统默认的编译器版本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
name: CMake on multiple platforms

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ${{ matrix.os }}

strategy:
fail-fast: false

matrix:
os: [ubuntu-latest, windows-latest]
build_type: [Release]
c_compiler: [gcc, clang, cl]
include:
- os: windows-latest
c_compiler: cl
cpp_compiler: cl
- os: ubuntu-latest
c_compiler: gcc
cpp_compiler: g++
- os: ubuntu-latest
c_compiler: clang
cpp_compiler: clang++
exclude:
- os: windows-latest
c_compiler: gcc
- os: windows-latest
c_compiler: clang
- os: ubuntu-latest
c_compiler: cl

steps:
- uses: actions/checkout@v3

- name: Set reusable strings
id: strings
shell: bash
run: |
echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT"

- name: Configure CMake
run: >
cmake -B ${{ steps.strings.outputs.build-output-dir }}
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }}
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }}
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
-S ${{ github.workspace }}

- name: Build
run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }}

- name: Test
working-directory: ${{ steps.strings.outputs.build-output-dir }}
run: ctest --build-config ${{ matrix.build_type }}

上面的就是默认提供的配置文件,其实啥也不用改,只是清理了注释,当然也可以修改一下触发条件。 这个配置模板使用了矩阵,看起来比较高级,但是缺点是并没有指定具体的编译器版本,始终使用的是系统默认版本,这也意味着不能使用C++20的部分特性。

CMake跨平台工作流(最新版)

下面提供的是基于最新版编译器(VS2022gcc13clang18)的测试工作流(已经足够支持C++20的基础使用)

  • pipeline-ci.yml,依次调用了三个具体的测试工作流
  • bvt-msvc14.yml
  • bvt-gcc13.yml
  • bvt-clang18.yml

配置文件是参考微软的proxy库的,将编译器版本修改为最新版。 这里 pipeline-ci 指持续集成(Continuous Integration)中的一个流水线(Pipeline),bvt 指基本验证测试(Basic Verification Test)。

配置文件 pipeline-ci.yml 具体如下

pipeline-ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
name: Test-CI

on:
push:
branches: [ "main" ]
paths:
- 'src/**'
- 'test/**'
pull_request:
branches: [ "main" ]
paths:
- 'src/**'
- 'test/**'

env:
BUILD_TYPE: Release

jobs:
run-bvt-gcc13:
uses: ./.github/workflows/bvt-gcc13.yml
name: run bvt with g++ 13

run-bvt-clang15:
uses: ./.github/workflows/bvt-clang18.yml
name: run bvt with clang 18

run-bvt-msvc14:
uses: ./.github/workflows/bvt-msvc14.yml
name: run bvt with msvc14 (vs2022)

三个工作流是在不同编译器和环境下的基本测试,除了运行环境不同,以及预先下载配置指定的编译器,核心步骤都是一样的:生成,编译,进入构建目录,执行测试。

1
2
3
4
5
6
7
8
9
- name: build with cmake
run: |
cmake . -B build
cmake --build ./build -j8

- name: run tests
run: |
cd ./build
ctest -j8

需要注意的是对于MSVC,在ctest时需要明确构建类型,添加-C Release选项。

完整的工作流配置文件依次为

bvt-msvc14.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
name: bvt-msvc14

on:
workflow_call:
inputs:
branch:
type: string
required: false

jobs:
bvt-msvc14:
runs-on: windows-2022
steps:
- uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}

- name: build with cmake
run: |
cmake . -B build
cmake --build ./build -j8

- name: run tests
run: |
cd ./build
ctest -j8 -C Release
bvt-gcc13.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
name: bvt-gcc13

on:
workflow_call:
inputs:
branch:
type: string
required: false

jobs:
bvt-gcc13:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}

- name: install gcc 13
run: |
sudo apt update
sudo apt install -y gcc-13 g++-13
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13

- name: check compiler version
run: g++ --version

- name: build with cmake
run: |
cmake . -B build
cmake --build ./build -j8

- name: run tests
run: |
cd ./build
ctest -j8
bvt-clang18.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
name: bvt-clang18

on:
workflow_call:
inputs:
branch:
type: string
required: false

jobs:
bvt-clang18:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
ref: ${{ inputs.branch }}

- name: install clang 18
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-add-repository "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" -y
sudo apt update
sudo apt install -y clang-18
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/clang-18 18
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/clang++-18 18

- name: check compiler version
run: g++ --version

- name: build with cmake
run: |
cmake . -B build
cmake --build ./build -j8

- name: run tests
run: |
cd ./build
ctest -j8

为了复用工作流,可以将上面的文件全部存放在某一个公开仓库中(fenglielie/cmakezero.git),然后在其它仓库就可以直接调用它,只需要写一个test-ci.yml,例如

test-ci.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name: Test-CI

on:
push:
branches:
- 'main'
paths:
- 'src/utils/**'
- 'test/utils/**'

env:
BUILD_TYPE: Release

jobs:
run-bvt-gcc13:
uses: fenglielie/cmakezero/.github/workflows/bvt-gcc13.yml@main
name: run bvt with g++ 13

run-bvt-clang15:
uses: fenglielie/cmakezero/.github/workflows/bvt-clang18.yml@main
name: run bvt with clang 18

run-bvt-msvc14:
uses: fenglielie/cmakezero/.github/workflows/bvt-msvc14.yml@main
name: run bvt with msvc14 (vs2022)

LaTeX 项目

编译测试

如果只需要保证LaTex项目可以顺利编译,可以参考下面的配置模板,这里使用XeLaTeX编译main.tex,并且设置工作目录为./latex/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
name: Build LaTeX document

on:
push:
branches: [ main, release/** ]
pull_request:
branches: [ main, release/** ]

jobs:
build_release_latex:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Compile LaTeX document
uses: xu-cheng/latex-action@v2
with:
working_directory: ./latex
root_file: main.tex
args: -pdf -xelatex -file-line-error -halt-on-error -interaction=nonstopmode

编译测试与发布

如果对LaTeX项目有更多的需求:

  • 将编译生成的PDF文件保留在Git仓库的指定分支(gh_actions_builds)中
  • 将编译生成的PDF文件添加到Release中

配置示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
name: Build and Release LaTeX document

on:
push:
branches: [ main ]
tags:
- 'v*'
pull_request:
branches: [ main ]

workflow_dispatch:

jobs:
build_release_latex:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Compile LaTeX document
uses: xu-cheng/latex-action@v2
with:
root_file: main.tex
args: -pdf -xelatex -file-line-error -halt-on-error -interaction=nonstopmode

- name: Stash PDF
run: |
mv main.pdf $HOME # cache the file

- name: Create Branch
uses: peterjgrainger/action-create-branch@v2.0.1
env:
GITHUB_TOKEN: ${{ secrets.GH_LATEX_TEST_TOKEN }}
with:
branch: gh_actions_builds

- name: Checkout gh_actions_builds Branch
uses: actions/checkout@v3
with:
ref: gh_actions_builds

- name: Commit PDF
run: |
git config --local user.email "fenglielie@gmail.com"
git config --local user.name "fenglielie"
mv $HOME/main.pdf $(pwd) # bring it back
git add -f main.pdf
git commit -m "Updated by GitHub Action Automatically"

- name: Push PDF
uses: ad-m/github-push-action@master
with:
branch: gh_actions_builds
force: false
github_token: ${{ secrets.GH_LATEX_TEST_TOKEN }}

- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: main.pdf
env:
GITHUB_TOKEN: ${{ secrets.GH_LATEX_TEST_TOKEN }}

注意:由于需要提交,这里需要配置用户名和邮箱,创建和切换分支,并且需要在Github中设置并提供一个GITHUB_TOKEN,否则在虚拟环境中默认是没有权限对仓库进行任何修改操作的。