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

Side by Side Diff: recipe_engine/package.py

Issue 2785503002: Revert of [package.proto] convert deps from list to map. (Closed)
Patch Set: Created 3 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 | « recipe_engine/package.proto ('k') | recipe_engine/package_pb2.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 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 copy 5 import copy
6 import difflib 6 import difflib
7 import json 7 import json
8 import logging 8 import logging
9 import operator 9 import operator
10 import os 10 import os
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 return os.path.dirname( # <repo root> 53 return os.path.dirname( # <repo root>
54 os.path.dirname( # infra 54 os.path.dirname( # infra
55 os.path.dirname( # config 55 os.path.dirname( # config
56 os.path.abspath(recipes_cfg)))) # recipes.cfg 56 os.path.abspath(recipes_cfg)))) # recipes.cfg
57 57
58 58
59 class ProtoFile(object): 59 class ProtoFile(object):
60 """A collection of functions operating on a proto path. 60 """A collection of functions operating on a proto path.
61 61
62 This is an object so that it can be mocked in the tests. 62 This is an object so that it can be mocked in the tests.
63
64 Proto files read will always be upconverted to the current proto in
65 package.proto, and will be written back in their original format.
66 """ 63 """
67 API_VERSIONS = (1, 2)
68
69 def __init__(self, path): 64 def __init__(self, path):
70 self._path = path 65 self._path = path
71 66
72 @property 67 @property
73 def path(self): 68 def path(self):
74 return os.path.realpath(self._path) 69 return os.path.realpath(self._path)
75 70
76 def read_raw(self): 71 def read_raw(self):
77 with open(self._path, 'r') as fh: 72 with open(self._path, 'r') as fh:
78 return fh.read() 73 return fh.read()
79 74
80 def read(self): 75 def read(self):
81 obj = json.loads(self.read_raw()) 76 text = self.read_raw()
82
83 vers = obj.get('api_version')
84 assert vers in self.API_VERSIONS, (
85 'expected %r to be one of %r' % (vers, self.API_VERSIONS)
86 )
87
88 # upconvert old deps-as-a-list to deps-as-a-dict
89 if 'deps' in obj and vers == 1:
90 obj['deps'] = {d.pop('project_id'): d for d in obj['deps']}
91
92 buf = package_pb2.Package() 77 buf = package_pb2.Package()
93 json_format.ParseDict(obj, buf, ignore_unknown_fields=True) 78 json_format.Parse(text, buf, ignore_unknown_fields=True)
94 return buf 79 return buf
95 80
96 def to_raw(self, buf): 81 def to_raw(self, buf):
97 obj = json_format.MessageToDict(buf, preserving_proto_field_name=True) 82 obj = json_format.MessageToDict(buf, preserving_proto_field_name=True)
98
99 # downconvert if api_version is 1
100 if buf.deps and buf.api_version < 2:
101 deps = []
102 for pid, d in sorted(obj['deps'].iteritems()):
103 d['project_id'] = pid
104 deps.append(d)
105 obj['deps'] = deps
106
107 return json.dumps(obj, indent=2, sort_keys=True).replace(' \n', '\n') 83 return json.dumps(obj, indent=2, sort_keys=True).replace(' \n', '\n')
108 84
109 def write(self, buf): 85 def write(self, buf):
110 with open(self._path, 'w') as fh: 86 with open(self._path, 'w') as fh:
111 fh.write(self.to_raw(buf)) 87 fh.write(self.to_raw(buf))
112 88
113 89
114 class PackageContext(object): 90 class PackageContext(object):
115 """Contains information about where the root package and its dependency 91 """Contains information about where the root package and its dependency
116 checkouts live. 92 checkouts live.
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 checkout_dir = self._dep_dir(context) 198 checkout_dir = self._dep_dir(context)
223 self.backend.checkout( 199 self.backend.checkout(
224 self.repo, self.revision, checkout_dir, context.allow_fetch) 200 self.repo, self.revision, checkout_dir, context.allow_fetch)
225 cleanup_pyc(checkout_dir) 201 cleanup_pyc(checkout_dir)
226 202
227 def repo_root(self, context): 203 def repo_root(self, context):
228 return os.path.join(self._dep_dir(context), self.path) 204 return os.path.join(self._dep_dir(context), self.path)
229 205
230 def dump(self): 206 def dump(self):
231 buf = package_pb2.DepSpec( 207 buf = package_pb2.DepSpec(
208 project_id=self.project_id,
232 url=self.repo, 209 url=self.repo,
233 branch=self.branch, 210 branch=self.branch,
234 revision=self.revision) 211 revision=self.revision)
235 if self.path: 212 if self.path:
236 buf.path_override = self.path 213 buf.path_override = self.path
237 214
238 # Only dump repo_type if it's different from default. This preserves 215 # Only dump repo_type if it's different from default. This preserves
239 # compatibility e.g. with recipes.py bootstrap scripts in client repos 216 # compatibility e.g. with recipes.py bootstrap scripts in client repos
240 # which may not handle repo_type correctly. 217 # which may not handle repo_type correctly.
241 # TODO(phajdan.jr): programmatically extract the default value. 218 # TODO(phajdan.jr): programmatically extract the default value.
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
346 Requires a good checkout.""" 323 Requires a good checkout."""
347 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.path)) 324 return ProtoFile(InfraRepoConfig().to_recipes_cfg(self.path))
348 325
349 def updates(self, _context, _other_revision=None): 326 def updates(self, _context, _other_revision=None):
350 """Returns (empty) list of potential updates for this spec.""" 327 """Returns (empty) list of potential updates for this spec."""
351 return [] 328 return []
352 329
353 def dump(self): 330 def dump(self):
354 """Returns the package.proto DepSpec form of this RepoSpec.""" 331 """Returns the package.proto DepSpec form of this RepoSpec."""
355 return package_pb2.DepSpec( 332 return package_pb2.DepSpec(
333 project_id=self.project_id,
356 url="file://"+self.path) 334 url="file://"+self.path)
357 335
358 def __eq__(self, other): 336 def __eq__(self, other):
359 if not isinstance(other, type(self)): 337 if not isinstance(other, type(self)):
360 return False 338 return False
361 return self.path == other.path 339 return self.path == other.path
362 340
363 341
364 class RootRepoSpec(RepoSpec): 342 class RootRepoSpec(RepoSpec):
365 def __init__(self, proto_file): 343 def __init__(self, proto_file):
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 # Prevent rolling backwards. 455 # Prevent rolling backwards.
478 more_recent_revision = other_spec.get_more_recent_revision( 456 more_recent_revision = other_spec.get_more_recent_revision(
479 self._context, current_revision, other_spec.revision) 457 self._context, current_revision, other_spec.revision)
480 if more_recent_revision != other_spec.revision: 458 if more_recent_revision != other_spec.revision:
481 return False 459 return False
482 460
483 self._updates[other_spec.project_id] = other_spec 461 self._updates[other_spec.project_id] = other_spec
484 462
485 def get_rolled_spec(self): 463 def get_rolled_spec(self):
486 """Returns a PackageSpec with all the deps updates from this roll.""" 464 """Returns a PackageSpec with all the deps updates from this roll."""
465 # TODO(phajdan.jr): does this preserve comments? should it?
487 new_deps = _updated( 466 new_deps = _updated(
488 self._package_spec.deps, 467 self._package_spec.deps,
489 { project_id: spec for project_id, spec in 468 { project_id: spec for project_id, spec in
490 self._updates.iteritems() }) 469 self._updates.iteritems() })
491 return PackageSpec( 470 return PackageSpec(
492 self._package_spec.api_version,
493 self._package_spec.project_id, 471 self._package_spec.project_id,
494 self._package_spec.recipes_path, 472 self._package_spec.recipes_path,
495 new_deps) 473 new_deps)
496 474
497 def get_commit_infos(self): 475 def get_commit_infos(self):
498 """Returns a mapping project_id -> list of commits from that repo 476 """Returns a mapping project_id -> list of commits from that repo
499 that are getting pulled by this roll. 477 that are getting pulled by this roll.
500 """ 478 """
501 commit_infos = {} 479 commit_infos = {}
502 480
(...skipping 11 matching lines...) Expand all
514 492
515 def get_diff(self): 493 def get_diff(self):
516 """Returns a unified diff between original package spec and one after roll. 494 """Returns a unified diff between original package spec and one after roll.
517 """ 495 """
518 orig = str(self._package_spec.dump()).splitlines() 496 orig = str(self._package_spec.dump()).splitlines()
519 new = str(self.get_rolled_spec().dump()).splitlines() 497 new = str(self.get_rolled_spec().dump()).splitlines()
520 return '\n'.join(difflib.unified_diff(orig, new, lineterm='')) 498 return '\n'.join(difflib.unified_diff(orig, new, lineterm=''))
521 499
522 500
523 class PackageSpec(object): 501 class PackageSpec(object):
524 def __init__(self, api_version, project_id, recipes_path, deps): 502 API_VERSION = 1
525 self._api_version = api_version 503
504 def __init__(self, project_id, recipes_path, deps):
526 self._project_id = project_id 505 self._project_id = project_id
527 self._recipes_path = recipes_path 506 self._recipes_path = recipes_path
528 self._deps = deps 507 self._deps = deps
529 508
530 def __repr__(self): 509 def __repr__(self):
531 return 'PackageSpec(%s, %s, %r)' % (self._project_id, self._recipes_path, 510 return 'PackageSpec(%s, %s, %r)' % (self._project_id, self._recipes_path,
532 self._deps) 511 self._deps)
533 512
534 @classmethod 513 @classmethod
535 def load_proto(cls, proto_file): 514 def load_proto(cls, proto_file):
536 buf = proto_file.read() 515 buf = proto_file.read()
516 assert buf.api_version == cls.API_VERSION
537 517
538 deps = { pid: cls.spec_for_dep(pid, dep) 518 deps = { str(dep.project_id): cls.spec_for_dep(dep)
539 for pid, dep in buf.deps.iteritems() } 519 for dep in buf.deps }
540 return cls(buf.api_version, str(buf.project_id), str(buf.recipes_path), 520 return cls(str(buf.project_id), str(buf.recipes_path), deps)
541 deps)
542 521
543 @classmethod 522 @classmethod
544 def spec_for_dep(cls, project_id, dep): 523 def spec_for_dep(cls, dep):
545 """Returns a RepoSpec for the given dependency protobuf.""" 524 """Returns a RepoSpec for the given dependency protobuf."""
546 url = str(dep.url) 525 url = str(dep.url)
547 if url.startswith("file://"): 526 if url.startswith("file://"):
548 return PathRepoSpec(str(project_id), url[len("file://"):]) 527 return PathRepoSpec(str(dep.project_id), url[len("file://"):])
549 528
550 if dep.repo_type in (package_pb2.DepSpec.GIT, package_pb2.DepSpec.GITILES): 529 if dep.repo_type in (package_pb2.DepSpec.GIT, package_pb2.DepSpec.GITILES):
551 if dep.repo_type == package_pb2.DepSpec.GIT: 530 if dep.repo_type == package_pb2.DepSpec.GIT:
552 backend = fetch.GitBackend() 531 backend = fetch.GitBackend()
553 elif dep.repo_type == package_pb2.DepSpec.GITILES: 532 elif dep.repo_type == package_pb2.DepSpec.GITILES:
554 backend = fetch.GitilesBackend() 533 backend = fetch.GitilesBackend()
555 return GitRepoSpec(str(project_id), 534 return GitRepoSpec(str(dep.project_id),
556 url, 535 url,
557 str(dep.branch), 536 str(dep.branch),
558 str(dep.revision), 537 str(dep.revision),
559 str(dep.path_override), 538 str(dep.path_override),
560 backend) 539 backend)
561 540
562 assert False, 'Unexpected repo type: %s' % dep 541 assert False, 'Unexpected repo type: %s' % dep
563 542
564 @property 543 @property
565 def project_id(self): 544 def project_id(self):
566 return self._project_id 545 return self._project_id
567 546
568 @property 547 @property
569 def recipes_path(self): 548 def recipes_path(self):
570 return self._recipes_path 549 return self._recipes_path
571 550
572 @property 551 @property
573 def deps(self): 552 def deps(self):
574 return self._deps 553 return self._deps
575 554
576 @property
577 def api_version(self):
578 return self._api_version
579
580 def dump(self): 555 def dump(self):
581 return package_pb2.Package( 556 return package_pb2.Package(
582 api_version=self._api_version, 557 api_version=self.API_VERSION,
583 project_id=self._project_id, 558 project_id=self._project_id,
584 recipes_path=self._recipes_path, 559 recipes_path=self._recipes_path,
585 deps={k: v.dump() for k, v in self._deps.iteritems()}) 560 deps=[ self._deps[dep].dump() for dep in sorted(self._deps.keys()) ])
586 561
587 def roll_candidates(self, root_spec, context): 562 def roll_candidates(self, root_spec, context):
588 """Returns list of consistent roll candidates, and rejected roll candidates. 563 """Returns list of consistent roll candidates, and rejected roll candidates.
589 564
590 The first one is sorted by score, descending. The more commits are pulled by 565 The first one is sorted by score, descending. The more commits are pulled by
591 the roll, the higher score. 566 the roll, the higher score.
592 567
593 Second list is included to distinguish between a situation where there are 568 Second list is included to distinguish between a situation where there are
594 no roll candidates from one where there are updates but they're not 569 no roll candidates from one where there are updates but they're not
595 consistent. 570 consistent.
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
717 >>> d = { 'x': 1, 'y': 2 } 692 >>> d = { 'x': 1, 'y': 2 }
718 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items()) 693 >>> sorted(_updated(d, { 'y': 3, 'z': 4 }).items())
719 [('x', 1), ('y', 3), ('z', 4)] 694 [('x', 1), ('y', 3), ('z', 4)]
720 >>> sorted(d.items()) 695 >>> sorted(d.items())
721 [('x', 1), ('y', 2)] 696 [('x', 1), ('y', 2)]
722 """ 697 """
723 698
724 d = copy.copy(d) 699 d = copy.copy(d)
725 d.update(updates) 700 d.update(updates)
726 return d 701 return d
OLDNEW
« no previous file with comments | « recipe_engine/package.proto ('k') | recipe_engine/package_pb2.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698