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