OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Applies an issue from Rietveld. | 6 """Applies an issue from Rietveld. |
7 """ | 7 """ |
8 | 8 |
9 import getpass | 9 import getpass |
10 import json | 10 import json |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
44 sys.stdout = Unbuffered(sys.stdout) | 44 sys.stdout = Unbuffered(sys.stdout) |
45 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) | 45 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
46 parser.add_option( | 46 parser.add_option( |
47 '-v', '--verbose', action='count', default=0, | 47 '-v', '--verbose', action='count', default=0, |
48 help='Prints debugging infos') | 48 help='Prints debugging infos') |
49 parser.add_option( | 49 parser.add_option( |
50 '-e', '--email', | 50 '-e', '--email', |
51 help='Email address to access rietveld. If not specified, anonymous ' | 51 help='Email address to access rietveld. If not specified, anonymous ' |
52 'access will be used.') | 52 'access will be used.') |
53 parser.add_option( | 53 parser.add_option( |
54 '-w', '--password', default=None, | 54 '-E', '--email-file', |
55 help='Password for email addressed. Use - to read password from stdin.') | 55 help='File containing the email address to access rietveld. ' |
56 'If not specified, anonymous access will be used.') | |
57 parser.add_option( | |
58 '-w', '--password', | |
59 help='Password for email addressed. Use - to read password from stdin. ' | |
60 'Incompatible with -k') | |
61 parser.add_option( | |
62 '-k', '--private-key-file', | |
63 help='Path to file containing a private key in p12 format for OAuth2 ' | |
64 'authentication. Incompatible with -w.') | |
56 parser.add_option( | 65 parser.add_option( |
57 '-i', '--issue', type='int', help='Rietveld issue number') | 66 '-i', '--issue', type='int', help='Rietveld issue number') |
58 parser.add_option( | 67 parser.add_option( |
59 '-p', '--patchset', type='int', help='Rietveld issue\'s patchset number') | 68 '-p', '--patchset', type='int', help='Rietveld issue\'s patchset number') |
60 parser.add_option( | 69 parser.add_option( |
61 '-r', | 70 '-r', |
62 '--root_dir', | 71 '--root_dir', |
63 default=os.getcwd(), | 72 default=os.getcwd(), |
64 help='Root directory to apply the patch') | 73 help='Root directory to apply the patch') |
65 parser.add_option( | 74 parser.add_option( |
66 '-s', | 75 '-s', |
67 '--server', | 76 '--server', |
68 default='http://codereview.chromium.org', | 77 default='http://codereview.chromium.org', |
69 help='Rietveld server') | 78 help='Rietveld server') |
70 parser.add_option('--no-auth', action='store_true', | 79 parser.add_option('--no-auth', action='store_true', |
71 help='Do not attempt authenticated requests.') | 80 help='Do not attempt authenticated requests.') |
72 parser.add_option('--revision-mapping', default='{}', | 81 parser.add_option('--revision-mapping', default='{}', |
73 help='When running gclient, annotate the got_revisions ' | 82 help='When running gclient, annotate the got_revisions ' |
74 'using the revision-mapping.') | 83 'using the revision-mapping.') |
75 parser.add_option('-f', '--force', action='store_true', | 84 parser.add_option('-f', '--force', action='store_true', |
76 help='Really run apply_issue, even if .update.flag ' | 85 help='Really run apply_issue, even if .update.flag ' |
77 'is detected.') | 86 'is detected.') |
78 parser.add_option('-b', '--base_ref', help='Base git ref to patch on top of, ' | 87 parser.add_option('-b', '--base_ref', help='Base git ref to patch on top of, ' |
79 'used for verification.') | 88 'used for verification.') |
80 options, args = parser.parse_args() | 89 options, args = parser.parse_args() |
90 | |
91 if options.password and options.private_key_file: | |
92 parser.error('-k and -w options are incompatible') | |
93 if options.email and options.email_file: | |
94 parser.error('-e and -E options are incompatible') | |
95 | |
81 if (os.path.isfile(os.path.join(os.getcwd(), 'update.flag')) | 96 if (os.path.isfile(os.path.join(os.getcwd(), 'update.flag')) |
82 and not options.force): | 97 and not options.force): |
83 print 'update.flag file found: bot_update has run and checkout is already ' | 98 print 'update.flag file found: bot_update has run and checkout is already ' |
84 print 'in a consistent state. No actions will be performed in this step.' | 99 print 'in a consistent state. No actions will be performed in this step.' |
85 return 0 | 100 return 0 |
86 logging.basicConfig( | 101 logging.basicConfig( |
87 format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s', | 102 format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s', |
88 level=[logging.WARNING, logging.INFO, logging.DEBUG][ | 103 level=[logging.WARNING, logging.INFO, logging.DEBUG][ |
89 min(2, options.verbose)]) | 104 min(2, options.verbose)]) |
90 if args: | 105 if args: |
91 parser.error('Extra argument(s) "%s" not understood' % ' '.join(args)) | 106 parser.error('Extra argument(s) "%s" not understood' % ' '.join(args)) |
92 if not options.issue: | 107 if not options.issue: |
93 parser.error('Require --issue') | 108 parser.error('Require --issue') |
94 options.server = options.server.rstrip('/') | 109 options.server = options.server.rstrip('/') |
95 if not options.server: | 110 if not options.server: |
96 parser.error('Require a valid server') | 111 parser.error('Require a valid server') |
97 | 112 |
98 options.revision_mapping = json.loads(options.revision_mapping) | 113 options.revision_mapping = json.loads(options.revision_mapping) |
99 | 114 |
100 if options.password == '-': | 115 if options.password == '-': |
101 print('Reading password') | 116 print('Reading password') |
102 options.password = sys.stdin.readline().strip() | 117 options.password = sys.stdin.readline().strip() |
103 | 118 |
119 # read email if needed | |
120 if options.email_file: | |
121 if not os.path.exists(options.email_file): | |
122 parser.error('file does not exist: %s' % options.email_file) | |
123 with open(options.email_file, 'rb') as f: | |
124 options.email = f.read().strip() | |
125 | |
104 print('Connecting to %s' % options.server) | 126 print('Connecting to %s' % options.server) |
105 # Always try un-authenticated first. | 127 # Always try un-authenticated first, except for OAuth2 |
106 # TODO(maruel): Use OAuth2 properly so we don't hit rate-limiting on login | 128 if options.private_key_file: |
107 # attempts. | 129 # OAuth2 authentication |
108 # Bad except clauses order (HTTPError is an ancestor class of | 130 obj = rietveld.JwtOAuth2Rietveld(options.server, |
109 # ClientLoginError) | 131 options.email, |
Vadim Sh.
2014/03/14 01:55:35
align indentation
| |
110 # pylint: disable=E0701 | 132 options.private_key_file, |
111 obj = rietveld.Rietveld(options.server, '', None) | 133 private_key_password=options.password) |
112 properties = None | |
113 try: | |
114 properties = obj.get_issue_properties(options.issue, False) | 134 properties = obj.get_issue_properties(options.issue, False) |
115 except urllib2.HTTPError, e: | 135 else: |
116 if e.getcode() != 302: | 136 obj = rietveld.Rietveld(options.server, '', None) |
117 raise | 137 properties = None |
118 elif options.no_auth: | 138 # Bad except clauses order (HTTPError is an ancestor class of |
119 exit('FAIL: Login detected -- is issue private?') | 139 # ClientLoginError) |
120 # TODO(maruel): A few 'Invalid username or password.' are printed first, we | 140 # pylint: disable=E0701 |
121 # should get rid of those. | 141 try: |
122 except rietveld.upload.ClientLoginError, e: | 142 properties = obj.get_issue_properties(options.issue, False) |
123 # Fine, we'll do proper authentication. | 143 except urllib2.HTTPError, e: |
124 pass | 144 if e.getcode() != 302: |
125 if properties is None: | 145 raise |
126 if options.email is not None: | 146 elif options.no_auth: |
127 obj = rietveld.Rietveld(options.server, options.email, options.password) | 147 exit('FAIL: Login detected -- is issue private?') |
128 try: | 148 # TODO(maruel): A few 'Invalid username or password.' are printed first, |
129 properties = obj.get_issue_properties(options.issue, False) | 149 # we should get rid of those. |
130 except rietveld.upload.ClientLoginError, e: | 150 except rietveld.upload.ClientLoginError, e: |
131 if sys.stdout.closed: | 151 # Fine, we'll do proper authentication. |
152 pass | |
153 if properties is None: | |
154 if options.email is not None: | |
155 obj = rietveld.Rietveld(options.server, options.email, options.password) | |
156 try: | |
157 properties = obj.get_issue_properties(options.issue, False) | |
158 except rietveld.upload.ClientLoginError, e: | |
159 if sys.stdout.closed: | |
160 print('Accessing the issue requires proper credentials.') | |
161 return 1 | |
162 else: | |
163 print('Accessing the issue requires login.') | |
164 obj = rietveld.Rietveld(options.server, None, None) | |
165 try: | |
166 properties = obj.get_issue_properties(options.issue, False) | |
167 except rietveld.upload.ClientLoginError, e: | |
132 print('Accessing the issue requires proper credentials.') | 168 print('Accessing the issue requires proper credentials.') |
133 return 1 | 169 return 1 |
134 else: | |
135 print('Accessing the issue requires login.') | |
136 obj = rietveld.Rietveld(options.server, None, None) | |
137 try: | |
138 properties = obj.get_issue_properties(options.issue, False) | |
139 except rietveld.upload.ClientLoginError, e: | |
140 print('Accessing the issue requires proper credentials.') | |
141 return 1 | |
142 | 170 |
143 if not options.patchset: | 171 if not options.patchset: |
144 options.patchset = properties['patchsets'][-1] | 172 options.patchset = properties['patchsets'][-1] |
145 print('No patchset specified. Using patchset %d' % options.patchset) | 173 print('No patchset specified. Using patchset %d' % options.patchset) |
146 | 174 |
147 print('Downloading the patch.') | 175 print('Downloading the patch.') |
148 try: | 176 try: |
149 patchset = obj.get_patch(options.issue, options.patchset) | 177 patchset = obj.get_patch(options.issue, options.patchset) |
150 except urllib2.HTTPError, e: | 178 except urllib2.HTTPError, e: |
151 print( | 179 print( |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
216 f, options.revision_mapping) | 244 f, options.revision_mapping) |
217 annotated_gclient.emit_buildprops(revisions) | 245 annotated_gclient.emit_buildprops(revisions) |
218 | 246 |
219 return retcode | 247 return retcode |
220 return 0 | 248 return 0 |
221 | 249 |
222 | 250 |
223 if __name__ == "__main__": | 251 if __name__ == "__main__": |
224 fix_encoding.fix_encoding() | 252 fix_encoding.fix_encoding() |
225 sys.exit(main()) | 253 sys.exit(main()) |
OLD | NEW |