blob: eb73456659cf9a5251ae8a5a608df1fdf105d906 [file] [log] [blame]
Okash Khawaja671bed22022-11-10 15:51:19 +00001#!/usr/bin/env python3
2#
3# Copyright (c) 2022 Google LLC. All rights reserved.
4#
5# SPDX-License-Identifier: BSD-3-Clause
6
7# quick hacky script to check patches if they are candidates for lts. it checks
8# only the non-merge commits.
9
10import pkg_resources
11import os
12import git
13import re
Okash Khawaja68751502023-02-03 18:53:14 +000014import sys
Okash Khawaja671bed22022-11-10 15:51:19 +000015import argparse
16from io import StringIO
17pkg_resources.require("unidiff>=0.7.4")
18from unidiff import PatchSet
19
20global_debug = False
21def debug_print(*args, **kwargs):
22 global global_var
23 if global_debug:
24 print(*args, **kwargs)
25
26def contains_re(pf, tok):
27 for hnk in pf:
28 for ln in hnk:
29 if ln.is_context:
30 continue
31 # here means the line is either added or removed
32 txt = ln.value.strip()
33 if tok.search(txt) is not None:
34 return True
35
36 return False
37
38def process_ps(ps):
39 score = 0
40
41 cpu_tok = re.compile(CPU_PATH_TOKEN)
42 doc_tok = re.compile(DOC_PATH_TOKEN)
43
44 for pf in ps:
45 if pf.is_binary_file or not pf.is_modified_file:
46 continue
47 if cpu_tok.search(pf.path) is not None:
48 debug_print("* change found in cpu path:", pf.path);
49 cpu_tok = re.compile(CPU_ERRATA_TOKEN)
50 if contains_re(pf, cpu_tok):
51 score = score + 1
52 debug_print(" found", CPU_ERRATA_TOKEN)
53
54 if doc_tok.search(pf.path) is not None:
55 debug_print("* change found in macros doc path:", pf.path);
56 doc_tok = re.compile(DOC_ERRATA_TOKEN)
57 if contains_re(pf, doc_tok):
58 score = score + 1
59 debug_print(" found", DOC_ERRATA_TOKEN)
60
61 return score
62
63SUBJECT_TOKENS = r'fix\(cpus\)|revert\(cpus\)|fix\(errata\)|\(security\)'
64CPU_PATH_TOKEN = r'lib/cpus/aarch(32|64)/.*\.S'
65CPU_ERRATA_TOKEN = r'^report_errata ERRATA_'
66DOC_PATH_TOKEN = r'docs/design/cpu-specific-build-macros.rst'
67DOC_ERRATA_TOKEN = r'^^-\s*``ERRATA_'
Okash Khawaja66a3a7e2023-01-24 11:06:16 +000068# REBASE_DEPTH is number of commits from tip of integration branch that we need
69# to check to find the commit that the current patch set is based on
70REBASE_DEPTH = 50
71# MAX_PATCHSET_DEPTH is the maximum number of patches that we expect in the current
72# patch set. for each commit in the patch set we will look at past REBASE_DEPTH commits
73# of integration branch. if there is a match we'd know the current patch set was based
74# off of that matching commit. This is not necessarily the optimal method but I'm not
75# familiar with gerrit API. If there is a way to do this better we should implement that.
76MAX_PATCHSET_DEPTH = 50
77CHECK_AGAINST = 'integration'
78TO_CHECK = 'to_check'
79
Okash Khawaja671bed22022-11-10 15:51:19 +000080
81## TODO: for case like 921081049ec3 where we need to refactor first for security
82# patch to be applied then we should:
83# 1. find the security patch
84# 2. from that patch find CVE number if any
85# 3. look for all patches that contain that CVE number in commit message
86
87## TODO: similar to errata macros and rst file additions, we have CVE macros and rst file
88# additions. so we can use similar logic for that.
89
90## TODO: for security we should look for CVE numbed regex match and if found flag it
91def main():
92 parser = argparse.ArgumentParser(prog="lts-triage.py", description="check patches for LTS candidacy")
93 parser.add_argument("--repo", required=True, help="path to tf-a git repo")
Okash Khawaja68751502023-02-03 18:53:14 +000094 parser.add_argument("--email_path", required=True, help="path including the filename for email file")
Okash Khawaja671bed22022-11-10 15:51:19 +000095 parser.add_argument("--debug", help="print debug logs", action="store_true")
96
97 args = parser.parse_args()
98 global global_debug
99 global_debug = args.debug
100
Okash Khawaja68751502023-02-03 18:53:14 +0000101 file_str = "Below is an interesting patchset. Patches are listed in format {Subject}: {Score}.\n\n"
102 at_least_one_match = False
103
Okash Khawaja671bed22022-11-10 15:51:19 +0000104 repo = git.Repo(args.repo)
Okash Khawaja671bed22022-11-10 15:51:19 +0000105
Okash Khawaja66a3a7e2023-01-24 11:06:16 +0000106 # collect the integration hashes in a list
107 rebase_hashes = []
108 for cmt in repo.iter_commits(CHECK_AGAINST):
109 rebase_hashes.append(cmt.hexsha)
110 if len(rebase_hashes) == REBASE_DEPTH:
111 break
112
113 cnt = MAX_PATCHSET_DEPTH
114 for cmt in repo.iter_commits(TO_CHECK):
Okash Khawaja671bed22022-11-10 15:51:19 +0000115 score = 0
116
Okash Khawaja66a3a7e2023-01-24 11:06:16 +0000117 # if we find a same commit hash among the ones we collected from integration branch
118 # then we have seen all the new patches in this patch set, so we should exit.
119 if cmt.hexsha in rebase_hashes:
Okash Khawaja68751502023-02-03 18:53:14 +0000120 print("## stopping because found sha1 common between the two branches: ", cmt.hexsha)
Okash Khawaja66a3a7e2023-01-24 11:06:16 +0000121 break;
122
Okash Khawaja671bed22022-11-10 15:51:19 +0000123 # don't process merge commits
124 if len(cmt.parents) > 1:
125 continue
126
127 tok = re.compile(SUBJECT_TOKENS)
128 if tok.search(cmt.summary) is not None:
129 debug_print("## subject match")
130 score = score + 1
131
132 diff_text = repo.git.diff(cmt.hexsha + "~1", cmt.hexsha, ignore_blank_lines=True, ignore_space_at_eol=True)
133 ps = PatchSet(StringIO(diff_text))
134 debug_print("# score before process_ps:", score)
135 score = score + process_ps(ps)
136 debug_print("# score after process_ps:", score)
137
Okash Khawajaa5909ac2023-02-03 19:22:34 +0000138 ln = f"{cmt.summary}: {score}"
Okash Khawajaa5909ac2023-02-03 19:22:34 +0000139 print(ln)
Okash Khawaja68751502023-02-03 18:53:14 +0000140
141 if score > 0:
Okash Khawaja00859482023-02-20 17:42:02 +0000142 file_str += ln + "\n"
Okash Khawaja68751502023-02-03 18:53:14 +0000143 at_least_one_match = True
144
Okash Khawaja671bed22022-11-10 15:51:19 +0000145 cnt = cnt - 1
146 if cnt == 0:
147 break
148
Okash Khawaja68751502023-02-03 18:53:14 +0000149 if at_least_one_match == True:
150 try:
151 with open(args.email_path, "x") as f:
152 f.write(file_str)
153 except:
154 print("\n\nERROR: Couldn't open email file due to error: ", sys.exc_info()[0])
155
Okash Khawaja671bed22022-11-10 15:51:19 +0000156if __name__ == '__main__':
157 main()