blob: 916ddf8fcfff863c5d7819b173e3b2bbad57377c [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
19def setup():
20 """Do required setup before doing anything"""
Simon Glass0157b182022-01-29 14:14:11 -070021 gitutil.setup()
Simon Glass7d5b04e2020-07-05 21:41:49 -060022
Philipp Tomsichb3aff152020-11-24 18:14:52 +010023def prepare_patches(col, branch, count, start, end, ignore_binary, signoff):
Simon Glass7d5b04e2020-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 Glass262130f2020-07-05 21:41:51 -060031 branch (str): Branch to create patches from (None = current)
Simon Glass7d5b04e2020-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 Glass137947e2020-07-05 21:41:52 -060035 end (int): End patch to use (0=last one in series, 1=one before that,
36 etc.)
Simon Glass7d5b04e2020-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 Glass0157b182022-01-29 14:14:11 -070048 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glass7d5b04e2020-07-05 21:41:49 -060049
50 if not count:
Nicolas Boichate1db5c92020-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 Glass252ac582022-01-29 14:14:17 -070053 sys.exit(col.build(col.RED, str))
Simon Glass7d5b04e2020-07-05 21:41:49 -060054
55 # Read the metadata from the commits
Simon Glass137947e2020-07-05 21:41:52 -060056 to_do = count - end
Simon Glassd93720e2020-10-29 21:46:19 -060057 series = patchstream.get_metadata(branch, start, to_do)
Simon Glass0157b182022-01-29 14:14:11 -070058 cover_fname, patch_files = gitutil.create_patches(
Philipp Tomsichb3aff152020-11-24 18:14:52 +010059 branch, start, to_do, ignore_binary, series, signoff)
Simon Glass7d5b04e2020-07-05 21:41:49 -060060
61 # Fix up the patch files to our liking, and insert the cover letter
Simon Glassd93720e2020-10-29 21:46:19 -060062 patchstream.fix_patches(series, patch_files)
Simon Glass7d5b04e2020-07-05 21:41:49 -060063 if cover_fname and series.get('cover'):
Simon Glassd93720e2020-10-29 21:46:19 -060064 patchstream.insert_cover_letter(cover_fname, series, to_do)
Simon Glass7d5b04e2020-07-05 21:41:49 -060065 return series, cover_fname, patch_files
66
Douglas Andersondce43222022-07-19 14:56:27 -070067def check_patches(series, patch_files, run_checkpatch, verbose, use_tree):
Simon Glass7d5b04e2020-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 Andersondce43222022-07-19 14:56:27 -070080 use_tree (bool): If False we'll pass '--no-tree' to checkpatch.
Simon Glass7d5b04e2020-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
Simon Glass98bc0b42023-03-08 10:52:52 -080088 # Check the patches
Simon Glass7d5b04e2020-07-05 21:41:49 -060089 if run_checkpatch:
Douglas Andersondce43222022-07-19 14:56:27 -070090 ok = checkpatch.check_patches(verbose, patch_files, use_tree)
Simon Glass7d5b04e2020-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,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -050097 ignore_bad_tags, add_maintainers, get_maintainer_script, limit,
98 dry_run, in_reply_to, thread, smtp_server):
Simon Glass7d5b04e2020-07-05 21:41:49 -060099 """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
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500126 get_maintainer_script (str): The script used to retrieve which
127 maintainers to cc
Simon Glass7d5b04e2020-07-05 21:41:49 -0600128 limit (int): Limit on the number of people that can be cc'd on a single
129 patch or the cover letter (None if no limit)
130 dry_run (bool): Don't actually email the patches, just print out what
131 would be sent
132 in_reply_to (str): If not None we'll pass this to git as --in-reply-to.
133 Should be a message ID that this is in reply to.
134 thread (bool): True to add --thread to git send-email (make all patches
135 reply to cover-letter or first patch in series)
136 smtp_server (str): SMTP server to use to send patches (None for default)
137 """
138 cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500139 add_maintainers, limit, get_maintainer_script)
Simon Glass7d5b04e2020-07-05 21:41:49 -0600140
141 # Email the patches out (giving the user time to check / cancel)
142 cmd = ''
143 if its_a_go:
Simon Glass0157b182022-01-29 14:14:11 -0700144 cmd = gitutil.email_patches(
Simon Glass7d5b04e2020-07-05 21:41:49 -0600145 series, cover_fname, patch_files, dry_run, not ignore_bad_tags,
146 cc_file, in_reply_to=in_reply_to, thread=thread,
147 smtp_server=smtp_server)
148 else:
Simon Glass252ac582022-01-29 14:14:17 -0700149 print(col.build(col.RED, "Not sending emails due to errors/warnings"))
Simon Glass7d5b04e2020-07-05 21:41:49 -0600150
151 # For a dry run, just show our actions as a sanity check
152 if dry_run:
153 series.ShowActions(patch_files, cmd, process_tags)
154 if not its_a_go:
Simon Glass252ac582022-01-29 14:14:17 -0700155 print(col.build(col.RED, "Email would not be sent"))
Simon Glass7d5b04e2020-07-05 21:41:49 -0600156
157 os.remove(cc_file)
158
Simon Glassfda1e372020-07-05 21:41:53 -0600159def send(args):
Simon Glass7d5b04e2020-07-05 21:41:49 -0600160 """Create, check and send patches by email
161
162 Args:
Simon Glassfda1e372020-07-05 21:41:53 -0600163 args (argparse.Namespace): Arguments to patman
Simon Glass7d5b04e2020-07-05 21:41:49 -0600164 """
165 setup()
166 col = terminal.Color()
167 series, cover_fname, patch_files = prepare_patches(
Simon Glassfda1e372020-07-05 21:41:53 -0600168 col, args.branch, args.count, args.start, args.end,
Philipp Tomsichb3aff152020-11-24 18:14:52 +0100169 args.ignore_binary, args.add_signoff)
Simon Glassfda1e372020-07-05 21:41:53 -0600170 ok = check_patches(series, patch_files, args.check_patch,
Douglas Andersondce43222022-07-19 14:56:27 -0700171 args.verbose, args.check_patch_use_tree)
Simon Glass262130f2020-07-05 21:41:51 -0600172
Simon Glass0157b182022-01-29 14:14:11 -0700173 ok = ok and gitutil.check_suppress_cc_config()
Nicolas Boichat94977562020-07-13 10:50:00 +0800174
Simon Glassfda1e372020-07-05 21:41:53 -0600175 its_a_go = ok or args.ignore_errors
Simon Glass4a9e5782020-10-29 21:46:10 -0600176 email_patches(
177 col, series, cover_fname, patch_files, args.process_tags,
178 its_a_go, args.ignore_bad_tags, args.add_maintainers,
Maxim Cournoyer8c042fb2022-12-20 00:28:46 -0500179 args.get_maintainer_script, args.limit, args.dry_run,
180 args.in_reply_to, args.thread, args.smtp_server)
Simon Glassdc6df972020-10-29 21:46:35 -0600181
Simon Glassdc4b2a92020-10-29 21:46:38 -0600182def patchwork_status(branch, count, start, end, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700183 show_comments, url):
Simon Glassdc6df972020-10-29 21:46:35 -0600184 """Check the status of patches in patchwork
185
186 This finds the series in patchwork using the Series-link tag, checks for new
Simon Glassdc4b2a92020-10-29 21:46:38 -0600187 comments and review tags, displays then and creates a new branch with the
188 review tags.
Simon Glassdc6df972020-10-29 21:46:35 -0600189
190 Args:
191 branch (str): Branch to create patches from (None = current)
192 count (int): Number of patches to produce, or -1 to produce patches for
193 the current branch back to the upstream commit
194 start (int): Start partch to use (0=first / top of branch)
195 end (int): End patch to use (0=last one in series, 1=one before that,
196 etc.)
Simon Glass8f9ba3a2020-10-29 21:46:36 -0600197 dest_branch (str): Name of new branch to create with the updated tags
198 (None to not create a branch)
199 force (bool): With dest_branch, force overwriting an existing branch
Simon Glassdc4b2a92020-10-29 21:46:38 -0600200 show_comments (bool): True to display snippets from the comments
201 provided by reviewers
Simon Glassfcbec652020-11-03 13:54:16 -0700202 url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
203 This is ignored if the series provides a Series-patchwork-url tag.
Simon Glassdc6df972020-10-29 21:46:35 -0600204
205 Raises:
206 ValueError: if the branch has no Series-link value
207 """
208 if count == -1:
209 # Work out how many patches to send if we can
Simon Glass0157b182022-01-29 14:14:11 -0700210 count = (gitutil.count_commits_to_branch(branch) - start)
Simon Glassdc6df972020-10-29 21:46:35 -0600211
212 series = patchstream.get_metadata(branch, start, count - end)
213 warnings = 0
214 for cmt in series.commits:
215 if cmt.warn:
216 print('%d warnings for %s:' % (len(cmt.warn), cmt.hash))
217 for warn in cmt.warn:
218 print('\t', warn)
219 warnings += 1
220 print
221 if warnings:
222 raise ValueError('Please fix warnings before running status')
223 links = series.get('links')
224 if not links:
225 raise ValueError("Branch has no Series-links value")
226
227 # Find the link without a version number (we don't support versions yet)
228 found = [link for link in links.split() if not ':' in link]
229 if not found:
230 raise ValueError('Series-links has no current version (without :)')
231
Simon Glassfcbec652020-11-03 13:54:16 -0700232 # Allow the series to override the URL
233 if 'patchwork_url' in series:
234 url = series.patchwork_url
235
Simon Glassdc6df972020-10-29 21:46:35 -0600236 # Import this here to avoid failing on other commands if the dependencies
237 # are not present
238 from patman import status
Simon Glassdc4b2a92020-10-29 21:46:38 -0600239 status.check_patchwork_status(series, found[0], branch, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700240 show_comments, url)