]>
Commit | Line | Data |
---|---|---|
94b13bba | 1 | #!/usr/bin/env python2 |
0d24de9d SG |
2 | # |
3 | # Copyright (c) 2011 The Chromium OS Authors. | |
4 | # | |
1a459660 | 5 | # SPDX-License-Identifier: GPL-2.0+ |
0d24de9d SG |
6 | # |
7 | ||
8 | """See README for more information""" | |
9 | ||
10 | from optparse import OptionParser | |
11 | import os | |
12 | import re | |
13 | import sys | |
14 | import unittest | |
15 | ||
16 | # Our modules | |
488d19cb CP |
17 | try: |
18 | from patman import checkpatch, command, gitutil, patchstream, \ | |
19 | project, settings, terminal, test | |
20 | except ImportError: | |
21 | import checkpatch | |
22 | import command | |
23 | import gitutil | |
24 | import patchstream | |
25 | import project | |
26 | import settings | |
27 | import terminal | |
28 | import test | |
0d24de9d SG |
29 | |
30 | ||
31 | parser = OptionParser() | |
32 | parser.add_option('-H', '--full-help', action='store_true', dest='full_help', | |
33 | default=False, help='Display the README file') | |
34 | parser.add_option('-c', '--count', dest='count', type='int', | |
35 | default=-1, help='Automatically create patches from top n commits') | |
36 | parser.add_option('-i', '--ignore-errors', action='store_true', | |
37 | dest='ignore_errors', default=False, | |
38 | help='Send patches email even if patch errors are found') | |
983a2749 SG |
39 | parser.add_option('-m', '--no-maintainers', action='store_false', |
40 | dest='add_maintainers', default=True, | |
41 | help="Don't cc the file maintainers automatically") | |
0d24de9d | 42 | parser.add_option('-n', '--dry-run', action='store_true', dest='dry_run', |
ca706e76 | 43 | default=False, help="Do a dry run (create but don't email patches)") |
99adf6ed VB |
44 | parser.add_option('-p', '--project', default=project.DetectProject(), |
45 | help="Project name; affects default option values and " | |
46 | "aliases [default: %default]") | |
6d819925 DA |
47 | parser.add_option('-r', '--in-reply-to', type='string', action='store', |
48 | help="Message ID that this series is in reply to") | |
0d24de9d SG |
49 | parser.add_option('-s', '--start', dest='start', type='int', |
50 | default=0, help='Commit to start creating patches from (0 = HEAD)') | |
a1318f7c SG |
51 | parser.add_option('-t', '--ignore-bad-tags', action='store_true', |
52 | default=False, help='Ignore bad tags / aliases') | |
53 | parser.add_option('--test', action='store_true', dest='test', | |
0d24de9d SG |
54 | default=False, help='run tests') |
55 | parser.add_option('-v', '--verbose', action='store_true', dest='verbose', | |
56 | default=False, help='Verbose output of errors and warnings') | |
57 | parser.add_option('--cc-cmd', dest='cc_cmd', type='string', action='store', | |
58 | default=None, help='Output cc list for patch file (used by git)') | |
99adf6ed VB |
59 | parser.add_option('--no-check', action='store_false', dest='check_patch', |
60 | default=True, | |
61 | help="Don't check for patch compliance") | |
0d24de9d SG |
62 | parser.add_option('--no-tags', action='store_false', dest='process_tags', |
63 | default=True, help="Don't process subject tags as aliaes") | |
27067a46 MK |
64 | parser.add_option('-T', '--thread', action='store_true', dest='thread', |
65 | default=False, help='Create patches as a single thread') | |
0d24de9d | 66 | |
e0a4d06a | 67 | parser.usage += """ |
0d24de9d SG |
68 | |
69 | Create patches from commits in a branch, check them and email them as | |
ca706e76 | 70 | specified by tags you place in the commits. Use -n to do a dry run first.""" |
0d24de9d | 71 | |
8568baed | 72 | |
a1dcee84 DA |
73 | # Parse options twice: first to get the project and second to handle |
74 | # defaults properly (which depends on project). | |
75 | (options, args) = parser.parse_args() | |
76 | settings.Setup(parser, options.project, '') | |
0d24de9d SG |
77 | (options, args) = parser.parse_args() |
78 | ||
9649e152 SG |
79 | if __name__ != "__main__": |
80 | pass | |
81 | ||
0d24de9d | 82 | # Run our meagre tests |
9649e152 | 83 | elif options.test: |
0d24de9d | 84 | import doctest |
6e87ae1c | 85 | import func_test |
0d24de9d SG |
86 | |
87 | sys.argv = [sys.argv[0]] | |
0d24de9d | 88 | result = unittest.TestResult() |
6e87ae1c SG |
89 | for module in (test.TestPatch, func_test.TestFunctional): |
90 | suite = unittest.TestLoader().loadTestsFromTestCase(module) | |
91 | suite.run(result) | |
0d24de9d | 92 | |
656cffeb DA |
93 | for module in ['gitutil', 'settings']: |
94 | suite = doctest.DocTestSuite(module) | |
95 | suite.run(result) | |
0d24de9d SG |
96 | |
97 | # TODO: Surely we can just 'print' result? | |
a920a17b | 98 | print(result) |
0d24de9d | 99 | for test, err in result.errors: |
a920a17b | 100 | print(err) |
0d24de9d | 101 | for test, err in result.failures: |
a920a17b | 102 | print(err) |
0d24de9d SG |
103 | |
104 | # Called from git with a patch filename as argument | |
105 | # Printout a list of additional CC recipients for this patch | |
106 | elif options.cc_cmd: | |
107 | fd = open(options.cc_cmd, 'r') | |
108 | re_line = re.compile('(\S*) (.*)') | |
109 | for line in fd.readlines(): | |
110 | match = re_line.match(line) | |
111 | if match and match.group(1) == args[0]: | |
112 | for cc in match.group(2).split(', '): | |
113 | cc = cc.strip() | |
114 | if cc: | |
a920a17b | 115 | print(cc) |
0d24de9d SG |
116 | fd.close() |
117 | ||
118 | elif options.full_help: | |
119 | pager = os.getenv('PAGER') | |
120 | if not pager: | |
121 | pager = 'more' | |
2bdeade0 SG |
122 | fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), |
123 | 'README') | |
0d24de9d SG |
124 | command.Run(pager, fname) |
125 | ||
126 | # Process commits, produce patches files, check them, email them | |
127 | else: | |
128 | gitutil.Setup() | |
129 | ||
130 | if options.count == -1: | |
131 | # Work out how many patches to send if we can | |
132 | options.count = gitutil.CountCommitsToBranch() - options.start | |
133 | ||
134 | col = terminal.Color() | |
135 | if not options.count: | |
136 | str = 'No commits found to process - please use -c flag' | |
31e2141d | 137 | sys.exit(col.Color(col.RED, str)) |
0d24de9d SG |
138 | |
139 | # Read the metadata from the commits | |
140 | if options.count: | |
141 | series = patchstream.GetMetaData(options.start, options.count) | |
142 | cover_fname, args = gitutil.CreatePatches(options.start, options.count, | |
143 | series) | |
144 | ||
145 | # Fix up the patch files to our liking, and insert the cover letter | |
db116cc8 SG |
146 | patchstream.FixPatches(series, args) |
147 | if cover_fname and series.get('cover'): | |
0d24de9d SG |
148 | patchstream.InsertCoverLetter(cover_fname, series, options.count) |
149 | ||
150 | # Do a few checks on the series | |
151 | series.DoChecks() | |
152 | ||
153 | # Check the patches, and run them through 'git am' just to be sure | |
99adf6ed VB |
154 | if options.check_patch: |
155 | ok = checkpatch.CheckPatches(options.verbose, args) | |
156 | else: | |
157 | ok = True | |
0d24de9d | 158 | |
a1318f7c | 159 | cc_file = series.MakeCcFile(options.process_tags, cover_fname, |
983a2749 SG |
160 | not options.ignore_bad_tags, |
161 | options.add_maintainers) | |
d94566a1 | 162 | |
0d24de9d SG |
163 | # Email the patches out (giving the user time to check / cancel) |
164 | cmd = '' | |
1f727885 VB |
165 | its_a_go = ok or options.ignore_errors |
166 | if its_a_go: | |
0d24de9d | 167 | cmd = gitutil.EmailPatches(series, cover_fname, args, |
a1318f7c | 168 | options.dry_run, not options.ignore_bad_tags, cc_file, |
27067a46 | 169 | in_reply_to=options.in_reply_to, thread=options.thread) |
1f727885 | 170 | else: |
a920a17b | 171 | print(col.Color(col.RED, "Not sending emails due to errors/warnings")) |
0d24de9d SG |
172 | |
173 | # For a dry run, just show our actions as a sanity check | |
174 | if options.dry_run: | |
175 | series.ShowActions(args, cmd, options.process_tags) | |
1f727885 | 176 | if not its_a_go: |
a920a17b | 177 | print(col.Color(col.RED, "Email would not be sent")) |
d94566a1 DA |
178 | |
179 | os.remove(cc_file) |