| OLD | NEW |
| 1 # Copyright 2015 The LUCI Authors. All rights reserved. | 1 # Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be 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 difflib | 9 import difflib |
| 10 import functools | 10 import functools |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 def checkout(self, context): | 151 def checkout(self, context): |
| 152 """Fetches the specified package and returns the path of the package root | 152 """Fetches the specified package and returns the path of the package root |
| 153 (the directory that contains recipes and recipe_modules). | 153 (the directory that contains recipes and recipe_modules). |
| 154 """ | 154 """ |
| 155 raise NotImplementedError() | 155 raise NotImplementedError() |
| 156 | 156 |
| 157 def repo_root(self, context): | 157 def repo_root(self, context): |
| 158 """Returns the root of this repository.""" | 158 """Returns the root of this repository.""" |
| 159 raise NotImplementedError() | 159 raise NotImplementedError() |
| 160 | 160 |
| 161 def is_consistent_with(self, other): |
| 162 """Returns True iff |other| can be used in place of |self| while keeping |
| 163 the dependency graph consistent. Most often it means being pinned at the |
| 164 same revision, but some specs (like path-based) are always compatible.""" |
| 165 raise NotImplementedError() |
| 166 |
| 161 def __eq__(self, other): | 167 def __eq__(self, other): |
| 162 raise NotImplementedError() | 168 raise NotImplementedError() |
| 163 | 169 |
| 164 def __ne__(self, other): | 170 def __ne__(self, other): |
| 165 return not (self == other) | 171 return not (self == other) |
| 166 | 172 |
| 167 def proto_file(self, context): | 173 def proto_file(self, context): |
| 168 """Returns the ProtoFile of the recipes config file in this repository. | 174 """Returns the ProtoFile of the recipes config file in this repository. |
| 169 Requires a good checkout.""" | 175 Requires a good checkout.""" |
| 170 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.repo_root(context))) | 176 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.repo_root(context))) |
| (...skipping 24 matching lines...) Expand all Loading... |
| 195 | 201 |
| 196 def checkout(self, context): | 202 def checkout(self, context): |
| 197 checkout_dir = self._dep_dir(context) | 203 checkout_dir = self._dep_dir(context) |
| 198 self.backend.checkout( | 204 self.backend.checkout( |
| 199 self.repo, self.revision, checkout_dir, context.allow_fetch) | 205 self.repo, self.revision, checkout_dir, context.allow_fetch) |
| 200 cleanup_pyc(checkout_dir) | 206 cleanup_pyc(checkout_dir) |
| 201 | 207 |
| 202 def repo_root(self, context): | 208 def repo_root(self, context): |
| 203 return os.path.join(self._dep_dir(context), self.path) | 209 return os.path.join(self._dep_dir(context), self.path) |
| 204 | 210 |
| 211 def is_consistent_with(self, other): |
| 212 return (self == other) |
| 213 |
| 205 def dump(self): | 214 def dump(self): |
| 206 buf = package_pb2.DepSpec( | 215 buf = package_pb2.DepSpec( |
| 207 project_id=self.project_id, | 216 project_id=self.project_id, |
| 208 url=self.repo, | 217 url=self.repo, |
| 209 branch=self.branch, | 218 branch=self.branch, |
| 210 revision=self.revision) | 219 revision=self.revision) |
| 211 if self.path: | 220 if self.path: |
| 212 buf.path_override = self.path | 221 buf.path_override = self.path |
| 213 | 222 |
| 214 # Only dump repo_type if it's different from default. This preserves | 223 # Only dump repo_type if it's different from default. This preserves |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 def proto_file(self, context): | 326 def proto_file(self, context): |
| 318 """Returns the ProtoFile of the recipes config file in this repository. | 327 """Returns the ProtoFile of the recipes config file in this repository. |
| 319 Requires a good checkout.""" | 328 Requires a good checkout.""" |
| 320 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.path)) | 329 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.path)) |
| 321 | 330 |
| 322 def __eq__(self, other): | 331 def __eq__(self, other): |
| 323 if not isinstance(other, type(self)): | 332 if not isinstance(other, type(self)): |
| 324 return False | 333 return False |
| 325 return self.path == other.path | 334 return self.path == other.path |
| 326 | 335 |
| 336 def is_consistent_with(self, other): |
| 337 # PathRepoSpec is always compatible, unless it's PathRepoSpec with a |
| 338 # different path. |
| 339 if isinstance(other, type(self)): |
| 340 return self.path == other.path |
| 341 return True |
| 342 |
| 327 | 343 |
| 328 class RootRepoSpec(RepoSpec): | 344 class RootRepoSpec(RepoSpec): |
| 329 def __init__(self, proto_file): | 345 def __init__(self, proto_file): |
| 330 self._proto_file = proto_file | 346 self._proto_file = proto_file |
| 331 | 347 |
| 332 def checkout(self, context): | 348 def checkout(self, context): |
| 333 # We assume this is already checked out. | 349 # We assume this is already checked out. |
| 334 pass | 350 pass |
| 335 | 351 |
| 336 def repo_root(self, context): | 352 def repo_root(self, context): |
| 337 return context.repo_root | 353 return context.repo_root |
| 338 | 354 |
| 355 def is_consistent_with(self, other): |
| 356 return (self == other) |
| 357 |
| 339 def proto_file(self, context): | 358 def proto_file(self, context): |
| 340 return self._proto_file | 359 return self._proto_file |
| 341 | 360 |
| 342 def __eq__(self, other): | 361 def __eq__(self, other): |
| 343 if not isinstance(other, type(self)): | 362 if not isinstance(other, type(self)): |
| 344 return False | 363 return False |
| 345 return self._proto_file == other._proto_file | 364 return self._proto_file == other._proto_file |
| 346 | 365 |
| 347 | 366 |
| 348 class Package(object): | 367 class Package(object): |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 def _create_from_spec(self, repo_spec, package_spec, overriding): | 666 def _create_from_spec(self, repo_spec, package_spec, overriding): |
| 648 project_id = package_spec.project_id | 667 project_id = package_spec.project_id |
| 649 | 668 |
| 650 if project_id in self._packages: | 669 if project_id in self._packages: |
| 651 current = self._packages[project_id] | 670 current = self._packages[project_id] |
| 652 if current is None: | 671 if current is None: |
| 653 raise CyclicDependencyError( | 672 raise CyclicDependencyError( |
| 654 'Package %s depends on itself' % project_id) | 673 'Package %s depends on itself' % project_id) |
| 655 | 674 |
| 656 # Only enforce package consistency within the override boundary. | 675 # Only enforce package consistency within the override boundary. |
| 657 if current.is_override == overriding and repo_spec != current.repo_spec: | 676 # It's fine if either spec claims it's consistent with the other. |
| 677 if (current.is_override == overriding and |
| 678 not repo_spec.is_consistent_with(current.repo_spec) and |
| 679 not current.repo_spec.is_consistent_with(repo_spec)): |
| 658 raise InconsistentDependencyGraphError( | 680 raise InconsistentDependencyGraphError( |
| 659 project_id, (repo_spec, current.repo_spec)) | 681 project_id, (repo_spec, current.repo_spec)) |
| 660 self._packages[project_id] = None | 682 self._packages[project_id] = None |
| 661 | 683 |
| 662 deps = {} | 684 deps = {} |
| 663 for dep, dep_repo in sorted(package_spec.deps.items()): | 685 for dep, dep_repo in sorted(package_spec.deps.items()): |
| 664 dep_package = self._packages.get(dep) | 686 dep_package = self._packages.get(dep) |
| 665 if not (dep_package and dep_package.is_override): | 687 if not (dep_package and dep_package.is_override): |
| 666 dep_package = self._create_package(dep_repo, overriding) | 688 dep_package = self._create_package(dep_repo, overriding) |
| 667 deps[dep] = dep_package | 689 deps[dep] = dep_package |
| (...skipping 28 matching lines...) Expand all Loading... |
| 696 >>> d = { 'x': 1, 'y': 2 } | 718 >>> d = { 'x': 1, 'y': 2 } |
| 697 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items()) | 719 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items()) |
| 698 [('x', 1), ('y', 3), ('z', 4)] | 720 [('x', 1), ('y', 3), ('z', 4)] |
| 699 >>> sorted(d.items()) | 721 >>> sorted(d.items()) |
| 700 [('x', 1), ('y', 2)] | 722 [('x', 1), ('y', 2)] |
| 701 """ | 723 """ |
| 702 | 724 |
| 703 d = copy.copy(d) | 725 d = copy.copy(d) |
| 704 d.update(updates) | 726 d.update(updates) |
| 705 return d | 727 return d |
| OLD | NEW |