summaryrefslogtreecommitdiffstats
path: root/scripts/b4-wrapper-poky.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/b4-wrapper-poky.py')
-rwxr-xr-xscripts/b4-wrapper-poky.py185
1 files changed, 185 insertions, 0 deletions
diff --git a/scripts/b4-wrapper-poky.py b/scripts/b4-wrapper-poky.py
new file mode 100755
index 0000000000..f1170db06b
--- /dev/null
+++ b/scripts/b4-wrapper-poky.py
@@ -0,0 +1,185 @@
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
34import pathlib
35import re
36import shutil
37import subprocess
38import sys
39
40cmd = sys.argv[1]
41
42patch = 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.
49re_subject = re.compile(r'^Subject:.*\[.*PATCH.*\s(\d+)/\1')
50re_ref = re.compile(r'^References: <(.*)>$')
51
52subject = None
53ref = None
54
55if not shutil.which("lsdiff"):
56 print("lsdiff missing from host, please install patchutils", file=sys.stderr)
57 sys.exit(-1)
58
59try:
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:)...", file=sys.stderr)
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 # scripts/b4-wrapper-poky.py is only run by b4 when in poky
106 # git repo. With that limitation, changes made to .b4-config
107 # can only be for poky's and not OE-Core's as only poky's is
108 # stored in poky git repo.
109 ".b4-config",
110 ],
111 }
112
113 # List of projects touched by this patch
114 projs = []
115
116 # Any file not matched by any path in project_paths means it is from
117 # OE-Core.
118 # When matching some path in project_paths, remove the matched files from
119 # that list.
120 files_left = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"],
121 input=patch, text=True)
122 files_left = set(files_left)
123
124 for proj, proj_paths in project_paths.items():
125 lsdiff_args = [f"--include={path}" for path in proj_paths]
126 files = subprocess.check_output(["lsdiff", "--strip-match=1", "--strip=1"] + lsdiff_args,
127 input=patch, text=True)
128 if len(files):
129 files_left = files_left - set(files)
130 projs.append(proj)
131 continue
132
133 # Handle patches made with --no-prefix
134 files = subprocess.check_output(["lsdiff"] + lsdiff_args,
135 input=patch, text=True)
136 if len(files):
137 files_left = files_left - set(files)
138 projs.append(proj)
139
140 # Catch-all for everything not poky-specific or in bitbake/yocto-docs
141 if len(files_left) and cmd != "send-auto-cc-cmd":
142 projs.append("openembedded-core")
143
144 if cmd == "prep-perpatch-check-cmd":
145 if len(projs) > 1:
146 print(f"Diff spans more than one project ({', '.join(sorted(projs))}), split into multiple commits...",
147 file=sys.stderr)
148 sys.exit(-3)
149
150 # No need to check other patches in the series as there aren't any
151 if one_patch_series:
152 sys.exit(0)
153
154 # This should be replaced once b4 supports prep-perseries-check-cmd (or something similar)
155
156 if series_check.exists():
157 # NOT race-free if b4 decides to parallelize prep-perpatch-check-cmd
158 series_projs = series_check.read_text().split('\n')
159 else:
160 series_projs = []
161
162 series_projs += projs
163 uniq_series_projs = set(series_projs)
164 # NOT race-free, if b4 decides to parallelize prep-perpatch-check-cmd
165 series_check.write_text('\n'.join(uniq_series_projs))
166
167 if len(uniq_series_projs) > 1:
168 print(f"Series spans more than one project ({', '.join(sorted(uniq_series_projs))}), split into multiple series...",
169 file=sys.stderr)
170 sys.exit(-4)
171 else: # send-auto-cc-cmd / send-auto-to-cmd
172 ml_projs = {
173 "bitbake": "bitbake-devel@lists.openembedded.org",
174 "yocto-docs": "docs@lists.yoctoproject.org",
175 "poky": "poky@lists.yoctoproject.org",
176 "openembedded-core": "openembedded-core@lists.openembedded.org",
177 }
178
179 print("\n".join([ml_projs[ml] for ml in projs]))
180
181 sys.exit(0)
182finally:
183 # Last patch in the series, cleanup tmp file
184 if subject and ref and series_check.exists():
185 series_check.unlink()