blob: 7467506dcbceeaec46b10fcb64f715230093aeaa [file] [log] [blame]
Simon Glass24725af2020-07-05 21:41:49 -06001# SPDX-License-Identifier: GPL-2.0+
2#
3# Copyright 2020 Google LLC
4#
5"""Handles the main control logic of patman
6
7This module provides various functions called by the main program to implement
8the features of patman.
9"""
10
Simon Glass3c0196f2025-04-29 07:21:58 -060011import re
Simon Glass3c0196f2025-04-29 07:21:58 -060012import traceback
Simon Glass24725af2020-07-05 21:41:49 -060013
Simon Glass3c0196f2025-04-29 07:21:58 -060014try:
15 from importlib import resources
16except ImportError:
17 # for Python 3.6
18 import importlib_resources as resources
19
Simon Glassba1b3b92025-02-09 14:26:00 -070020from u_boot_pylib import gitutil
Simon Glass131444f2023-02-23 18:18:04 -070021from u_boot_pylib import terminal
Simon Glass3c0196f2025-04-29 07:21:58 -060022from u_boot_pylib import tools
Simon Glass6dfb4dd2025-05-10 13:05:11 +020023from u_boot_pylib import tout
24from patman import cseries
25from patman import cser_helper
Simon Glass3c0196f2025-04-29 07:21:58 -060026from patman import patchstream
Simon Glass6dfb4dd2025-05-10 13:05:11 +020027from patman.patchwork import Patchwork
Simon Glassc0257982025-04-29 07:22:11 -060028from patman import send
Simon Glassaaaff532025-04-07 22:51:43 +120029from patman import settings
Simon Glass24725af2020-07-05 21:41:49 -060030
Maxim Cournoyer12f99fd2023-10-12 23:06:24 -040031
Simon Glass24725af2020-07-05 21:41:49 -060032def setup():
33 """Do required setup before doing anything"""
Simon Glass761648b2022-01-29 14:14:11 -070034 gitutil.setup()
Simon Glassaaaff532025-04-07 22:51:43 +120035 alias_fname = gitutil.get_alias_file()
36 if alias_fname:
37 settings.ReadGitAliases(alias_fname)
Simon Glass24725af2020-07-05 21:41:49 -060038
Maxim Cournoyer12f99fd2023-10-12 23:06:24 -040039
Simon Glassc0257982025-04-29 07:22:11 -060040def do_send(args):
Simon Glass24725af2020-07-05 21:41:49 -060041 """Create, check and send patches by email
42
43 Args:
Simon Glasseb101ac2020-07-05 21:41:53 -060044 args (argparse.Namespace): Arguments to patman
Simon Glass24725af2020-07-05 21:41:49 -060045 """
46 setup()
Simon Glassc0257982025-04-29 07:22:11 -060047 send.send(args)
Simon Glass2eb4da72020-07-05 21:41:51 -060048
Simon Glass3db916d2020-10-29 21:46:35 -060049
Simon Glass2112d072020-10-29 21:46:38 -060050def patchwork_status(branch, count, start, end, dest_branch, force,
Simon Glass802eeea2025-04-29 07:22:25 -060051 show_comments, url, single_thread=False):
Simon Glass3db916d2020-10-29 21:46:35 -060052 """Check the status of patches in patchwork
53
54 This finds the series in patchwork using the Series-link tag, checks for new
Simon Glass2112d072020-10-29 21:46:38 -060055 comments and review tags, displays then and creates a new branch with the
56 review tags.
Simon Glass3db916d2020-10-29 21:46:35 -060057
58 Args:
59 branch (str): Branch to create patches from (None = current)
60 count (int): Number of patches to produce, or -1 to produce patches for
61 the current branch back to the upstream commit
62 start (int): Start partch to use (0=first / top of branch)
63 end (int): End patch to use (0=last one in series, 1=one before that,
64 etc.)
Simon Glassd0a0a582020-10-29 21:46:36 -060065 dest_branch (str): Name of new branch to create with the updated tags
66 (None to not create a branch)
67 force (bool): With dest_branch, force overwriting an existing branch
Simon Glass2112d072020-10-29 21:46:38 -060068 show_comments (bool): True to display snippets from the comments
69 provided by reviewers
Simon Glass4acc93c2020-11-03 13:54:16 -070070 url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
71 This is ignored if the series provides a Series-patchwork-url tag.
Simon Glass3db916d2020-10-29 21:46:35 -060072
73 Raises:
74 ValueError: if the branch has no Series-link value
75 """
Simon Glass6dfb4dd2025-05-10 13:05:11 +020076 if not branch:
77 branch = gitutil.get_branch()
Simon Glass3db916d2020-10-29 21:46:35 -060078 if count == -1:
79 # Work out how many patches to send if we can
Simon Glass6dfb4dd2025-05-10 13:05:11 +020080 count = gitutil.count_commits_to_branch(branch) - start
Simon Glass3db916d2020-10-29 21:46:35 -060081
82 series = patchstream.get_metadata(branch, start, count - end)
83 warnings = 0
84 for cmt in series.commits:
85 if cmt.warn:
86 print('%d warnings for %s:' % (len(cmt.warn), cmt.hash))
87 for warn in cmt.warn:
88 print('\t', warn)
89 warnings += 1
90 print
91 if warnings:
92 raise ValueError('Please fix warnings before running status')
93 links = series.get('links')
94 if not links:
95 raise ValueError("Branch has no Series-links value")
96
Simon Glass6dfb4dd2025-05-10 13:05:11 +020097 _, version = cser_helper.split_name_version(branch)
98 link = series.get_link_for_version(version, links)
99 if not link:
100 raise ValueError('Series-links has no link for v{version}')
101 tout.debug(f"Link '{link}")
Simon Glass3db916d2020-10-29 21:46:35 -0600102
Simon Glass4acc93c2020-11-03 13:54:16 -0700103 # Allow the series to override the URL
104 if 'patchwork_url' in series:
105 url = series.patchwork_url
Simon Glass6dfb4dd2025-05-10 13:05:11 +0200106 pwork = Patchwork(url, single_thread=single_thread)
Simon Glass4acc93c2020-11-03 13:54:16 -0700107
Simon Glass3db916d2020-10-29 21:46:35 -0600108 # Import this here to avoid failing on other commands if the dependencies
109 # are not present
110 from patman import status
Simon Glass6dfb4dd2025-05-10 13:05:11 +0200111 pwork = Patchwork(url)
112 status.check_and_show_status(series, link, branch, dest_branch, force,
113 show_comments, False, pwork)
Simon Glass3c0196f2025-04-29 07:21:58 -0600114
115
Simon Glass5e52d172025-05-10 13:05:15 +0200116def do_series(args, test_db=None, pwork=None, cser=None):
117 """Process a series subcommand
118
119 Args:
120 args (Namespace): Arguments to process
121 test_db (str or None): Directory containing the test database, None to
122 use the normal one
123 pwork (Patchwork): Patchwork object to use, None to create one if
124 needed
125 cser (Cseries): Cseries object to use, None to create one
126 """
127 if not cser:
128 cser = cseries.Cseries(test_db)
129 needs_patchwork = [
130 'autolink', 'autolink-all', 'open', 'send', 'status', 'gather',
131 'gather-all'
132 ]
133 try:
134 cser.open_database()
135 if args.subcmd in needs_patchwork:
136 if not pwork:
137 pwork = Patchwork(args.patchwork_url)
138 proj = cser.project_get()
139 if not proj:
140 raise ValueError(
141 "Please set project ID with 'patman patchwork set-project'")
142 _, proj_id, link_name = cser.project_get()
143 pwork.project_set(proj_id, link_name)
144 elif pwork and pwork is not True:
145 raise ValueError(
146 f"Internal error: command '{args.subcmd}' should not have patchwork")
147 if args.subcmd == 'add':
148 cser.add(args.series, args.desc, mark=args.mark,
149 allow_unmarked=args.allow_unmarked, end=args.upstream,
150 dry_run=args.dry_run)
151 elif args.subcmd == 'archive':
152 cser.archive(args.series)
153 elif args.subcmd == 'autolink':
154 cser.link_auto(pwork, args.series, args.version, args.update,
155 args.autolink_wait)
156 elif args.subcmd == 'autolink-all':
157 cser.link_auto_all(pwork, update_commit=args.update,
158 link_all_versions=args.link_all_versions,
159 replace_existing=args.replace_existing,
160 dry_run=args.dry_run, show_summary=True)
161 elif args.subcmd == 'dec':
162 cser.decrement(args.series, args.dry_run)
163 elif args.subcmd == 'gather':
164 cser.gather(pwork, args.series, args.version, args.show_comments,
165 args.show_cover_comments, args.gather_tags,
166 dry_run=args.dry_run)
167 elif args.subcmd == 'gather-all':
168 cser.gather_all(
169 pwork, args.show_comments, args.show_cover_comments,
170 args.gather_all_versions, args.gather_tags, args.dry_run)
171 elif args.subcmd == 'get-link':
172 link = cser.link_get(args.series, args.version)
173 print(link)
174 elif args.subcmd == 'inc':
175 cser.increment(args.series, args.dry_run)
176 elif args.subcmd == 'ls':
177 cser.series_list()
178 elif args.subcmd == 'open':
179 cser.open(pwork, args.series, args.version)
180 elif args.subcmd == 'mark':
181 cser.mark(args.series, args.allow_marked, dry_run=args.dry_run)
182 elif args.subcmd == 'patches':
183 cser.list_patches(args.series, args.version, args.commit,
184 args.patch)
185 elif args.subcmd == 'progress':
186 cser.progress(args.series, args.show_all_versions,
187 args.list_patches)
188 elif args.subcmd == 'rm':
189 cser.remove(args.series, dry_run=args.dry_run)
190 elif args.subcmd == 'rm-version':
191 cser.version_remove(args.series, args.version, dry_run=args.dry_run)
192 elif args.subcmd == 'rename':
193 cser.rename(args.series, args.new_name, dry_run=args.dry_run)
194 elif args.subcmd == 'scan':
195 cser.scan(args.series, mark=args.mark,
196 allow_unmarked=args.allow_unmarked, end=args.upstream,
197 dry_run=args.dry_run)
198 elif args.subcmd == 'send':
199 cser.send(pwork, args.series, args.autolink, args.autolink_wait,
200 args)
201 elif args.subcmd == 'set-link':
202 cser.link_set(args.series, args.version, args.link, args.update)
203 elif args.subcmd == 'status':
204 cser.status(pwork, args.series, args.version, args.show_comments,
205 args.show_cover_comments)
206 elif args.subcmd == 'summary':
207 cser.summary(args.series)
208 elif args.subcmd == 'unarchive':
209 cser.unarchive(args.series)
210 elif args.subcmd == 'unmark':
211 cser.unmark(args.series, args.allow_unmarked, dry_run=args.dry_run)
212 elif args.subcmd == 'version-change':
213 cser.version_change(args.series, args.version, args.new_version,
214 dry_run=args.dry_run)
215 else:
216 raise ValueError(f"Unknown series subcommand '{args.subcmd}'")
217 finally:
218 cser.close_database()
219
220
Simon Glassbe7e6c02025-05-10 13:05:12 +0200221def patchwork(args, test_db=None, pwork=None):
222 """Process a 'patchwork' subcommand
223 Args:
224 args (Namespace): Arguments to process
225 test_db (str or None): Directory containing the test database, None to
226 use the normal one
227 pwork (Patchwork): Patchwork object to use
228 """
229 cser = cseries.Cseries(test_db)
230 try:
231 cser.open_database()
232 if args.subcmd == 'set-project':
233 if not pwork:
234 pwork = Patchwork(args.patchwork_url)
235 cser.project_set(pwork, args.project_name)
236 elif args.subcmd == 'get-project':
237 info = cser.project_get()
238 if not info:
239 raise ValueError("Project has not been set; use 'patman patchwork set-project'")
240 name, pwid, link_name = info
241 print(f"Project '{name}' patchwork-ID {pwid} link-name {link_name}")
242 else:
243 raise ValueError(f"Unknown patchwork subcommand '{args.subcmd}'")
244 finally:
245 cser.close_database()
246
247def do_patman(args, test_db=None, pwork=None, cser=None):
Simon Glassb8a14f92025-05-08 06:30:14 +0200248 """Process a patman command
249
250 Args:
251 args (Namespace): Arguments to process
Simon Glassbe7e6c02025-05-10 13:05:12 +0200252 test_db (str or None): Directory containing the test database, None to
253 use the normal one
254 pwork (Patchwork): Patchwork object to use, or None to create one
255 cser (Cseries): Cseries object to use when executing the command,
256 or None to create one
Simon Glassb8a14f92025-05-08 06:30:14 +0200257 """
258 if args.full_help:
259 with resources.path('patman', 'README.rst') as readme:
260 tools.print_full_help(str(readme))
261 return 0
Simon Glass3c0196f2025-04-29 07:21:58 -0600262 if args.cmd == 'send':
263 # Called from git with a patch filename as argument
264 # Printout a list of additional CC recipients for this patch
265 if args.cc_cmd:
266 re_line = re.compile(r'(\S*) (.*)')
267 with open(args.cc_cmd, 'r', encoding='utf-8') as inf:
268 for line in inf.readlines():
269 match = re_line.match(line)
270 if match and match.group(1) == args.patchfiles[0]:
271 for cca in match.group(2).split('\0'):
272 cca = cca.strip()
273 if cca:
274 print(cca)
Simon Glass3c0196f2025-04-29 07:21:58 -0600275 else:
276 # If we are not processing tags, no need to warning about bad ones
277 if not args.process_tags:
278 args.ignore_bad_tags = True
Simon Glassc0257982025-04-29 07:22:11 -0600279 do_send(args)
Simon Glassb8a14f92025-05-08 06:30:14 +0200280 return 0
Simon Glass3c0196f2025-04-29 07:21:58 -0600281
Simon Glass78ee8f82025-04-29 07:22:09 -0600282 ret_code = 0
283 try:
284 # Check status of patches in patchwork
285 if args.cmd == 'status':
Simon Glass3c0196f2025-04-29 07:21:58 -0600286 patchwork_status(args.branch, args.count, args.start, args.end,
287 args.dest_branch, args.force, args.show_comments,
288 args.patchwork_url)
Simon Glass5e52d172025-05-10 13:05:15 +0200289 elif args.cmd == 'series':
290 do_series(args, test_db, pwork, cser)
Simon Glassbe7e6c02025-05-10 13:05:12 +0200291 elif args.cmd == 'patchwork':
292 patchwork(args, test_db, pwork)
Simon Glass78ee8f82025-04-29 07:22:09 -0600293 except Exception as exc:
294 terminal.tprint(f'patman: {type(exc).__name__}: {exc}',
295 colour=terminal.Color.RED)
296 if args.debug:
297 print()
298 traceback.print_exc()
299 ret_code = 1
300 return ret_code