xref: /src/sys/contrib/openzfs/.github/workflows/scripts/generate-ci-type.py (revision 8ac904ce090b1c2e355da8aa122ca2252183f4e1)
1#!/usr/bin/env python3
2
3"""
4Determine the CI type based on the change list and commit message.
5
6Prints "quick" if (explicity required by user):
7- the *last* commit message contains 'ZFS-CI-Type: quick'
8or 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|linux|freebsd)'
11
12Otherwise prints "full".
13"""
14
15import sys
16import subprocess
17import re
18
19"""
20Patterns of files that are not considered to trigger full CI.
21Note: not using pathlib.Path.match() because it does not support '**'
22"""
23FULL_RUN_IGNORE_REGEX = list(map(re.compile, [
24    r'.*\.md',
25    r'.*\.gitignore'
26]))
27
28"""
29Patterns of files that are considered to trigger full CI.
30"""
31FULL_RUN_REGEX = list(map(re.compile, [
32    r'\.github/workflows/scripts/.*',
33    r'cmd.*',
34    r'configs/.*',
35    r'META',
36    r'.*\.am',
37    r'.*\.m4',
38    r'autogen\.sh',
39    r'configure\.ac',
40    r'copy-builtin',
41    r'contrib',
42    r'etc',
43    r'include',
44    r'lib/.*',
45    r'module/.*',
46    r'scripts/.*',
47    r'tests/.*',
48    r'udev/.*'
49]))
50
51if __name__ == '__main__':
52
53    prog = sys.argv[0]
54
55    if len(sys.argv) != 3:
56        print(f'Usage: {prog} <head_ref> <base_ref>')
57        sys.exit(1)
58
59    head, base = sys.argv[1:3]
60
61    def output_type(type, reason):
62        print(f'{prog}: will run {type} CI: {reason}', file=sys.stderr)
63        print(type)
64        sys.exit(0)
65
66    # check last (HEAD) commit message
67    last_commit_message_raw = subprocess.run([
68        'git', 'show', '-s', '--format=%B', head
69    ], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
70
71    for line in last_commit_message_raw.stdout.decode().splitlines():
72        if line.strip().lower() == 'zfs-ci-type: quick':
73            output_type('quick', f'requested by HEAD commit {head}')
74
75    # check all commit messages
76    all_commit_message_raw = subprocess.run([
77        'git', 'show', '-s',
78        '--format=ZFS-CI-Commit: %H%n%B', f'{head}...{base}'
79    ], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
80    all_commit_message = all_commit_message_raw.stdout.decode().splitlines()
81
82    commit_ref = head
83    for line in all_commit_message:
84        if line.startswith('ZFS-CI-Commit:'):
85            commit_ref = line.lstrip('ZFS-CI-Commit:').rstrip()
86        if line.strip().lower() == 'zfs-ci-type: freebsd':
87            output_type('freebsd', f'requested by commit {commit_ref}')
88        if line.strip().lower() == 'zfs-ci-type: linux':
89            output_type('linux', f'requested by commit {commit_ref}')
90        if line.strip().lower() == 'zfs-ci-type: full':
91            output_type('full', f'requested by commit {commit_ref}')
92
93    # check changed files
94    changed_files_raw = subprocess.run([
95        'git', 'diff', '--name-only', head, base
96    ], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97    changed_files = changed_files_raw.stdout.decode().splitlines()
98
99    for f in changed_files:
100        for r in FULL_RUN_IGNORE_REGEX:
101            if r.match(f):
102                break
103        else:
104            for r in FULL_RUN_REGEX:
105                if r.match(f):
106                    output_type(
107                        'full',
108                        f'changed file "{f}" matches pattern "{r.pattern}"'
109                        )
110
111    # catch-all
112    output_type('quick', 'no changed file matches full CI patterns')
113