blob: b292da9dc27622010c236d68825a650c375037d3 [file] [log] [blame]
Simon Glass7d5b04e2020-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
11import os
12import sys
13
14from patman import checkpatch
15from patman import gitutil
16from patman import patchstream
Simon Glass4583c002023-02-23 18:18:04 -070017from u_boot_pylib import terminal
Simon Glass7d5b04e2020-07-05 21:41:49 -060018
Maxim Cournoyera13af892023-10-12 23:06:24 -040019
Simon Glass7d5b04e2020-07-05 21:41:49 -060020def setup():
21 """Do required setup before doing anything"""
Simon Glass0157b182022-01-29 14:14:11 -070022 gitutil.setup()
Simon Glass7d5b04e2020-07-05 21:41:49 -060023
Maxim Cournoyera13af892023-10-12 23:06:24 -040024
25def prepare_patches(col, branch, count, start, end, ignore_binary, signoff,
26 keep_change_id=False):
Simon Glass7d5b04e2020-07-05 21:41:49 -060027 """Figure out what patches to generate, then generate them
28
29 The patch files are written to the current directory, e.g. 0001_xxx.patch
30 0002_yyy.patch
31
32 Args:
33 col (terminal.Color): Colour output object
Simon Glass262130f2020-07-05 21:41:51 -060034 branch (str): Branch to create patches from (None = current)
Simon Glass7d5b04e2020-07-05 21:41:49 -060035 count (int): Number of patches to produce, or -1 to produce patches for
36 the current branch back to the upstream commit
37 start (int): Start partch to use (0=first / top of branch)
Simon Glass137947e2020-07-05 21:41:52 -060038 end (int): End patch to use (0=last one in series, 1=one before that,
39 etc.)
Simon Glass7d5b04e2020-07-05 21:41:49 -060040 ignore_binary (bool): Don't generate patches for binary files
Maxim Cournoyera13af892023-10-12 23:06:24 -040041 keep_change_id (bool): Preserve the Change-Id tag.
Simon Glass7d5b04e2020-07-05 21:41:49 -060042
43 Returns:
44 Tuple:
45 Series object for this series (set of patches)
46 Filename of the cover letter as a string (None if none)
47 patch_files: List of patch filenames, each a string, e.g.
48 ['0001_xxx.patch', '0002_yyy.patch']
49 """
50 if count == -1:
51 # Work out how many patches to send if we can
Simon Glass0157b182022-01-29 14:14:11 -070052 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glass7d5b04e2020-07-05 21:41:49 -060053
54 if not count:
Nicolas Boichate1db5c92020-07-13 10:50:01 +080055 str = 'No commits found to process - please use -c flag, or run:\n' \
56 ' git branch --set-upstream-to remote/branch'
Simon Glass252ac582022-01-29 14:14:17 -070057 sys.exit(col.build(col.RED, str))
Simon Glass7d5b04e2020-07-05 21:41:49 -060058
59 # Read the metadata from the commits
Simon Glass137947e2020-07-05 21:41:52 -060060 to_do = count - end
Simon Glassd93720e2020-10-29 21:46:19 -060061 series = patchstream.get_metadata(branch, start, to_do)
Simon Glass0157b182022-01-29 14:14:11 -070062 cover_fname, patch_files = gitutil.create_patches(
Philipp Tomsichb3aff152020-11-24 18:14:52 +010063 branch, start, to_do, ignore_binary, series, signoff)
Simon Glass7d5b04e2020-07-05 21:41:49 -060064
65 # Fix up the patch files to our liking, and insert the cover letter
Maxim Cournoyera13af892023-10-12 23:06:24 -040066 patchstream.fix_patches(series, patch_files, keep_change_id)
Simon Glass7d5b04e2020-07-05 21:41:49 -060067 if cover_fname and series.get('cover'):
Simon Glassd93720e2020-10-29 21:46:19 -060068 patchstream.insert_cover_letter(cover_fname, series, to_do)
Simon Glass7d5b04e2020-07-05 21:41:49 -060069 return series, cover_fname, patch_files
70
Maxim Cournoyera13af892023-10-12 23:06:24 -040071
Douglas Andersondce43222022-07-19 14:56:27 -070072def check_patches(series, patch_files, run_checkpatch, verbose, use_tree):
Simon Glass7d5b04e2020-07-05 21:41:49 -060073 """Run some checks on a set of patches
74
75 This santiy-checks the patman tags like Series-version and runs the patches
76 through checkpatch
77
78 Args:
79 series (Series): Series object for this series (set of patches)
80 patch_files (list): List of patch filenames, each a string, e.g.
81 ['0001_xxx.patch', '0002_yyy.patch']
82 run_checkpatch (bool): True to run checkpatch.pl
83 verbose (bool): True to print out every line of the checkpatch output as
84 it is parsed
Douglas Andersondce43222022-07-19 14:56:27 -070085 use_tree (bool): If False we'll pass '--no-tree' to checkpatch.
Simon Glass7d5b04e2020-07-05 21:41:49 -060086
87 Returns:
88 bool: True if the patches had no errors, False if they did
89 """
90 # Do a few checks on the series
91 series.DoChecks()
92
Simon Glass98bc0b42023-03-08 10:52:52 -080093 # Check the patches
Simon Glass7d5b04e2020-07-05 21:41:49 -060094 if run_checkpatch:
Douglas Andersondce43222022-07-19 14:56:27 -070095 ok = checkpatch.check_patches(verbose, patch_files, use_tree)
Simon Glass7d5b04e2020-07-05 21:41:49 -060096 else:
97 ok = True
98 return ok
99
100
101def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500102 ignore_bad_tags, add_maintainers, get_maintainer_script, limit,
103 dry_run, in_reply_to, thread, smtp_server):
Simon Glass7d5b04e2020-07-05 21:41:49 -0600104 """Email patches to the recipients
105
106 This emails out the patches and cover letter using 'git send-email'. Each
107 patch is copied to recipients identified by the patch tag and output from
108 the get_maintainer.pl script. The cover letter is copied to all recipients
109 of any patch.
110
111 To make this work a CC file is created holding the recipients for each patch
112 and the cover letter. See the main program 'cc_cmd' for this logic.
113
114 Args:
115 col (terminal.Color): Colour output object
116 series (Series): Series object for this series (set of patches)
117 cover_fname (str): Filename of the cover letter as a string (None if
118 none)
119 patch_files (list): List of patch filenames, each a string, e.g.
120 ['0001_xxx.patch', '0002_yyy.patch']
121 process_tags (bool): True to process subject tags in each patch, e.g.
122 for 'dm: spi: Add SPI support' this would be 'dm' and 'spi'. The
123 tags are looked up in the configured sendemail.aliasesfile and also
124 in ~/.patman (see README)
125 its_a_go (bool): True if we are going to actually send the patches,
126 False if the patches have errors and will not be sent unless
127 @ignore_errors
128 ignore_bad_tags (bool): True to just print a warning for unknown tags,
129 False to halt with an error
130 add_maintainers (bool): Run the get_maintainer.pl script for each patch
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500131 get_maintainer_script (str): The script used to retrieve which
132 maintainers to cc
Simon Glass7d5b04e2020-07-05 21:41:49 -0600133 limit (int): Limit on the number of people that can be cc'd on a single
134 patch or the cover letter (None if no limit)
135 dry_run (bool): Don't actually email the patches, just print out what
136 would be sent
137 in_reply_to (str): If not None we'll pass this to git as --in-reply-to.
138 Should be a message ID that this is in reply to.
139 thread (bool): True to add --thread to git send-email (make all patches
140 reply to cover-letter or first patch in series)
141 smtp_server (str): SMTP server to use to send patches (None for default)
142 """
143 cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500144 add_maintainers, limit, get_maintainer_script)
Simon Glass7d5b04e2020-07-05 21:41:49 -0600145
146 # Email the patches out (giving the user time to check / cancel)
147 cmd = ''
148 if its_a_go:
Simon Glass0157b182022-01-29 14:14:11 -0700149 cmd = gitutil.email_patches(
Simon Glass7d5b04e2020-07-05 21:41:49 -0600150 series, cover_fname, patch_files, dry_run, not ignore_bad_tags,
151 cc_file, in_reply_to=in_reply_to, thread=thread,
152 smtp_server=smtp_server)
153 else:
Simon Glass252ac582022-01-29 14:14:17 -0700154 print(col.build(col.RED, "Not sending emails due to errors/warnings"))
Simon Glass7d5b04e2020-07-05 21:41:49 -0600155
156 # For a dry run, just show our actions as a sanity check
157 if dry_run:
158 series.ShowActions(patch_files, cmd, process_tags)
159 if not its_a_go:
Simon Glass252ac582022-01-29 14:14:17 -0700160 print(col.build(col.RED, "Email would not be sent"))
Simon Glass7d5b04e2020-07-05 21:41:49 -0600161
162 os.remove(cc_file)
163
Simon Glassfda1e372020-07-05 21:41:53 -0600164def send(args):
Simon Glass7d5b04e2020-07-05 21:41:49 -0600165 """Create, check and send patches by email
166
167 Args:
Simon Glassfda1e372020-07-05 21:41:53 -0600168 args (argparse.Namespace): Arguments to patman
Simon Glass7d5b04e2020-07-05 21:41:49 -0600169 """
170 setup()
171 col = terminal.Color()
172 series, cover_fname, patch_files = prepare_patches(
Simon Glassfda1e372020-07-05 21:41:53 -0600173 col, args.branch, args.count, args.start, args.end,
Maxim Cournoyera13af892023-10-12 23:06:24 -0400174 args.ignore_binary, args.add_signoff,
175 keep_change_id=args.keep_change_id)
Simon Glassfda1e372020-07-05 21:41:53 -0600176 ok = check_patches(series, patch_files, args.check_patch,
Douglas Andersondce43222022-07-19 14:56:27 -0700177 args.verbose, args.check_patch_use_tree)
Simon Glass262130f2020-07-05 21:41:51 -0600178
Simon Glass0157b182022-01-29 14:14:11 -0700179 ok = ok and gitutil.check_suppress_cc_config()
Nicolas Boichat94977562020-07-13 10:50:00 +0800180
Simon Glassfda1e372020-07-05 21:41:53 -0600181 its_a_go = ok or args.ignore_errors
Simon Glass4a9e5782020-10-29 21:46:10 -0600182 email_patches(
183 col, series, cover_fname, patch_files, args.process_tags,
184 its_a_go, args.ignore_bad_tags, args.add_maintainers,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500185 args.get_maintainer_script, args.limit, args.dry_run,
186 args.in_reply_to, args.thread, args.smtp_server)
Simon Glassdc6df972020-10-29 21:46:35 -0600187
Simon Glassdc4b2a92020-10-29 21:46:38 -0600188def patchwork_status(branch, count, start, end, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700189 show_comments, url):
Simon Glassdc6df972020-10-29 21:46:35 -0600190 """Check the status of patches in patchwork
191
192 This finds the series in patchwork using the Series-link tag, checks for new
Simon Glassdc4b2a92020-10-29 21:46:38 -0600193 comments and review tags, displays then and creates a new branch with the
194 review tags.
Simon Glassdc6df972020-10-29 21:46:35 -0600195
196 Args:
197 branch (str): Branch to create patches from (None = current)
198 count (int): Number of patches to produce, or -1 to produce patches for
199 the current branch back to the upstream commit
200 start (int): Start partch to use (0=first / top of branch)
201 end (int): End patch to use (0=last one in series, 1=one before that,
202 etc.)
Simon Glass8f9ba3a2020-10-29 21:46:36 -0600203 dest_branch (str): Name of new branch to create with the updated tags
204 (None to not create a branch)
205 force (bool): With dest_branch, force overwriting an existing branch
Simon Glassdc4b2a92020-10-29 21:46:38 -0600206 show_comments (bool): True to display snippets from the comments
207 provided by reviewers
Simon Glassfcbec652020-11-03 13:54:16 -0700208 url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
209 This is ignored if the series provides a Series-patchwork-url tag.
Simon Glassdc6df972020-10-29 21:46:35 -0600210
211 Raises:
212 ValueError: if the branch has no Series-link value
213 """
214 if count == -1:
215 # Work out how many patches to send if we can
Simon Glass0157b182022-01-29 14:14:11 -0700216 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glassdc6df972020-10-29 21:46:35 -0600217
218 series = patchstream.get_metadata(branch, start, count - end)
219 warnings = 0
220 for cmt in series.commits:
221 if cmt.warn:
222 print('%d warnings for %s:' % (len(cmt.warn), cmt.hash))
223 for warn in cmt.warn:
224 print('\t', warn)
225 warnings += 1
226 print
227 if warnings:
228 raise ValueError('Please fix warnings before running status')
229 links = series.get('links')
230 if not links:
231 raise ValueError("Branch has no Series-links value")
232
233 # Find the link without a version number (we don't support versions yet)
234 found = [link for link in links.split() if not ':' in link]
235 if not found:
236 raise ValueError('Series-links has no current version (without :)')
237
Simon Glassfcbec652020-11-03 13:54:16 -0700238 # Allow the series to override the URL
239 if 'patchwork_url' in series:
240 url = series.patchwork_url
241
Simon Glassdc6df972020-10-29 21:46:35 -0600242 # Import this here to avoid failing on other commands if the dependencies
243 # are not present
244 from patman import status
Simon Glassdc4b2a92020-10-29 21:46:38 -0600245 status.check_patchwork_status(series, found[0], branch, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700246 show_comments, url)