Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(582)

Side by Side Diff: recipe_modules/tryserver/api.py

Issue 1644173002: Revert of depot_tools: import bot_update gclient git rietveld tryserver recipe modules (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « recipe_modules/tryserver/__init__.py ('k') | recipe_modules/tryserver/example.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import contextlib
6 import hashlib
7
8 from recipe_engine import recipe_api
9
10
11 PATCH_STORAGE_RIETVELD = 'rietveld'
12 PATCH_STORAGE_GIT = 'git'
13 PATCH_STORAGE_SVN = 'svn'
14
15
16 class TryserverApi(recipe_api.RecipeApi):
17 def __init__(self, *args, **kwargs):
18 super(TryserverApi, self).__init__(*args, **kwargs)
19 self._failure_reasons = []
20
21 @property
22 def patch_url(self):
23 """Reads patch_url property and corrects it if needed."""
24 url = self.m.properties.get('patch_url')
25 return url
26
27 @property
28 def is_tryserver(self):
29 """Returns true iff we can apply_issue or patch."""
30 return (self.can_apply_issue or self.is_patch_in_svn or
31 self.is_patch_in_git or self.is_gerrit_issue)
32
33 @property
34 def can_apply_issue(self):
35 """Returns true iff the properties exist to apply_issue from rietveld."""
36 return (self.m.properties.get('rietveld')
37 and 'issue' in self.m.properties
38 and 'patchset' in self.m.properties)
39
40 @property
41 def is_gerrit_issue(self):
42 """Returns true iff the properties exist to match a Gerrit issue."""
43 return ('event.patchSet.ref' in self.m.properties and
44 'event.change.url' in self.m.properties and
45 'event.change.id' in self.m.properties)
46
47 @property
48 def is_patch_in_svn(self):
49 """Returns true iff the properties exist to patch from a patch URL."""
50 return self.patch_url
51
52 @property
53 def is_patch_in_git(self):
54 return (self.m.properties.get('patch_storage') == PATCH_STORAGE_GIT and
55 self.m.properties.get('patch_repo_url') and
56 self.m.properties.get('patch_ref'))
57
58 def _apply_patch_step(self, patch_file=None, patch_content=None, root=None):
59 assert not (patch_file and patch_content), (
60 'Please only specify either patch_file or patch_content, not both!')
61 patch_cmd = [
62 'patch',
63 '--dir', root or self.m.path['checkout'],
64 '--force',
65 '--forward',
66 '--remove-empty-files',
67 '--strip', '0',
68 ]
69 if patch_file:
70 patch_cmd.extend(['--input', patch_file])
71
72 self.m.step('apply patch', patch_cmd,
73 stdin=patch_content)
74
75 def apply_from_svn(self, cwd):
76 """Downloads patch from patch_url using svn-export and applies it"""
77 # TODO(nodir): accept these properties as parameters
78 patch_url = self.patch_url
79 root = cwd
80 if root is None:
81 issue_root = self.m.rietveld.calculate_issue_root()
82 root = self.m.path['checkout'].join(issue_root)
83
84 patch_file = self.m.raw_io.output('.diff')
85 ext = '.bat' if self.m.platform.is_win else ''
86 svn_cmd = ['svn' + ext, 'export', '--force', patch_url, patch_file]
87
88 result = self.m.step('download patch', svn_cmd,
89 step_test_data=self.test_api.patch_content)
90 result.presentation.logs['patch.diff'] = (
91 result.raw_io.output.split('\n'))
92
93 patch_content = self.m.raw_io.input(result.raw_io.output)
94 self._apply_patch_step(patch_content=patch_content, root=root)
95
96 def apply_from_git(self, cwd):
97 """Downloads patch from given git repo and ref and applies it"""
98 # TODO(nodir): accept these properties as parameters
99 patch_repo_url = self.m.properties['patch_repo_url']
100 patch_ref = self.m.properties['patch_ref']
101
102 patch_dir = self.m.path.mkdtemp('patch')
103 git_setup_py = self.m.path['build'].join('scripts', 'slave', 'git_setup.py')
104 git_setup_args = ['--path', patch_dir, '--url', patch_repo_url]
105 patch_path = patch_dir.join('patch.diff')
106
107 self.m.python('patch git setup', git_setup_py, git_setup_args)
108 self.m.git('fetch', 'origin', patch_ref,
109 name='patch fetch', cwd=patch_dir)
110 self.m.git('clean', '-f', '-d', '-x',
111 name='patch clean', cwd=patch_dir)
112 self.m.git('checkout', '-f', 'FETCH_HEAD',
113 name='patch git checkout', cwd=patch_dir)
114 self._apply_patch_step(patch_file=patch_path, root=cwd)
115 self.m.step('remove patch', ['rm', '-rf', patch_dir])
116
117 def determine_patch_storage(self):
118 """Determines patch_storage automatically based on properties."""
119 storage = self.m.properties.get('patch_storage')
120 if storage:
121 return storage
122
123 if self.can_apply_issue:
124 return PATCH_STORAGE_RIETVELD
125 elif self.is_patch_in_svn:
126 return PATCH_STORAGE_SVN
127
128 def maybe_apply_issue(self, cwd=None, authentication=None):
129 """If we're a trybot, apply a codereview issue.
130
131 Args:
132 cwd: If specified, apply the patch from the specified directory.
133 authentication: authentication scheme whenever apply_issue.py is called.
134 This is only used if the patch comes from Rietveld. Possible values:
135 None, 'oauth2' (see also api.rietveld.apply_issue.)
136 """
137 storage = self.determine_patch_storage()
138
139 if storage == PATCH_STORAGE_RIETVELD:
140 return self.m.rietveld.apply_issue(
141 self.m.rietveld.calculate_issue_root(),
142 authentication=authentication)
143 elif storage == PATCH_STORAGE_SVN:
144 return self.apply_from_svn(cwd)
145 elif storage == PATCH_STORAGE_GIT:
146 return self.apply_from_git(cwd)
147 else:
148 # Since this method is "maybe", we don't raise an Exception.
149 pass
150
151 def get_files_affected_by_patch(self):
152 git_diff_kwargs = {}
153 issue_root = self.m.rietveld.calculate_issue_root()
154 if issue_root:
155 git_diff_kwargs['cwd'] = self.m.path['checkout'].join(issue_root)
156 step_result = self.m.git('diff', '--cached', '--name-only',
157 name='git diff to analyze patch',
158 stdout=self.m.raw_io.output(),
159 step_test_data=lambda:
160 self.m.raw_io.test_api.stream_output('foo.cc'),
161 **git_diff_kwargs)
162 paths = step_result.stdout.split()
163 if issue_root:
164 paths = [self.m.path.join(issue_root, path) for path in paths]
165 if self.m.platform.is_win:
166 # Looks like "analyze" wants POSIX slashes even on Windows (since git
167 # uses that format even on Windows).
168 paths = [path.replace('\\', '/') for path in paths]
169
170 step_result.presentation.logs['files'] = paths
171 return paths
172
173 def set_subproject_tag(self, subproject_tag):
174 """Adds a subproject tag to the build.
175
176 This can be used to distinguish between builds that execute different steps
177 depending on what was patched, e.g. blink vs. pure chromium patches.
178 """
179 assert self.is_tryserver
180
181 step_result = self.m.step.active_result
182 step_result.presentation.properties['subproject_tag'] = subproject_tag
183
184 def _set_failure_type(self, failure_type):
185 if not self.is_tryserver:
186 return
187
188 step_result = self.m.step.active_result
189 step_result.presentation.properties['failure_type'] = failure_type
190
191 def set_patch_failure_tryjob_result(self):
192 """Mark the tryjob result as failure to apply the patch."""
193 self._set_failure_type('PATCH_FAILURE')
194
195 def set_compile_failure_tryjob_result(self):
196 """Mark the tryjob result as a compile failure."""
197 self._set_failure_type('COMPILE_FAILURE')
198
199 def set_test_failure_tryjob_result(self):
200 """Mark the tryjob result as a test failure.
201
202 This means we started running actual tests (not prerequisite steps
203 like checkout or compile), and some of these tests have failed.
204 """
205 self._set_failure_type('TEST_FAILURE')
206
207 def set_invalid_test_results_tryjob_result(self):
208 """Mark the tryjob result as having invalid test results.
209
210 This means we run some tests, but the results were not valid
211 (e.g. no list of specific test cases that failed, or too many
212 tests failing, etc).
213 """
214 self._set_failure_type('INVALID_TEST_RESULTS')
215
216 def add_failure_reason(self, reason):
217 """
218 Records a more detailed reason why build is failing.
219
220 The reason can be any JSON-serializable object.
221 """
222 assert self.m.json.is_serializable(reason)
223 self._failure_reasons.append(reason)
224
225 @contextlib.contextmanager
226 def set_failure_hash(self):
227 """
228 Context manager that sets a failure_hash build property on StepFailure.
229
230 This can be used to easily compare whether two builds have failed
231 for the same reason. For example, if a patch is bad (breaks something),
232 we'd expect it to always break in the same way. Different failures
233 for the same patch are usually a sign of flakiness.
234 """
235 try:
236 yield
237 except self.m.step.StepFailure as e:
238 self.add_failure_reason(e.reason)
239
240 failure_hash = hashlib.sha1()
241 failure_hash.update(self.m.json.dumps(self._failure_reasons))
242
243 step_result = self.m.step.active_result
244 step_result.presentation.properties['failure_hash'] = \
245 failure_hash.hexdigest()
246
247 raise
OLDNEW
« no previous file with comments | « recipe_modules/tryserver/__init__.py ('k') | recipe_modules/tryserver/example.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698