-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapply_issue.py
executable file
·265 lines (237 loc) · 9.46 KB
/
apply_issue.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Applies an issue from Rietveld.
"""
import getpass
import json
import logging
import optparse
import os
import subprocess
import sys
import urllib2
import breakpad # pylint: disable=W0611
import annotated_gclient
import checkout
import fix_encoding
import gclient_utils
import rietveld
import scm
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Unbuffered(object):
"""Disable buffering on a file object."""
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.write(data)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
def main():
# TODO(pgervais): This function is way too long. Split.
sys.stdout = Unbuffered(sys.stdout)
parser = optparse.OptionParser(description=sys.modules[__name__].__doc__)
parser.add_option(
'-v', '--verbose', action='count', default=0,
help='Prints debugging infos')
parser.add_option(
'-e', '--email',
help='Email address to access rietveld. If not specified, anonymous '
'access will be used.')
parser.add_option(
'-E', '--email-file',
help='File containing the email address to access rietveld. '
'If not specified, anonymous access will be used.')
parser.add_option(
'-w', '--password',
help='Password for email addressed. Use - to read password from stdin. '
'if -k is provided, this is the private key file password.')
parser.add_option(
'-k', '--private-key-file',
help='Path to file containing a private key in p12 format for OAuth2 '
'authentication. Use -w to provide the decrypting password, if any.')
parser.add_option(
'-i', '--issue', type='int', help='Rietveld issue number')
parser.add_option(
'-p', '--patchset', type='int', help='Rietveld issue\'s patchset number')
parser.add_option(
'-r',
'--root_dir',
default=os.getcwd(),
help='Root directory to apply the patch')
parser.add_option(
'-s',
'--server',
default='http://codereview.chromium.org',
help='Rietveld server')
parser.add_option('--no-auth', action='store_true',
help='Do not attempt authenticated requests.')
parser.add_option('--revision-mapping', default='{}',
help='When running gclient, annotate the got_revisions '
'using the revision-mapping.')
parser.add_option('-f', '--force', action='store_true',
help='Really run apply_issue, even if .update.flag '
'is detected.')
parser.add_option('-b', '--base_ref', help='DEPRECATED do not use.')
parser.add_option('--whitelist', action='append', default=[],
help='Patch only specified file(s).')
parser.add_option('--blacklist', action='append', default=[],
help='Don\'t patch specified file(s).')
parser.add_option('-d', '--ignore_deps', action='store_true',
help='Don\'t run gclient sync on DEPS changes.')
options, args = parser.parse_args()
if options.whitelist and options.blacklist:
parser.error('Cannot specify both --whitelist and --blacklist')
if options.password and options.private_key_file:
parser.error('-k and -w options are incompatible')
if options.email and options.email_file:
parser.error('-e and -E options are incompatible')
if (os.path.isfile(os.path.join(os.getcwd(), 'update.flag'))
and not options.force):
print 'update.flag file found: bot_update has run and checkout is already '
print 'in a consistent state. No actions will be performed in this step.'
return 0
logging.basicConfig(
format='%(levelname)5s %(module)11s(%(lineno)4d): %(message)s',
level=[logging.WARNING, logging.INFO, logging.DEBUG][
min(2, options.verbose)])
if args:
parser.error('Extra argument(s) "%s" not understood' % ' '.join(args))
if not options.issue:
parser.error('Require --issue')
options.server = options.server.rstrip('/')
if not options.server:
parser.error('Require a valid server')
options.revision_mapping = json.loads(options.revision_mapping)
if options.password == '-':
print('Reading password')
options.password = sys.stdin.readline().strip()
# read email if needed
if options.email_file:
if not os.path.exists(options.email_file):
parser.error('file does not exist: %s' % options.email_file)
with open(options.email_file, 'rb') as f:
options.email = f.read().strip()
print('Connecting to %s' % options.server)
# Always try un-authenticated first, except for OAuth2
if options.private_key_file:
# OAuth2 authentication
obj = rietveld.JwtOAuth2Rietveld(options.server,
options.email,
options.private_key_file,
private_key_password=options.password)
properties = obj.get_issue_properties(options.issue, False)
else:
obj = rietveld.Rietveld(options.server, '', None)
properties = None
# Bad except clauses order (HTTPError is an ancestor class of
# ClientLoginError)
# pylint: disable=E0701
try:
properties = obj.get_issue_properties(options.issue, False)
except urllib2.HTTPError as e:
if e.getcode() != 302:
raise
if options.no_auth:
exit('FAIL: Login detected -- is issue private?')
# TODO(maruel): A few 'Invalid username or password.' are printed first,
# we should get rid of those.
except rietveld.upload.ClientLoginError, e:
# Fine, we'll do proper authentication.
pass
if properties is None:
if options.email is not None:
obj = rietveld.Rietveld(options.server, options.email, options.password)
try:
properties = obj.get_issue_properties(options.issue, False)
except rietveld.upload.ClientLoginError, e:
if sys.stdout.closed:
print('Accessing the issue requires proper credentials.')
return 1
else:
print('Accessing the issue requires login.')
obj = rietveld.Rietveld(options.server, None, None)
try:
properties = obj.get_issue_properties(options.issue, False)
except rietveld.upload.ClientLoginError, e:
print('Accessing the issue requires proper credentials.')
return 1
if not options.patchset:
options.patchset = properties['patchsets'][-1]
print('No patchset specified. Using patchset %d' % options.patchset)
print('Downloading the patch.')
try:
patchset = obj.get_patch(options.issue, options.patchset)
except urllib2.HTTPError, e:
print(
'Failed to fetch the patch for issue %d, patchset %d.\n'
'Try visiting %s/%d') % (
options.issue, options.patchset,
options.server, options.issue)
return 1
if options.whitelist:
patchset.patches = [patch for patch in patchset.patches
if patch.filename in options.whitelist]
if options.blacklist:
patchset.patches = [patch for patch in patchset.patches
if patch.filename not in options.blacklist]
for patch in patchset.patches:
print(patch)
full_dir = os.path.abspath(options.root_dir)
scm_type = scm.determine_scm(full_dir)
if scm_type == 'svn':
scm_obj = checkout.SvnCheckout(full_dir, None, None, None, None)
elif scm_type == 'git':
scm_obj = checkout.GitCheckout(full_dir, None, None, None, None)
elif scm_type == None:
scm_obj = checkout.RawCheckout(full_dir, None, None)
else:
parser.error('Couldn\'t determine the scm')
# TODO(maruel): HACK, remove me.
# When run a build slave, make sure buildbot knows that the checkout was
# modified.
if options.root_dir == 'src' and getpass.getuser() == 'chrome-bot':
# See sourcedirIsPatched() in:
# http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/slave/
# chromium_commands.py?view=markup
open('.buildbot-patched', 'w').close()
print('\nApplying the patch.')
try:
scm_obj.apply_patch(patchset, verbose=True)
except checkout.PatchApplicationFailed, e:
print(str(e))
print('CWD=%s' % os.getcwd())
print('Checkout path=%s' % scm_obj.project_path)
return 1
if ('DEPS' in map(os.path.basename, patchset.filenames)
and not options.ignore_deps):
gclient_root = gclient_utils.FindGclientRoot(full_dir)
if gclient_root and scm_type:
print(
'A DEPS file was updated inside a gclient checkout, running gclient '
'sync.')
gclient_path = os.path.join(BASE_DIR, 'gclient')
if sys.platform == 'win32':
gclient_path += '.bat'
with annotated_gclient.temp_filename(suffix='gclient') as f:
cmd = [
gclient_path, 'sync',
'--nohooks',
'--delete_unversioned_trees',
]
if scm_type == 'svn':
cmd.extend(['--revision', 'BASE'])
if options.revision_mapping:
cmd.extend(['--output-json', f])
retcode = subprocess.call(cmd, cwd=gclient_root)
if retcode == 0 and options.revision_mapping:
revisions = annotated_gclient.parse_got_revision(
f, options.revision_mapping)
annotated_gclient.emit_buildprops(revisions)
return retcode
return 0
if __name__ == "__main__":
fix_encoding.fix_encoding()
sys.exit(main())