blob: bf426cf7bcf4e74c222972187aff47e991431dc6 [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
11import os
12import sys
13
14from patman import checkpatch
15from patman import gitutil
16from patman import patchstream
17from patman import terminal
18
19def setup():
20 """Do required setup before doing anything"""
Simon Glass761648b2022-01-29 14:14:11 -070021 gitutil.setup()
Simon Glass24725af2020-07-05 21:41:49 -060022
Philipp Tomsich858531a2020-11-24 18:14:52 +010023def prepare_patches(col, branch, count, start, end, ignore_binary, signoff):
Simon Glass24725af2020-07-05 21:41:49 -060024 """Figure out what patches to generate, then generate them
25
26 The patch files are written to the current directory, e.g. 0001_xxx.patch
27 0002_yyy.patch
28
29 Args:
30 col (terminal.Color): Colour output object
Simon Glass2eb4da72020-07-05 21:41:51 -060031 branch (str): Branch to create patches from (None = current)
Simon Glass24725af2020-07-05 21:41:49 -060032 count (int): Number of patches to produce, or -1 to produce patches for
33 the current branch back to the upstream commit
34 start (int): Start partch to use (0=first / top of branch)
Simon Glassb3bf4e12020-07-05 21:41:52 -060035 end (int): End patch to use (0=last one in series, 1=one before that,
36 etc.)
Simon Glass24725af2020-07-05 21:41:49 -060037 ignore_binary (bool): Don't generate patches for binary files
38
39 Returns:
40 Tuple:
41 Series object for this series (set of patches)
42 Filename of the cover letter as a string (None if none)
43 patch_files: List of patch filenames, each a string, e.g.
44 ['0001_xxx.patch', '0002_yyy.patch']
45 """
46 if count == -1:
47 # Work out how many patches to send if we can
Simon Glass761648b2022-01-29 14:14:11 -070048 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glass24725af2020-07-05 21:41:49 -060049
50 if not count:
Nicolas Boichat2173fb42020-07-13 10:50:01 +080051 str = 'No commits found to process - please use -c flag, or run:\n' \
52 ' git branch --set-upstream-to remote/branch'
Simon Glassf45d3742022-01-29 14:14:17 -070053 sys.exit(col.build(col.RED, str))
Simon Glass24725af2020-07-05 21:41:49 -060054
55 # Read the metadata from the commits
Simon Glassb3bf4e12020-07-05 21:41:52 -060056 to_do = count - end
Simon Glass93f61c02020-10-29 21:46:19 -060057 series = patchstream.get_metadata(branch, start, to_do)
Simon Glass761648b2022-01-29 14:14:11 -070058 cover_fname, patch_files = gitutil.create_patches(
Philipp Tomsich858531a2020-11-24 18:14:52 +010059 branch, start, to_do, ignore_binary, series, signoff)
Simon Glass24725af2020-07-05 21:41:49 -060060
61 # Fix up the patch files to our liking, and insert the cover letter
Simon Glass93f61c02020-10-29 21:46:19 -060062 patchstream.fix_patches(series, patch_files)
Simon Glass24725af2020-07-05 21:41:49 -060063 if cover_fname and series.get('cover'):
Simon Glass93f61c02020-10-29 21:46:19 -060064 patchstream.insert_cover_letter(cover_fname, series, to_do)
Simon Glass24725af2020-07-05 21:41:49 -060065 return series, cover_fname, patch_files
66
Douglas Andersonbe4f2712022-07-19 14:56:27 -070067def check_patches(series, patch_files, run_checkpatch, verbose, use_tree):
Simon Glass24725af2020-07-05 21:41:49 -060068 """Run some checks on a set of patches
69
70 This santiy-checks the patman tags like Series-version and runs the patches
71 through checkpatch
72
73 Args:
74 series (Series): Series object for this series (set of patches)
75 patch_files (list): List of patch filenames, each a string, e.g.
76 ['0001_xxx.patch', '0002_yyy.patch']
77 run_checkpatch (bool): True to run checkpatch.pl
78 verbose (bool): True to print out every line of the checkpatch output as
79 it is parsed
Douglas Andersonbe4f2712022-07-19 14:56:27 -070080 use_tree (bool): If False we'll pass '--no-tree' to checkpatch.
Simon Glass24725af2020-07-05 21:41:49 -060081
82 Returns:
83 bool: True if the patches had no errors, False if they did
84 """
85 # Do a few checks on the series
86 series.DoChecks()
87
88 # Check the patches, and run them through 'git am' just to be sure
89 if run_checkpatch:
Douglas Andersonbe4f2712022-07-19 14:56:27 -070090 ok = checkpatch.check_patches(verbose, patch_files, use_tree)
Simon Glass24725af2020-07-05 21:41:49 -060091 else:
92 ok = True
93 return ok
94
95
96def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
97 ignore_bad_tags, add_maintainers, limit, dry_run, in_reply_to,
98 thread, smtp_server):
99 """Email patches to the recipients
100
101 This emails out the patches and cover letter using 'git send-email'. Each
102 patch is copied to recipients identified by the patch tag and output from
103 the get_maintainer.pl script. The cover letter is copied to all recipients
104 of any patch.
105
106 To make this work a CC file is created holding the recipients for each patch
107 and the cover letter. See the main program 'cc_cmd' for this logic.
108
109 Args:
110 col (terminal.Color): Colour output object
111 series (Series): Series object for this series (set of patches)
112 cover_fname (str): Filename of the cover letter as a string (None if
113 none)
114 patch_files (list): List of patch filenames, each a string, e.g.
115 ['0001_xxx.patch', '0002_yyy.patch']
116 process_tags (bool): True to process subject tags in each patch, e.g.
117 for 'dm: spi: Add SPI support' this would be 'dm' and 'spi'. The
118 tags are looked up in the configured sendemail.aliasesfile and also
119 in ~/.patman (see README)
120 its_a_go (bool): True if we are going to actually send the patches,
121 False if the patches have errors and will not be sent unless
122 @ignore_errors
123 ignore_bad_tags (bool): True to just print a warning for unknown tags,
124 False to halt with an error
125 add_maintainers (bool): Run the get_maintainer.pl script for each patch
126 limit (int): Limit on the number of people that can be cc'd on a single
127 patch or the cover letter (None if no limit)
128 dry_run (bool): Don't actually email the patches, just print out what
129 would be sent
130 in_reply_to (str): If not None we'll pass this to git as --in-reply-to.
131 Should be a message ID that this is in reply to.
132 thread (bool): True to add --thread to git send-email (make all patches
133 reply to cover-letter or first patch in series)
134 smtp_server (str): SMTP server to use to send patches (None for default)
135 """
136 cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
137 add_maintainers, limit)
138
139 # Email the patches out (giving the user time to check / cancel)
140 cmd = ''
141 if its_a_go:
Simon Glass761648b2022-01-29 14:14:11 -0700142 cmd = gitutil.email_patches(
Simon Glass24725af2020-07-05 21:41:49 -0600143 series, cover_fname, patch_files, dry_run, not ignore_bad_tags,
144 cc_file, in_reply_to=in_reply_to, thread=thread,
145 smtp_server=smtp_server)
146 else:
Simon Glassf45d3742022-01-29 14:14:17 -0700147 print(col.build(col.RED, "Not sending emails due to errors/warnings"))
Simon Glass24725af2020-07-05 21:41:49 -0600148
149 # For a dry run, just show our actions as a sanity check
150 if dry_run:
151 series.ShowActions(patch_files, cmd, process_tags)
152 if not its_a_go:
Simon Glassf45d3742022-01-29 14:14:17 -0700153 print(col.build(col.RED, "Email would not be sent"))
Simon Glass24725af2020-07-05 21:41:49 -0600154
155 os.remove(cc_file)
156
Simon Glasseb101ac2020-07-05 21:41:53 -0600157def send(args):
Simon Glass24725af2020-07-05 21:41:49 -0600158 """Create, check and send patches by email
159
160 Args:
Simon Glasseb101ac2020-07-05 21:41:53 -0600161 args (argparse.Namespace): Arguments to patman
Simon Glass24725af2020-07-05 21:41:49 -0600162 """
163 setup()
164 col = terminal.Color()
165 series, cover_fname, patch_files = prepare_patches(
Simon Glasseb101ac2020-07-05 21:41:53 -0600166 col, args.branch, args.count, args.start, args.end,
Philipp Tomsich858531a2020-11-24 18:14:52 +0100167 args.ignore_binary, args.add_signoff)
Simon Glasseb101ac2020-07-05 21:41:53 -0600168 ok = check_patches(series, patch_files, args.check_patch,
Douglas Andersonbe4f2712022-07-19 14:56:27 -0700169 args.verbose, args.check_patch_use_tree)
Simon Glass2eb4da72020-07-05 21:41:51 -0600170
Simon Glass761648b2022-01-29 14:14:11 -0700171 ok = ok and gitutil.check_suppress_cc_config()
Nicolas Boichat0da95742020-07-13 10:50:00 +0800172
Simon Glasseb101ac2020-07-05 21:41:53 -0600173 its_a_go = ok or args.ignore_errors
Simon Glass4318b712020-10-29 21:46:10 -0600174 email_patches(
175 col, series, cover_fname, patch_files, args.process_tags,
176 its_a_go, args.ignore_bad_tags, args.add_maintainers,
177 args.limit, args.dry_run, args.in_reply_to, args.thread,
178 args.smtp_server)
Simon Glass3db916d2020-10-29 21:46:35 -0600179
Simon Glass2112d072020-10-29 21:46:38 -0600180def patchwork_status(branch, count, start, end, dest_branch, force,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700181 show_comments, url):
Simon Glass3db916d2020-10-29 21:46:35 -0600182 """Check the status of patches in patchwork
183
184 This finds the series in patchwork using the Series-link tag, checks for new
Simon Glass2112d072020-10-29 21:46:38 -0600185 comments and review tags, displays then and creates a new branch with the
186 review tags.
Simon Glass3db916d2020-10-29 21:46:35 -0600187
188 Args:
189 branch (str): Branch to create patches from (None = current)
190 count (int): Number of patches to produce, or -1 to produce patches for
191 the current branch back to the upstream commit
192 start (int): Start partch to use (0=first / top of branch)
193 end (int): End patch to use (0=last one in series, 1=one before that,
194 etc.)
Simon Glassd0a0a582020-10-29 21:46:36 -0600195 dest_branch (str): Name of new branch to create with the updated tags
196 (None to not create a branch)
197 force (bool): With dest_branch, force overwriting an existing branch
Simon Glass2112d072020-10-29 21:46:38 -0600198 show_comments (bool): True to display snippets from the comments
199 provided by reviewers
Simon Glass4acc93c2020-11-03 13:54:16 -0700200 url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
201 This is ignored if the series provides a Series-patchwork-url tag.
Simon Glass3db916d2020-10-29 21:46:35 -0600202
203 Raises:
204 ValueError: if the branch has no Series-link value
205 """
206 if count == -1:
207 # Work out how many patches to send if we can
Simon Glass761648b2022-01-29 14:14:11 -0700208 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glass3db916d2020-10-29 21:46:35 -0600209
210 series = patchstream.get_metadata(branch, start, count - end)
211 warnings = 0
212 for cmt in series.commits:
213 if cmt.warn:
214 print('%d warnings for %s:' % (len(cmt.warn), cmt.hash))
215 for warn in cmt.warn:
216 print('\t', warn)
217 warnings += 1
218 print
219 if warnings:
220 raise ValueError('Please fix warnings before running status')
221 links = series.get('links')
222 if not links:
223 raise ValueError("Branch has no Series-links value")
224
225 # Find the link without a version number (we don't support versions yet)
226 found = [link for link in links.split() if not ':' in link]
227 if not found:
228 raise ValueError('Series-links has no current version (without :)')
229
Simon Glass4acc93c2020-11-03 13:54:16 -0700230 # Allow the series to override the URL
231 if 'patchwork_url' in series:
232 url = series.patchwork_url
233
Simon Glass3db916d2020-10-29 21:46:35 -0600234 # Import this here to avoid failing on other commands if the dependencies
235 # are not present
236 from patman import status
Simon Glass2112d072020-10-29 21:46:38 -0600237 status.check_patchwork_status(series, found[0], branch, dest_branch, force,
Simon Glassf9b03cf2020-11-03 13:54:14 -0700238 show_comments, url)