Skip to content

Commit ae04fc6

Browse files
committed
CI: run only sanity check on limited OSes for nonbehavioral changes
The commit uses heuristics to determine whether a PR is behavioral: It runs "quick" CI (i.e., only use sanity.run on fewer OSes) if (explicity required by user): - the *last* commit message contains a line 'ZFS-CI-Type: quick', or if (by heuristics): - the files changed are not in the list of specified directory, and - all commit messages does not contain 'ZFS-CI-Type: full'. It runs "full" CI otherwise. See: openzfs#16561 (comment) Signed-off-by: Shengqi Chen <[email protected]>
1 parent 832f66b commit ae04fc6

File tree

4 files changed

+150
-2
lines changed

4 files changed

+150
-2
lines changed

.github/CONTRIBUTING.md

+3
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ Any required reviews can then be finalized and the pull request merged.
147147
#### Tests and Benchmarks
148148
* Every pull request is tested using a GitHub Actions workflow on multiple platforms by running the [zfs-tests.sh and zloop.sh](
149149
https://openzfs.github.io/openzfs-docs/Developer%20Resources/Building%20ZFS.html#running-zloop-sh-and-zfs-tests-sh) test suites.
150+
`.github/workflows/scripts/generate-ci-type.py` is used to determine whether the pull request is nonbehavior, i.e., not introducing behavior changes of any code, configuration or tests. If so, the CI will run on fewer platforms and only essential sanity tests will run. You can always override this by adding `ZFS-CI-Type` line to your commit message:
151+
* If your last commit (or `HEAD` in git terms) contains a line `ZFS-CI-Type: quick`, quick mode is forced regardless of what files are changed.
152+
* Otherwise, if any commit in a PR contains a line `ZFS-CI-Type: full`, full mode is forced.
150153
* To verify your changes conform to the [style guidelines](
151154
https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#style-guides
152155
), please run `make checkstyle` and resolve any warnings.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Determine the CI type based on the change list and commit message.
5+
6+
Prints "quick" if (explicity required by user):
7+
- the *last* commit message contains 'ZFS-CI-Type: quick'
8+
or if (heuristics):
9+
- the files changed are not in the list of specified directories, and
10+
- all commit messages do not contain 'ZFS-CI-Type: full'
11+
12+
Otherwise prints "full".
13+
"""
14+
15+
import sys
16+
import subprocess
17+
import re
18+
19+
"""
20+
Patterns of files that are not considered to trigger full CI.
21+
Note: not using pathlib.Path.match() because it does not support '**'
22+
"""
23+
FULL_RUN_IGNORE_REGEX = list(map(re.compile, [
24+
r'.*\.md',
25+
r'.*\.gitignore'
26+
]))
27+
28+
"""
29+
Patterns of files that are considered to trigger full CI.
30+
"""
31+
FULL_RUN_REGEX = list(map(re.compile, [
32+
r'cmd.*',
33+
r'configs/.*',
34+
r'META',
35+
r'.*\.am',
36+
r'.*\.m4',
37+
r'autogen\.sh',
38+
r'configure\.ac',
39+
r'copy-builtin',
40+
r'contrib',
41+
r'etc',
42+
r'include',
43+
r'lib/.*',
44+
r'module/.*',
45+
r'scripts/.*',
46+
r'tests/.*',
47+
r'udev/.*'
48+
]))
49+
50+
if __name__ == '__main__':
51+
52+
prog = sys.argv[0]
53+
54+
if len(sys.argv) != 3:
55+
print(f'Usage: {prog} <head_ref> <base_ref>')
56+
sys.exit(1)
57+
58+
head, base = sys.argv[1:3]
59+
60+
def output_type(type, reason):
61+
print(f'{prog}: will run {type} CI: {reason}', file=sys.stderr)
62+
print(type)
63+
sys.exit(0)
64+
65+
# check last (HEAD) commit message
66+
last_commit_message_raw = subprocess.run([
67+
'git', 'show', '-s', '--format=%B', 'HEAD'
68+
], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
69+
70+
for line in last_commit_message_raw.stdout.decode().splitlines():
71+
if line.strip().lower() == 'zfs-ci-type: quick':
72+
output_type('quick', f'explicitly requested by HEAD commit {head}')
73+
74+
# check all commit messages
75+
all_commit_message_raw = subprocess.run([
76+
'git', 'show', '-s',
77+
'--format=ZFS-CI-Commit: %H%n%B', f'{head}...{base}'
78+
], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
79+
all_commit_message = all_commit_message_raw.stdout.decode().splitlines()
80+
81+
commit_ref = head
82+
for line in all_commit_message:
83+
if line.startswith('ZFS-CI-Commit:'):
84+
commit_ref = line.lstrip('ZFS-CI-Commit:').rstrip()
85+
if line.strip().lower() == 'zfs-ci-type: full':
86+
output_type('full', f'explicitly requested by commit {commit_ref}')
87+
88+
# check changed files
89+
changed_files_raw = subprocess.run([
90+
'git', 'diff', '--name-only', head, base
91+
], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
92+
changed_files = changed_files_raw.stdout.decode().splitlines()
93+
94+
for f in changed_files:
95+
for r in FULL_RUN_IGNORE_REGEX:
96+
if r.match(f):
97+
break
98+
else:
99+
for r in FULL_RUN_REGEX:
100+
if r.match(f):
101+
output_type(
102+
'full',
103+
f'changed file "{f}" matches pattern "{r.pattern}"'
104+
)
105+
106+
# catch-all
107+
output_type('quick', 'no changed file matches full CI patterns')

.github/workflows/scripts/qemu-6-tests.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ if [ -z ${1:-} ]; then
4848
for i in $(seq 1 $VMs); do
4949
IP="192.168.122.1$i"
5050
daemonize -c /var/tmp -p vm${i}.pid -o vm${i}log.txt -- \
51-
$SSH zfs@$IP $TESTS $OS $i $VMs
51+
$SSH zfs@$IP $TESTS $OS $i $VMs $CI_TYPE
5252
# handly line by line and add info prefix
5353
stdbuf -oL tail -fq vm${i}log.txt \
5454
| while read -r line; do prefix "$i" "$line"; done &
@@ -91,6 +91,9 @@ esac
9191
# run functional testings and save exitcode
9292
cd /var/tmp
9393
TAGS=$2/$3
94+
if [ "$4" == "quick" ]; then
95+
export RUNFILES="sanity.run"
96+
fi
9497
sudo dmesg -c > dmesg-prerun.txt
9598
mount > mount.txt
9699
df -h > df-prerun.txt

.github/workflows/zfs-qemu.yml

+36-1
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,48 @@ concurrency:
99
cancel-in-progress: true
1010

1111
jobs:
12+
test-config:
13+
name: Setup
14+
runs-on: ubuntu-24.04
15+
outputs:
16+
test_os: ${{ steps.os.outputs.os }}
17+
ci_type: ${{ steps.os.outputs.ci_type }}
18+
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
- name: Generate OS config and CI type
23+
id: os
24+
run: |
25+
FULL_OS='["almalinux8", "almalinux9", "centos-stream9", "debian11", "debian12", "fedora39", "fedora40", "freebsd13", "freebsd13r", "freebsd14", "freebsd14r", "ubuntu20", "ubuntu22", "ubuntu24"]'
26+
QUICK_OS='["almalinux8", "almalinux9", "debian12", "fedora40", "freebsd13", "freebsd14", "ubuntu24"]'
27+
# determine CI type when running on PR
28+
ci_type="full"
29+
if ${{ github.event_name == 'pull_request' }}; then
30+
head=${{ github.event.pull_request.head.sha }}
31+
base=${{ github.event.pull_request.base.sha }}
32+
ci_type=$(python3 .github/workflows/scripts/generate-ci-type.py $head $base)
33+
fi
34+
if [ "$ci_type" == "quick" ]; then
35+
os_selection="$QUICK_OS"
36+
else
37+
os_selection="$FULL_OS"
38+
fi
39+
os_json=$(echo ${os_selection} | jq -c)
40+
echo "os=$os_json" >> $GITHUB_OUTPUT
41+
echo "ci_type=$ci_type" >> $GITHUB_OUTPUT
42+
1243
qemu-vm:
1344
name: qemu-x86
45+
needs: [ test-config ]
1446
strategy:
1547
fail-fast: false
1648
matrix:
1749
# all:
1850
# os: [almalinux8, almalinux9, archlinux, centos-stream9, fedora39, fedora40, debian11, debian12, freebsd13, freebsd13r, freebsd14, freebsd14r, freebsd15, ubuntu20, ubuntu22, ubuntu24]
1951
# openzfs:
20-
os: [almalinux8, almalinux9, centos-stream9, debian11, debian12, fedora39, fedora40, freebsd13, freebsd13r, freebsd14, freebsd14r, ubuntu20, ubuntu22, ubuntu24]
52+
# os: [almalinux8, almalinux9, centos-stream9, debian11, debian12, fedora39, fedora40, freebsd13, freebsd13r, freebsd14, freebsd14r, ubuntu20, ubuntu22, ubuntu24]
53+
os: ${{ fromJson(needs.test-config.outputs.test_os) }}
2154
runs-on: ubuntu-24.04
2255
steps:
2356
- uses: actions/checkout@v4
@@ -67,6 +100,8 @@ jobs:
67100
- name: Run tests
68101
timeout-minutes: 270
69102
run: .github/workflows/scripts/qemu-6-tests.sh
103+
env:
104+
CI_TYPE: ${{ needs.test-config.outputs.ci_type }}
70105

71106
- name: Prepare artifacts
72107
if: always()

0 commit comments

Comments
 (0)