blob: ee9717cbf62e431793512b8c54d609bff1174869 [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
17from patman import terminal
18
19def setup():
20 """Do required setup before doing anything"""
21 gitutil.Setup()
22
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 Glass262130f2020-07-05 21:41:51 -060048 count = (gitutil.CountCommitsToBranch(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'
53 sys.exit(col.Color(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 Glass7d5b04e2020-07-05 21:41:49 -060058 cover_fname, patch_files = gitutil.CreatePatches(
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
67def check_patches(series, patch_files, run_checkpatch, verbose):
68 """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
80
81 Returns:
82 bool: True if the patches had no errors, False if they did
83 """
84 # Do a few checks on the series
85 series.DoChecks()
86
87 # Check the patches, and run them through 'git am' just to be sure
88 if run_checkpatch:
89 ok = checkpatch.CheckPatches(verbose, patch_files)
90 else:
91 ok = True
92 return ok
93
94
95def email_patches(col, series, cover_fname, patch_files, process_tags, its_a_go,
96 ignore_bad_tags, add_maintainers, limit, dry_run, in_reply_to,
97 thread, smtp_server):
98 """Email patches to the recipients
99
100 This emails out the patches and cover letter using 'git send-email'. Each
101 patch is copied to recipients identified by the patch tag and output from
102 the get_maintainer.pl script. The cover letter is copied to all recipients
103 of any patch.
104
105 To make this work a CC file is created holding the recipients for each patch
106 and the cover letter. See the main program 'cc_cmd' for this logic.
107
108 Args:
109 col (terminal.Color): Colour output object
110 series (Series): Series object for this series (set of patches)
111 cover_fname (str): Filename of the cover letter as a string (None if
112 none)
113 patch_files (list): List of patch filenames, each a string, e.g.
114 ['0001_xxx.patch', '0002_yyy.patch']
115 process_tags (bool): True to process subject tags in each patch, e.g.
116 for 'dm: spi: Add SPI support' this would be 'dm' and 'spi'. The
117 tags are looked up in the configured sendemail.aliasesfile and also
118 in ~/.patman (see README)
119 its_a_go (bool): True if we are going to actually send the patches,
120 False if the patches have errors and will not be sent unless
121 @ignore_errors
122 ignore_bad_tags (bool): True to just print a warning for unknown tags,
123 False to halt with an error
124 add_maintainers (bool): Run the get_maintainer.pl script for each patch
125 limit (int): Limit on the number of people that can be cc'd on a single
126 patch or the cover letter (None if no limit)
127 dry_run (bool): Don't actually email the patches, just print out what
128 would be sent
129 in_reply_to (str): If not None we'll pass this to git as --in-reply-to.
130 Should be a message ID that this is in reply to.
131 thread (bool): True to add --thread to git send-email (make all patches
132 reply to cover-letter or first patch in series)
133 smtp_server (str): SMTP server to use to send patches (None for default)
134 """
135 cc_file = series.MakeCcFile(process_tags, cover_fname, not ignore_bad_tags,
136 add_maintainers, limit)
137
138 # Email the patches out (giving the user time to check / cancel)
139 cmd = ''
140 if its_a_go:
141 cmd = gitutil.EmailPatches(
142 series, cover_fname, patch_files, dry_run, not ignore_bad_tags,
143 cc_file, in_reply_to=in_reply_to, thread=thread,
144 smtp_server=smtp_server)
145 else:
146 print(col.Color(col.RED, "Not sending emails due to errors/warnings"))
147
148 # For a dry run, just show our actions as a sanity check
149 if dry_run:
150 series.ShowActions(patch_files, cmd, process_tags)
151 if not its_a_go:
152 print(col.Color(col.RED, "Email would not be sent"))
153
154 os.remove(cc_file)
155
Simon Glassfda1e372020-07-05 21:41:53 -0600156def send(args):
Simon Glass7d5b04e2020-07-05 21:41:49 -0600157 """Create, check and send patches by email
158
159 Args:
Simon Glassfda1e372020-07-05 21:41:53 -0600160 args (argparse.Namespace): Arguments to patman
Simon Glass7d5b04e2020-07-05 21:41:49 -0600161 """
162 setup()
163 col = terminal.Color()
164 series, cover_fname, patch_files = prepare_patches(
Simon Glassfda1e372020-07-05 21:41:53 -0600165 col, args.branch, args.count, args.start, args.end,
Philipp Tomsichb3aff152020-11-24 18:14:52 +0100166 args.ignore_binary, args.add_signoff)
Simon Glassfda1e372020-07-05 21:41:53 -0600167 ok = check_patches(series, patch_files, args.check_patch,
168 args.verbose)
Simon Glass262130f2020-07-05 21:41:51 -0600169
Nicolas Boichat94977562020-07-13 10:50:00 +0800170 ok = ok and gitutil.CheckSuppressCCConfig()
171
Simon Glassfda1e372020-07-05 21:41:53 -0600172 its_a_go = ok or args.ignore_errors
Simon Glass4a9e5782020-10-29 21:46:10 -0600173 email_patches(
174 col, series, cover_fname, patch_files, args.process_tags,
175 its_a_go, args.ignore_bad_tags, args.add_maintainers,
176 args.limit, args.dry_run, args.in_reply_to, args.thread,
177 args.smtp_server)
Simon Glassdc6df972020-10-29 21:46:35 -0600178
Simon Glassdc4b2a92020-10-29 21:46:38 -0600179def patchwork_status(branch, count, start, end, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700180 show_comments, url):
Simon Glassdc6df972020-10-29 21:46:35 -0600181 """Check the status of patches in patchwork
182
183 This finds the series in patchwork using the Series-link tag, checks for new
Simon Glassdc4b2a92020-10-29 21:46:38 -0600184 comments and review tags, displays then and creates a new branch with the
185 review tags.
Simon Glassdc6df972020-10-29 21:46:35 -0600186
187 Args:
188 branch (str): Branch to create patches from (None = current)
189 count (int): Number of patches to produce, or -1 to produce patches for
190 the current branch back to the upstream commit
191 start (int): Start partch to use (0=first / top of branch)
192 end (int): End patch to use (0=last one in series, 1=one before that,
193 etc.)
Simon Glass8f9ba3a2020-10-29 21:46:36 -0600194 dest_branch (str): Name of new branch to create with the updated tags
195 (None to not create a branch)
196 force (bool): With dest_branch, force overwriting an existing branch
Simon Glassdc4b2a92020-10-29 21:46:38 -0600197 show_comments (bool): True to display snippets from the comments
198 provided by reviewers
Simon Glassfcbec652020-11-03 13:54:16 -0700199 url (str): URL of patchwork server, e.g. 'https://patchwork.ozlabs.org'.
200 This is ignored if the series provides a Series-patchwork-url tag.
Simon Glassdc6df972020-10-29 21:46:35 -0600201
202 Raises:
203 ValueError: if the branch has no Series-link value
204 """
205 if count == -1:
206 # Work out how many patches to send if we can
207 count = (gitutil.CountCommitsToBranch(branch) - start)
208
209 series = patchstream.get_metadata(branch, start, count - end)
210 warnings = 0
211 for cmt in series.commits:
212 if cmt.warn:
213 print('%d warnings for %s:' % (len(cmt.warn), cmt.hash))
214 for warn in cmt.warn:
215 print('\t', warn)
216 warnings += 1
217 print
218 if warnings:
219 raise ValueError('Please fix warnings before running status')
220 links = series.get('links')
221 if not links:
222 raise ValueError("Branch has no Series-links value")
223
224 # Find the link without a version number (we don't support versions yet)
225 found = [link for link in links.split() if not ':' in link]
226 if not found:
227 raise ValueError('Series-links has no current version (without :)')
228
Simon Glassfcbec652020-11-03 13:54:16 -0700229 # Allow the series to override the URL
230 if 'patchwork_url' in series:
231 url = series.patchwork_url
232
Simon Glassdc6df972020-10-29 21:46:35 -0600233 # Import this here to avoid failing on other commands if the dependencies
234 # are not present
235 from patman import status
Simon Glassdc4b2a92020-10-29 21:46:38 -0600236 status.check_patchwork_status(series, found[0], branch, dest_branch, force,
Simon Glass7cbf02e2020-11-03 13:54:14 -0700237 show_comments, url)