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

Side by Side Diff: recipe_engine/package.py

Issue 1849903002: Perform Git operations without changing CWD. (Closed) Base URL: https://github.com/luci/recipes-py@master
Patch Set: Cleaner. Created 4 years, 8 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 | « no previous file | unittests/package_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import ast 5 import ast
6 import collections 6 import collections
7 import contextlib 7 import contextlib
8 import copy 8 import copy
9 import functools 9 import functools
10 import itertools 10 import itertools
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 self.repo = repo 189 self.repo = repo
190 self.branch = branch 190 self.branch = branch
191 self.revision = revision 191 self.revision = revision
192 self.path = path 192 self.path = path
193 193
194 def __str__(self): 194 def __str__(self):
195 return ('GitRepoSpec{project_id="%(project_id)s", repo="%(repo)s", ' 195 return ('GitRepoSpec{project_id="%(project_id)s", repo="%(repo)s", '
196 'branch="%(branch)s", revision="%(revision)s", ' 196 'branch="%(branch)s", revision="%(revision)s", '
197 'path="%(path)s"}' % self.__dict__) 197 'path="%(path)s"}' % self.__dict__)
198 198
199 def run_git(self, context, *args):
200 cmd = [self._git]
201 if context is not None:
202 cmd += ['--git-dir', os.path.join(self._dep_dir(context), '.git')]
203 cmd += list(args)
204
205 logging.info('Running: %s', cmd)
206 return subprocess.check_output(cmd)
207
199 def checkout(self, context): 208 def checkout(self, context):
200 dep_dir = self._dep_dir(context) 209 dep_dir = self._dep_dir(context)
201 logging.info('Freshening repository %s' % dep_dir) 210 logging.info('Freshening repository %s', dep_dir)
202 211
203 if not os.path.isdir(dep_dir): 212 if not os.path.isdir(dep_dir):
204 _run_cmd([self._git, 'clone', self.repo, dep_dir]) 213 self.run_git(None, 'clone', self.repo, dep_dir)
205 elif not os.path.isdir(os.path.join(dep_dir, '.git')): 214 elif not os.path.isdir(os.path.join(dep_dir, '.git')):
206 raise UncleanFilesystemError('%s exists but is not a git repo' % dep_dir) 215 raise UncleanFilesystemError('%s exists but is not a git repo' % dep_dir)
207 216
208 try: 217 try:
209 subprocess.check_output([self._git, 'rev-parse', '-q', '--verify', 218 self.run_git(context, 'rev-parse', '-q', '--verify',
210 '%s^{commit}' % self.revision], cwd=dep_dir) 219 '%s^{commit}' % self.revision)
211 except subprocess.CalledProcessError: 220 except subprocess.CalledProcessError:
212 _run_cmd([self._git, 'fetch'], cwd=dep_dir) 221 self.run_git(context, 'fetch')
213 _run_cmd([self._git, 'reset', '-q', '--hard', self.revision], cwd=dep_dir) 222 self.run_git(context, 'reset', '-q', '--hard', self.revision)
214 223
215 def check_checkout(self, context): 224 def check_checkout(self, context):
216 dep_dir = self._dep_dir(context) 225 dep_dir = self._dep_dir(context)
217 if not os.path.isdir(dep_dir): 226 if not os.path.isdir(dep_dir):
218 raise UncleanFilesystemError('Dependency %s does not exist' % 227 raise UncleanFilesystemError('Dependency %s does not exist' %
219 dep_dir) 228 dep_dir)
220 elif not os.path.isdir(os.path.join(dep_dir, '.git')): 229 elif not os.path.isdir(os.path.join(dep_dir, '.git')):
221 raise UncleanFilesystemError('Dependency %s is not a git repo' % 230 raise UncleanFilesystemError('Dependency %s is not a git repo' %
222 dep_dir) 231 dep_dir)
223 232
224 git_status_command = [self._git, 'status', '--porcelain'] 233 output = self.run_git(context, 'status', '--porcelain')
225 logging.info('%s', git_status_command)
226 output = subprocess.check_output(git_status_command, cwd=dep_dir)
227 if output: 234 if output:
228 raise UncleanFilesystemError('Dependency %s is unclean:\n%s' % 235 raise UncleanFilesystemError('Dependency %s is unclean:\n%s' %
229 (dep_dir, output)) 236 (dep_dir, output))
230 237
231 def repo_root(self, context): 238 def repo_root(self, context):
232 return os.path.join(self._dep_dir(context), self.path) 239 return os.path.join(self._dep_dir(context), self.path)
233 240
234 def dump(self): 241 def dump(self):
235 buf = package_pb2.DepSpec( 242 buf = package_pb2.DepSpec(
236 project_id=self.project_id, 243 project_id=self.project_id,
(...skipping 15 matching lines...) Expand all
252 for rev in lines: 259 for rev in lines:
253 info = self._get_commit_info(rev, context) 260 info = self._get_commit_info(rev, context)
254 updates.append(RepoUpdate( 261 updates.append(RepoUpdate(
255 GitRepoSpec(self.project_id, self.repo, self.branch, rev, 262 GitRepoSpec(self.project_id, self.repo, self.branch, rev,
256 self.path), 263 self.path),
257 commit_infos=(info,))) 264 commit_infos=(info,)))
258 return updates 265 return updates
259 266
260 def _raw_updates(self, context, subdir): 267 def _raw_updates(self, context, subdir):
261 self.checkout(context) 268 self.checkout(context)
262 _run_cmd([self._git, 'fetch'], cwd=self._dep_dir(context)) 269 self.run_git(context, 'fetch')
263 args = [self._git, 'rev-list', '--reverse', 270 args = ['rev-list', '--reverse',
264 '%s..origin/%s' % (self.revision, self.branch)] 271 '%s..origin/%s' % (self.revision, self.branch)]
265 if subdir: 272 if subdir:
266 # We add proto_file to the list of paths to check because it might contain 273 # We add proto_file to the list of paths to check because it might contain
267 # other upstream rolls, which we want. 274 # other upstream rolls, which we want.
268 args.extend(['--', subdir + os.path.sep, self.proto_file(context).path]) 275 args.extend(['--', subdir + os.path.sep, self.proto_file(context).path])
269 git = subprocess.Popen( 276 return self.run_git(context, *args)
270 args, stdout=subprocess.PIPE, cwd=self._dep_dir(context))
271 (stdout, _) = git.communicate()
272 return stdout
273 277
274 def _get_commit_info(self, rev, context): 278 def _get_commit_info(self, rev, context):
275 author = subprocess.check_output( 279 author = self.run_git(context, 'show', '-s', '--pretty=%aE', rev).strip()
276 [self._git, 'show', '-s', '--pretty=%aE', rev], 280 message = self.run_git(context, 'show', '-s', '--pretty=%B', rev).strip()
277 cwd=self._dep_dir(context)).strip()
278 message = subprocess.check_output(
279 [self._git, 'show', '-s', '--pretty=%B', rev],
280 cwd=self._dep_dir(context)).strip()
281 return CommitInfo(author, message, self.project_id, rev) 281 return CommitInfo(author, message, self.project_id, rev)
282 282
283 def _dep_dir(self, context): 283 def _dep_dir(self, context):
284 return os.path.join(context.package_dir, self.project_id) 284 return os.path.join(context.package_dir, self.project_id)
285 285
286 @property 286 @property
287 def _git(self): 287 def _git(self):
288 if sys.platform.startswith(('win', 'cygwin')): 288 if sys.platform.startswith(('win', 'cygwin')):
289 return 'git.bat' 289 return 'git.bat'
290 else: 290 else:
(...skipping 359 matching lines...) Expand 10 before | Expand all | Expand 10 after
650 @property 650 @property
651 def packages(self): 651 def packages(self):
652 for p in self._packages.values(): 652 for p in self._packages.values():
653 yield p 653 yield p
654 654
655 @property 655 @property
656 def engine_recipes_py(self): 656 def engine_recipes_py(self):
657 return os.path.join(self._context.repo_root, 'recipes.py') 657 return os.path.join(self._context.repo_root, 'recipes.py')
658 658
659 659
660 def _run_cmd(cmd, cwd=None):
661 cwd_str = ' (in %s)' % cwd if cwd else ''
662 logging.info('%s%s', cmd, cwd_str)
663 subprocess.check_call(cmd, cwd=cwd)
664
665
666 def _merge2(xs, ys, compare=lambda x, y: x <= y): 660 def _merge2(xs, ys, compare=lambda x, y: x <= y):
667 """Merges two sorted iterables, preserving sort order. 661 """Merges two sorted iterables, preserving sort order.
668 662
669 >>> list(_merge2([1, 3, 6], [2, 4, 5])) 663 >>> list(_merge2([1, 3, 6], [2, 4, 5]))
670 [1, 2, 3, 4, 5, 6] 664 [1, 2, 3, 4, 5, 6]
671 >>> list(_merge2([1, 2, 3], [])) 665 >>> list(_merge2([1, 2, 3], []))
672 [1, 2, 3] 666 [1, 2, 3]
673 >>> list(_merge2([], [4, 5, 6])) 667 >>> list(_merge2([], [4, 5, 6]))
674 [4, 5, 6] 668 [4, 5, 6]
675 >>> list(_merge2([], [])) 669 >>> list(_merge2([], []))
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
731 >>> d = { 'x': 1, 'y': 2 } 725 >>> d = { 'x': 1, 'y': 2 }
732 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items()) 726 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items())
733 [('x', 1), ('y', 3), ('z', 4)] 727 [('x', 1), ('y', 3), ('z', 4)]
734 >>> sorted(d.items()) 728 >>> sorted(d.items())
735 [('x', 1), ('y', 2)] 729 [('x', 1), ('y', 2)]
736 """ 730 """
737 731
738 d = copy.copy(d) 732 d = copy.copy(d)
739 d.update(updates) 733 d.update(updates)
740 return d 734 return d
OLDNEW
« no previous file with comments | « no previous file | unittests/package_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698