patman: Quieten down the alias checking
[platform/kernel/u-boot.git] / tools / patman / main.py
1 #!/usr/bin/env python3
2 # SPDX-License-Identifier: GPL-2.0+
3 #
4 # Copyright (c) 2011 The Chromium OS Authors.
5 #
6
7 """See README for more information"""
8
9 from argparse import ArgumentParser
10 import os
11 import re
12 import sys
13 import traceback
14 import unittest
15
16 if __name__ == "__main__":
17     # Allow 'from patman import xxx to work'
18     our_path = os.path.dirname(os.path.realpath(__file__))
19     sys.path.append(os.path.join(our_path, '..'))
20
21 # Our modules
22 from patman import command
23 from patman import control
24 from patman import gitutil
25 from patman import project
26 from patman import settings
27 from patman import terminal
28 from patman import test_util
29 from patman import test_checkpatch
30
31 epilog = '''Create patches from commits in a branch, check them and email them
32 as specified by tags you place in the commits. Use -n to do a dry run first.'''
33
34 parser = ArgumentParser(epilog=epilog)
35 parser.add_argument('-b', '--branch', type=str,
36     help="Branch to process (by default, the current branch)")
37 parser.add_argument('-c', '--count', dest='count', type=int,
38     default=-1, help='Automatically create patches from top n commits')
39 parser.add_argument('-e', '--end', type=int, default=0,
40     help='Commits to skip at end of patch list')
41 parser.add_argument('-D', '--debug', action='store_true',
42     help='Enabling debugging (provides a full traceback on error)')
43 parser.add_argument('-p', '--project', default=project.DetectProject(),
44                     help="Project name; affects default option values and "
45                     "aliases [default: %(default)s]")
46 parser.add_argument('-P', '--patchwork-url',
47                     default='https://patchwork.ozlabs.org',
48                     help='URL of patchwork server [default: %(default)s]')
49 parser.add_argument('-s', '--start', dest='start', type=int,
50     default=0, help='Commit to start creating patches from (0 = HEAD)')
51 parser.add_argument('-v', '--verbose', action='store_true', dest='verbose',
52                     default=False, help='Verbose output of errors and warnings')
53 parser.add_argument('-H', '--full-help', action='store_true', dest='full_help',
54                     default=False, help='Display the README file')
55
56 subparsers = parser.add_subparsers(dest='cmd')
57 send = subparsers.add_parser('send')
58 send.add_argument('-i', '--ignore-errors', action='store_true',
59        dest='ignore_errors', default=False,
60        help='Send patches email even if patch errors are found')
61 send.add_argument('-l', '--limit-cc', dest='limit', type=int, default=None,
62        help='Limit the cc list to LIMIT entries [default: %(default)s]')
63 send.add_argument('-m', '--no-maintainers', action='store_false',
64        dest='add_maintainers', default=True,
65        help="Don't cc the file maintainers automatically")
66 send.add_argument('-n', '--dry-run', action='store_true', dest='dry_run',
67        default=False, help="Do a dry run (create but don't email patches)")
68 send.add_argument('-r', '--in-reply-to', type=str, action='store',
69                   help="Message ID that this series is in reply to")
70 send.add_argument('-t', '--ignore-bad-tags', action='store_true',
71                   default=False,
72                   help='Ignore bad tags / aliases (default=warn)')
73 send.add_argument('-T', '--thread', action='store_true', dest='thread',
74                   default=False, help='Create patches as a single thread')
75 send.add_argument('--cc-cmd', dest='cc_cmd', type=str, action='store',
76        default=None, help='Output cc list for patch file (used by git)')
77 send.add_argument('--no-binary', action='store_true', dest='ignore_binary',
78                   default=False,
79                   help="Do not output contents of changes in binary files")
80 send.add_argument('--no-check', action='store_false', dest='check_patch',
81                   default=True,
82                   help="Don't check for patch compliance")
83 send.add_argument('--no-tags', action='store_false', dest='process_tags',
84                   default=True, help="Don't process subject tags as aliases")
85 send.add_argument('--no-signoff', action='store_false', dest='add_signoff',
86                   default=True, help="Don't add Signed-off-by to patches")
87 send.add_argument('--smtp-server', type=str,
88                   help="Specify the SMTP server to 'git send-email'")
89
90 send.add_argument('patchfiles', nargs='*')
91
92 test_parser = subparsers.add_parser('test', help='Run tests')
93 test_parser.add_argument('testname', type=str, default=None, nargs='?',
94                          help="Specify the test to run")
95
96 status = subparsers.add_parser('status',
97                                help='Check status of patches in patchwork')
98 status.add_argument('-C', '--show-comments', action='store_true',
99                     help='Show comments from each patch')
100 status.add_argument('-d', '--dest-branch', type=str,
101                     help='Name of branch to create with collected responses')
102 status.add_argument('-f', '--force', action='store_true',
103                     help='Force overwriting an existing branch')
104
105 # Parse options twice: first to get the project and second to handle
106 # defaults properly (which depends on project)
107 # Use parse_known_args() in case 'cmd' is omitted
108 argv = sys.argv[1:]
109 args, rest = parser.parse_known_args(argv)
110 if hasattr(args, 'project'):
111     settings.Setup(gitutil, parser, args.project, '')
112     args, rest = parser.parse_known_args(argv)
113
114 # If we have a command, it is safe to parse all arguments
115 if args.cmd:
116     args = parser.parse_args(argv)
117 else:
118     # No command, so insert it after the known arguments and before the ones
119     # that presumably relate to the 'send' subcommand
120     nargs = len(rest)
121     argv = argv[:-nargs] + ['send'] + rest
122     args = parser.parse_args(argv)
123
124 if __name__ != "__main__":
125     pass
126
127 if not args.debug:
128     sys.tracebacklimit = 0
129
130 # Run our meagre tests
131 if args.cmd == 'test':
132     import doctest
133     from patman import func_test
134
135     sys.argv = [sys.argv[0]]
136     result = unittest.TestResult()
137     suite = unittest.TestSuite()
138     loader = unittest.TestLoader()
139     for module in (test_checkpatch.TestPatch, func_test.TestFunctional):
140         if args.testname:
141             try:
142                 suite.addTests(loader.loadTestsFromName(args.testname, module))
143             except AttributeError:
144                 continue
145         else:
146             suite.addTests(loader.loadTestsFromTestCase(module))
147     suite.run(result)
148
149     for module in ['gitutil', 'settings', 'terminal']:
150         suite = doctest.DocTestSuite(module)
151         suite.run(result)
152
153     sys.exit(test_util.ReportResult('patman', args.testname, result))
154
155 # Process commits, produce patches files, check them, email them
156 elif args.cmd == 'send':
157     # Called from git with a patch filename as argument
158     # Printout a list of additional CC recipients for this patch
159     if args.cc_cmd:
160         fd = open(args.cc_cmd, 'r')
161         re_line = re.compile('(\S*) (.*)')
162         for line in fd.readlines():
163             match = re_line.match(line)
164             if match and match.group(1) == args.patchfiles[0]:
165                 for cc in match.group(2).split('\0'):
166                     cc = cc.strip()
167                     if cc:
168                         print(cc)
169         fd.close()
170
171     elif args.full_help:
172         pager = os.getenv('PAGER')
173         if not pager:
174             pager = 'more'
175         fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
176                              'README')
177         command.Run(pager, fname)
178
179     else:
180         # If we are not processing tags, no need to warning about bad ones
181         if not args.process_tags:
182             args.ignore_bad_tags = True
183         control.send(args)
184
185 # Check status of patches in patchwork
186 elif args.cmd == 'status':
187     ret_code = 0
188     try:
189         control.patchwork_status(args.branch, args.count, args.start, args.end,
190                                  args.dest_branch, args.force,
191                                  args.show_comments, args.patchwork_url)
192     except Exception as e:
193         terminal.Print('patman: %s: %s' % (type(e).__name__, e),
194                        colour=terminal.Color.RED)
195         if args.debug:
196             print()
197             traceback.print_exc()
198         ret_code = 1
199     sys.exit(ret_code)