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

Side by Side Diff: infra/tools/cros_pin/pinfile.py

Issue 1403313002: Added `cros_pin` CrOS pin-bump tool. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Added pinfile.py tests, cleanup. Created 5 years, 2 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/tools/cros_pin/logger.py ('k') | infra/tools/cros_pin/test/__init__.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 2015 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 collections
6 import contextlib
7 import json
8 import os
9 import re
10
11 from infra.libs.gitiles import gitiles
12 from infra.tools.cros_pin.logger import LOGGER
13
14 class InvalidPinError(Exception):
15 pass
16
17 class ReadOnlyError(Exception):
18 pass
19
20
21 # Named Chromite pins to their checkout-relative paths.
22 Config = collections.namedtuple('Config',
23 ('name', 'base', 'json_subpath', 'masters'))
24
25
26 EXTERNAL = Config(
27 'external',
28 ('build',),
29 ('scripts', 'common', 'cros_chromite_pins.json'),
30 ('chromiumos',))
31
32
33 INTERNAL = Config(
34 'internal',
35 ('build_internal',),
36 ('scripts', 'common_internal', 'cros_chromite_internal_pins.json'),
37 ('chromeos', 'chromeos_release'))
38
39
40 PinUpdate = collections.namedtuple('PinUpdate',
41 ('name', 'fr', 'to'))
42
43
44 class Editor(object):
45
46 # Regular expression to match a Git commit (SHA1)
47 RE_COMMIT_SHA1 = re.compile(r'^[a-fA-F0-9]{40}$')
48
49 def __init__(self, checkout_path, gitiles_repo, validate=True):
50 self._checkout_path = checkout_path
51 self._gitiles = None
52 self._validate = validate
53 self._gitiles = gitiles_repo
54
55 def load(self, pin):
56 return self.File(self, pin)
57
58 def get_commit(self, branch):
59 try:
60 return self._gitiles.ref_info(branch)['commit']
61 except gitiles.GitilesError:
62 raise InvalidPinError("Pin ref [%s] does not exist." % (branch,))
63
64 def validate_pin(self, ref):
65 if not self.RE_COMMIT_SHA1.match(ref):
66 raise InvalidPinError("Not a valid SHA1 hash")
67 self.get_commit(ref)
68
69 class File(object):
70 def __init__(self, editor, pin):
71 self._editor = editor
72 self._pin = pin
73 self._path = os.path.join(editor._checkout_path,
74 *(pin.base + pin.json_subpath))
75
76 @contextlib.contextmanager
77 def edit(self):
78 d = self.load()
79 orig = d.copy()
80 try:
81 yield d
82 finally:
83 if d != orig:
84 self.save(**d)
85
86 def load(self):
87 with open(self._path, 'r') as fd:
88 return json.load(fd)
89
90 def save(self, **pins):
91 for k, v in pins.iteritems():
92 assert isinstance(k, basestring)
93 assert isinstance(v, basestring)
94 LOGGER.debug('Writing pin file [%s]: %s', self._path, pins)
95 with open(self._path, 'w') as fd:
96 json.dump(pins, fd, indent=2, sort_keys=True)
97
98
99 def update(self, pin_name, create=False, version=None):
100 """Updates a single pin value."""
101 if not version:
102 LOGGER.debug('Resolving version for pin [%s]', pin_name)
103 version = self._editor.get_commit(pin_name)
104 elif self._editor._validate:
105 LOGGER.debug('Validating pin [%s]', pin_name)
106 self._editor.validate_pin(version)
107
108 with self.edit() as pins:
109 current = pins.get(pin_name)
110 if current == version:
111 LOGGER.warning('Pin [%s.%s] is already at version [%s]',
112 self._pin.name, pin_name, current)
113 return None
114
115 LOGGER.info('Updating pin [%s.%s]: [%s] => [%s]',
116 self._pin.name, pin_name, current, version)
117 if not (current or create):
118 raise ReadOnlyError("Pin does not exist [%s]" % (pin_name,))
119 pins[pin_name] = version
120 return PinUpdate(pin_name, current, version)
121
122 def remove(self, pin_name):
123 """Removes a single pin from the pin list."""
124 with self.edit() as pins:
125 cur = pins.pop(pin_name, None)
126 if cur is None:
127 return None
128 return PinUpdate(pin_name, cur, None)
129
130 def iterpins(self):
131 """Returns a list of pins."""
132 return self.load().iteritems()
OLDNEW
« no previous file with comments | « infra/tools/cros_pin/logger.py ('k') | infra/tools/cros_pin/test/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698