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

Side by Side Diff: infra/recipe_modules/gclient/api.py

Issue 1651323002: Revert of Adds bot_update to depot_tools. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Update .gitignore for safety 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 | « infra/recipe_modules/gclient/__init__.py ('k') | infra/recipe_modules/gclient/config.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 2013 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 from recipe_engine import recipe_api
6
7
8 class RevisionResolver(object):
9 """Resolves the revision based on build properties."""
10
11 def resolve(self, properties): # pragma: no cover
12 raise NotImplementedError()
13
14
15 class RevisionFallbackChain(RevisionResolver):
16 """Specify that a given project's sync revision follows the fallback chain."""
17 def __init__(self, default=None):
18 self._default = default
19
20 def resolve(self, properties):
21 """Resolve the revision via the revision fallback chain.
22
23 If the given revision was set using the revision_fallback_chain() function,
24 this function will follow the chain, looking at relevant build properties
25 until it finds one set or reaches the end of the chain and returns the
26 default. If the given revision was not set using revision_fallback_chain(),
27 this function just returns it as-is.
28 """
29 return (properties.get('parent_got_revision') or
30 properties.get('orig_revision') or
31 properties.get('revision') or
32 self._default)
33
34
35 def jsonish_to_python(spec, is_top=False):
36 ret = ''
37 if is_top: # We're the 'top' level, so treat this dict as a suite.
38 ret = '\n'.join(
39 '%s = %s' % (k, jsonish_to_python(spec[k])) for k in sorted(spec)
40 )
41 else:
42 if isinstance(spec, dict):
43 ret += '{'
44 ret += ', '.join(
45 "%s: %s" % (repr(str(k)), jsonish_to_python(spec[k]))
46 for k in sorted(spec)
47 )
48 ret += '}'
49 elif isinstance(spec, list):
50 ret += '['
51 ret += ', '.join(jsonish_to_python(x) for x in spec)
52 ret += ']'
53 elif isinstance(spec, basestring):
54 ret = repr(str(spec))
55 else:
56 ret = repr(spec)
57 return ret
58
59 class GclientApi(recipe_api.RecipeApi):
60 # Singleton object to indicate to checkout() that we should run a revert if
61 # we detect that we're on the tryserver.
62 RevertOnTryserver = object()
63
64 def __init__(self, **kwargs):
65 super(GclientApi, self).__init__(**kwargs)
66 self.USE_MIRROR = None
67 self._spec_alias = None
68
69 def __call__(self, name, cmd, infra_step=True, **kwargs):
70 """Wrapper for easy calling of gclient steps."""
71 assert isinstance(cmd, (list, tuple))
72 prefix = 'gclient '
73 if self.spec_alias:
74 prefix = ('[spec: %s] ' % self.spec_alias) + prefix
75
76 return self.m.python(prefix + name,
77 self.m.depot_tools.gclient_py,
78 cmd,
79 infra_step=infra_step,
80 **kwargs)
81
82 @property
83 def use_mirror(self):
84 """Indicates if gclient will use mirrors in its configuration."""
85 if self.USE_MIRROR is None:
86 self.USE_MIRROR = self.m.properties.get('use_mirror', True)
87 return self.USE_MIRROR
88
89 @use_mirror.setter
90 def use_mirror(self, val): # pragma: no cover
91 self.USE_MIRROR = val
92
93 @property
94 def spec_alias(self):
95 """Optional name for the current spec for step naming."""
96 return self._spec_alias
97
98 @spec_alias.setter
99 def spec_alias(self, name):
100 self._spec_alias = name
101
102 @spec_alias.deleter
103 def spec_alias(self):
104 self._spec_alias = None
105
106 def get_config_defaults(self):
107 ret = {
108 'USE_MIRROR': self.use_mirror
109 }
110 ret['CACHE_DIR'] = self.m.path['root'].join('git_cache')
111 return ret
112
113 def resolve_revision(self, revision):
114 if hasattr(revision, 'resolve'):
115 return revision.resolve(self.m.properties)
116 return revision
117
118 def sync(self, cfg, with_branch_heads=False, **kwargs):
119 revisions = []
120 for i, s in enumerate(cfg.solutions):
121 if s.safesync_url: # prefer safesync_url in gclient mode
122 continue
123 if i == 0 and s.revision is None:
124 s.revision = RevisionFallbackChain()
125
126 if s.revision is not None and s.revision != '':
127 fixed_revision = self.resolve_revision(s.revision)
128 if fixed_revision:
129 revisions.extend(['--revision', '%s@%s' % (s.name, fixed_revision)])
130
131 for name, revision in sorted(cfg.revisions.items()):
132 fixed_revision = self.resolve_revision(revision)
133 if fixed_revision:
134 revisions.extend(['--revision', '%s@%s' % (name, fixed_revision)])
135
136 test_data_paths = set(cfg.got_revision_mapping.keys() +
137 [s.name for s in cfg.solutions])
138 step_test_data = lambda: (
139 self.test_api.output_json(test_data_paths, cfg.GIT_MODE))
140 try:
141 if not cfg.GIT_MODE:
142 args = ['sync', '--nohooks', '--force', '--verbose']
143 if cfg.delete_unversioned_trees:
144 args.append('--delete_unversioned_trees')
145 if with_branch_heads:
146 args.append('--with_branch_heads')
147 self('sync', args + revisions + ['--output-json', self.m.json.output()],
148 step_test_data=step_test_data,
149 **kwargs)
150 else:
151 # clean() isn't used because the gclient sync flags passed in checkout()
152 # do much the same thing, and they're more correct than doing a separate
153 # 'gclient revert' because it makes sure the other args are correct when
154 # a repo was deleted and needs to be re-cloned (notably
155 # --with_branch_heads), whereas 'revert' uses default args for clone
156 # operations.
157 #
158 # TODO(mmoss): To be like current official builders, this step could
159 # just delete the whole <slave_name>/build/ directory and start each
160 # build from scratch. That might be the least bad solution, at least
161 # until we have a reliable gclient method to produce a pristine working
162 # dir for git-based builds (e.g. maybe some combination of 'git
163 # reset/clean -fx' and removing the 'out' directory).
164 j = '-j2' if self.m.platform.is_win else '-j8'
165 args = ['sync', '--verbose', '--with_branch_heads', '--nohooks', j,
166 '--reset', '--force', '--upstream', '--no-nag-max']
167 if cfg.delete_unversioned_trees:
168 args.append('--delete_unversioned_trees')
169 self('sync', args + revisions +
170 ['--output-json', self.m.json.output()],
171 step_test_data=step_test_data,
172 **kwargs)
173 finally:
174 result = self.m.step.active_result
175 data = result.json.output
176 for path, info in data['solutions'].iteritems():
177 # gclient json paths always end with a slash
178 path = path.rstrip('/')
179 if path in cfg.got_revision_mapping:
180 propname = cfg.got_revision_mapping[path]
181 result.presentation.properties[propname] = info['revision']
182
183 return result
184
185 def inject_parent_got_revision(self, gclient_config=None, override=False):
186 """Match gclient config to build revisions obtained from build_properties.
187
188 Args:
189 gclient_config (gclient config object) - The config to manipulate. A value
190 of None manipulates the module's built-in config (self.c).
191 override (bool) - If True, will forcibly set revision and custom_vars
192 even if the config already contains values for them.
193 """
194 cfg = gclient_config or self.c
195
196 for prop, custom_var in cfg.parent_got_revision_mapping.iteritems():
197 val = str(self.m.properties.get(prop, ''))
198 # TODO(infra): Fix coverage.
199 if val: # pragma: no cover
200 # Special case for 'src', inject into solutions[0]
201 if custom_var is None:
202 # This is not covered because we are deprecating this feature and
203 # it is no longer used by the public recipes.
204 if cfg.solutions[0].revision is None or override: # pragma: no cover
205 cfg.solutions[0].revision = val
206 else:
207 if custom_var not in cfg.solutions[0].custom_vars or override:
208 cfg.solutions[0].custom_vars[custom_var] = val
209
210 def checkout(self, gclient_config=None, revert=RevertOnTryserver,
211 inject_parent_got_revision=True, with_branch_heads=False,
212 **kwargs):
213 """Return a step generator function for gclient checkouts."""
214 cfg = gclient_config or self.c
215 assert cfg.complete()
216
217 if revert is self.RevertOnTryserver:
218 revert = self.m.hacky_tryserver_detection.is_tryserver
219
220 if inject_parent_got_revision:
221 self.inject_parent_got_revision(cfg, override=True)
222
223 spec_string = jsonish_to_python(cfg.as_jsonish(), True)
224
225 self('setup', ['config', '--spec', spec_string], **kwargs)
226
227 sync_step = None
228 try:
229 if not cfg.GIT_MODE:
230 try:
231 if revert:
232 self.revert(**kwargs)
233 finally:
234 sync_step = self.sync(cfg, with_branch_heads=with_branch_heads,
235 **kwargs)
236 else:
237 sync_step = self.sync(cfg, with_branch_heads=with_branch_heads,
238 **kwargs)
239
240 cfg_cmds = [
241 ('user.name', 'local_bot'),
242 ('user.email', 'local_bot@example.com'),
243 ]
244 for var, val in cfg_cmds:
245 name = 'recurse (git config %s)' % var
246 self(name, ['recurse', 'git', 'config', var, val], **kwargs)
247
248 finally:
249 cwd = kwargs.get('cwd', self.m.path['slave_build'])
250 if 'checkout' not in self.m.path:
251 self.m.path['checkout'] = cwd.join(
252 *cfg.solutions[0].name.split(self.m.path.sep))
253
254 return sync_step
255
256 def revert(self, **kwargs):
257 """Return a gclient_safe_revert step."""
258 # Not directly calling gclient, so don't use self().
259 alias = self.spec_alias
260 prefix = '%sgclient ' % (('[spec: %s] ' % alias) if alias else '')
261
262 return self.m.python(prefix + 'revert',
263 self.m.path['build'].join('scripts', 'slave', 'gclient_safe_revert.py'),
264 ['.', self.m.path['depot_tools'].join('gclient',
265 platform_ext={'win': '.bat'})],
266 infra_step=True,
267 **kwargs
268 )
269
270 def runhooks(self, args=None, name='runhooks', **kwargs):
271 args = args or []
272 assert isinstance(args, (list, tuple))
273 return self(
274 name, ['runhooks'] + list(args), infra_step=False, **kwargs)
275
276 @property
277 def is_blink_mode(self):
278 """ Indicates wether the caller is to use the Blink config rather than the
279 Chromium config. This may happen for one of two reasons:
280 1. The builder is configured to always use TOT Blink. (factory property
281 top_of_tree_blink=True)
282 2. A try job comes in that applies to the Blink tree. (patch_project is
283 blink)
284 """
285 return (
286 self.m.properties.get('top_of_tree_blink') or
287 self.m.properties.get('patch_project') == 'blink')
288
289 def break_locks(self):
290 """Remove all index.lock files. If a previous run of git crashed, bot was
291 reset, etc... we might end up with leftover index.lock files.
292 """
293 self.m.python.inline(
294 'cleanup index.lock',
295 """
296 import os, sys
297
298 build_path = sys.argv[1]
299 if os.path.exists(build_path):
300 for (path, dir, files) in os.walk(build_path):
301 for cur_file in files:
302 if cur_file.endswith('index.lock'):
303 path_to_file = os.path.join(path, cur_file)
304 print 'deleting %s' % path_to_file
305 os.remove(path_to_file)
306 """,
307 args=[self.m.path['slave_build']],
308 infra_step=True,
309 )
OLDNEW
« no previous file with comments | « infra/recipe_modules/gclient/__init__.py ('k') | infra/recipe_modules/gclient/config.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698