diff options
-rwxr-xr-x | scripts/b4-wrapper-poky.py | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/scripts/b4-wrapper-poky.py b/scripts/b4-wrapper-poky.py new file mode 100755 index 0000000000..30a7b95ff3 --- /dev/null +++ b/scripts/b4-wrapper-poky.py | |||
@@ -0,0 +1,178 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | # | ||
3 | # Copyright OpenEmbedded Contributors | ||
4 | # | ||
5 | # SPDX-License-Identifier: MIT | ||
6 | # | ||
7 | # This script is to be called by b4: | ||
8 | # - through the b4.prep-perpatch-check-cmd with "prep-perpatch-check-cmd" as | ||
9 | # first argument, | ||
10 | # - through b4.send-auto-cc-cmd with "send-auto-cc-cmd" as first argument, | ||
11 | # - through b4.send-auto-to-cmd with "send-auto-to-cmd" as first argument, | ||
12 | # | ||
13 | # When prep-perpatch-check-cmd is passsed: | ||
14 | # | ||
15 | # This checks that a patch makes changes to at most one project in the poky | ||
16 | # combo repo (that is, out of yocto-docs, bitbake, openembedded-core combined | ||
17 | # into poky and the poky-specific files). | ||
18 | # | ||
19 | # Printing something to stdout in this file will result in b4 prep --check fail | ||
20 | # for the currently parsed patch. | ||
21 | # | ||
22 | # It checks that all patches in the series make changes to at most one project. | ||
23 | # | ||
24 | # When send-auto-cc-cmd is passed: | ||
25 | # | ||
26 | # This returns the list of Cc recipients for a patch. | ||
27 | # | ||
28 | # When send-auto-to-cmd is passed: | ||
29 | # | ||
30 | # This returns the list of To recipients for a patch. | ||
31 | # | ||
32 | # This script takes as stdin a patch. | ||
33 | |||
34 | import pathlib | ||
35 | import re | ||
36 | import shutil | ||
37 | import subprocess | ||
38 | import sys | ||
39 | |||
40 | cmd = sys.argv[1] | ||
41 | |||
42 | patch = sys.stdin.readlines() | ||
43 | |||
44 | # Subject field is used to identify the last patch as this script is called for | ||
45 | # each patch. We edit the same file in a series by using the References field | ||
46 | # unique identifier to check which projects are modified by earlier patches in | ||
47 | # the series. To avoid cluttering the disk, the last patch in the list removes | ||
48 | # that shared file. | ||
49 | re_subject = re.compile(r'^Subject:.*\[.*PATCH.*\s(\d+)/\1') | ||
50 | re_ref = re.compile(r'^References: <(.*)>$') | ||
51 | |||
52 | subject = None | ||
53 | ref = None | ||
54 | |||
55 | if not shutil.which("lsdiff"): | ||
56 | print("lsdiff missing from host, please install patchutils") | ||
57 | sys.exit(-1) | ||
58 | |||
59 | try: | ||
60 | one_patch_series = False | ||
61 | for line in patch: | ||
62 | subject = re_subject.match(line) | ||
63 | if subject: | ||
64 | # Handle [PATCH 1/1] | ||
65 | if subject.group(1) == 1: | ||
66 | one_patch_series = True | ||
67 | break | ||
68 | if re.match(r'^Subject: .*\[.*PATCH[^/]*\]', line): | ||
69 | # Single patch is named [PATCH] but if there are prefix, it could be | ||
70 | # [PATCH prefix], so handle everything that doesn't have a / | ||
71 | # character which is used as separator between current patch number | ||
72 | # and total patch number | ||
73 | one_patch_series = True | ||
74 | break | ||
75 | |||
76 | if cmd == "prep-perpatch-check-cmd" and not one_patch_series: | ||
77 | for line in patch: | ||
78 | ref = re_ref.match(line) | ||
79 | if ref: | ||
80 | break | ||
81 | |||
82 | if not ref: | ||
83 | print("Failed to find ref to cover letter (References:)...") | ||
84 | sys.exit(-2) | ||
85 | |||
86 | ref = ref.group(1) | ||
87 | series_check = pathlib.Path(f".tmp-{ref}") | ||
88 | |||
89 | patch = "".join(patch) | ||
90 | |||
91 | if cmd == "send-auto-cc-cmd": | ||
92 | # Patches to BitBake documentation should also go to yocto-docs mailing list | ||
93 | project_paths = { | ||
94 | "yocto-docs": ["bitbake/doc/*"], | ||
95 | } | ||
96 | else: | ||
97 | project_paths = { | ||
98 | "bitbake": ["bitbake/*"], | ||
99 | "yocto-docs": ["documentation/*"], | ||
100 | "poky": [ | ||
101 | "meta-poky/*", | ||
102 | "meta-yocto-bsp/*", | ||
103 | "README.hardware.md", | ||
104 | "README.poky.md", | ||
105 | ], | ||
106 | } | ||
107 | |||
108 | # List of projects touched by this patch | ||
109 | projs = [] | ||
110 | |||
111 | # Any file not matched by any path in project_paths means it is from | ||
112 | # OE-Core. | ||
113 | # When matching some path in project_paths, remove the matched files from | ||
114 | # that list. | ||
115 | files_left = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"], | ||
116 | input=patch, text=True) | ||
117 | files_left = set(files_left) | ||
118 | |||
119 | for proj, proj_paths in project_paths.items(): | ||
120 | lsdiff_args = [f"--include={path}" for path in proj_paths] | ||
121 | files = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"] + lsdiff_args, | ||
122 | input=patch, text=True) | ||
123 | if len(files): | ||
124 | files_left = files_left - set(files) | ||
125 | projs.append(proj) | ||
126 | continue | ||
127 | |||
128 | # Handle patches made with --no-prefix | ||
129 | files = subprocess.check_output(["lsdiff"] + lsdiff_args, | ||
130 | input=patch, text=True) | ||
131 | if len(files): | ||
132 | files_left = files_left - set(files) | ||
133 | projs.append(proj) | ||
134 | |||
135 | # Catch-all for everything not poky-specific or in bitbake/yocto-docs | ||
136 | if len(files_left) and cmd != "send-auto-cc-cmd": | ||
137 | projs.append("openembedded-core") | ||
138 | |||
139 | if cmd == "prep-perpatch-check-cmd": | ||
140 | if len(projs) > 1: | ||
141 | print(f"Diff spans more than one project ({', '.join(sorted(projs))}), split into multiple commits...") | ||
142 | sys.exit(-3) | ||
143 | |||
144 | # No need to check other patches in the series as there aren't any | ||
145 | if one_patch_series: | ||
146 | sys.exit(0) | ||
147 | |||
148 | # This should be replaced once b4 supports prep-perseries-check-cmd (or something similar) | ||
149 | |||
150 | if series_check.exists(): | ||
151 | # NOT race-free if b4 decides to parallelize prep-perpatch-check-cmd | ||
152 | series_projs = series_check.read_text().split('\n') | ||
153 | else: | ||
154 | series_projs = [] | ||
155 | |||
156 | series_projs += projs | ||
157 | uniq_series_projs = set(series_projs) | ||
158 | # NOT race-free, if b4 decides to parallelize prep-perpatch-check-cmd | ||
159 | series_check.write_text('\n'.join(uniq_series_projs)) | ||
160 | |||
161 | if len(uniq_series_projs) > 1: | ||
162 | print(f"Series spans more than one project ({', '.join(sorted(uniq_series_projs))}), split into multiple series...") | ||
163 | sys.exit(-4) | ||
164 | else: # send-auto-cc-cmd / send-auto-to-cmd | ||
165 | ml_projs = { | ||
166 | "bitbake": "bitbake-devel@lists.openembedded.org", | ||
167 | "yocto-docs": "docs@lists.yoctoproject.org", | ||
168 | "poky": "poky@lists.yoctoproject.org", | ||
169 | "openembedded-core": "openembedded-core@lists.openembedded.org", | ||
170 | } | ||
171 | |||
172 | print("\n".join([ml_projs[ml] for ml in projs])) | ||
173 | |||
174 | sys.exit(0) | ||
175 | finally: | ||
176 | # Last patch in the series, cleanup tmp file | ||
177 | if subject and ref and series_check.exists(): | ||
178 | series_check.unlink() | ||