Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2011 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 """Defines class Rietveld to easily access a rietveld instance. | 4 """Defines class Rietveld to easily access a rietveld instance. |
| 5 | 5 |
| 6 Security implications: | 6 Security implications: |
| 7 | 7 |
| 8 The following hypothesis are made: | 8 The following hypothesis are made: |
| 9 - Rietveld enforces: | 9 - Rietveld enforces: |
| 10 - Nobody else than issue owner can upload a patch set | 10 - Nobody else than issue owner can upload a patch set |
| 11 - Verifies the issue owner credentials when creating new issues | 11 - Verifies the issue owner credentials when creating new issues |
| 12 - A issue owner can't change once the issue is created | 12 - A issue owner can't change once the issue is created |
| 13 - A patch set cannot be modified | 13 - A patch set cannot be modified |
| 14 """ | 14 """ |
| 15 | 15 |
| 16 import logging | 16 import logging |
| 17 import os | 17 import os |
| 18 import re | |
| 18 import sys | 19 import sys |
| 19 import time | 20 import time |
| 20 import urllib2 | 21 import urllib2 |
| 21 | 22 |
| 22 try: | 23 try: |
| 23 import simplejson as json # pylint: disable=F0401 | 24 import simplejson as json # pylint: disable=F0401 |
| 24 except ImportError: | 25 except ImportError: |
| 25 try: | 26 try: |
| 26 import json # pylint: disable=F0401 | 27 import json # pylint: disable=F0401 |
| 27 except ImportError: | 28 except ImportError: |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 raise patch.UnsupportedPatchFormat( | 125 raise patch.UnsupportedPatchFormat( |
| 125 filename, 'File\'s status is None, patchset upload is incomplete.') | 126 filename, 'File\'s status is None, patchset upload is incomplete.') |
| 126 | 127 |
| 127 # TODO(maruel): That's bad, it confuses property change. | 128 # TODO(maruel): That's bad, it confuses property change. |
| 128 status = status.strip() | 129 status = status.strip() |
| 129 | 130 |
| 130 if status == 'D': | 131 if status == 'D': |
| 131 # Ignore the diff. | 132 # Ignore the diff. |
| 132 out.append(patch.FilePatchDelete(filename, state['is_binary'])) | 133 out.append(patch.FilePatchDelete(filename, state['is_binary'])) |
| 133 elif status in ('A', 'M'): | 134 elif status in ('A', 'M'): |
| 134 # TODO(maruel): Rietveld support is still weird, add this line once it's | 135 svn_props = self.parse_svn_properties( |
| 135 # safe to use. | 136 state.get('property_changes', ''), filename) |
| 136 # props = state.get('property_changes', '').splitlines() or [] | |
| 137 props = [] | |
| 138 if state['is_binary']: | 137 if state['is_binary']: |
| 139 out.append(patch.FilePatchBinary( | 138 out.append(patch.FilePatchBinary( |
| 140 filename, | 139 filename, |
| 141 self.get_file_content(issue, patchset, state['id']), | 140 self.get_file_content(issue, patchset, state['id']), |
| 142 props, | 141 svn_props, |
| 143 is_new=(status[0] == 'A'))) | 142 is_new=(status[0] == 'A'))) |
| 144 else: | 143 else: |
| 145 # Ignores num_chunks since it may only contain an header. | 144 # Ignores num_chunks since it may only contain an header. |
| 146 try: | 145 try: |
| 147 diff = self.get_file_diff(issue, patchset, state['id']) | 146 diff = self.get_file_diff(issue, patchset, state['id']) |
| 148 except urllib2.HTTPError, e: | 147 except urllib2.HTTPError, e: |
| 149 if e.code == 404: | 148 if e.code == 404: |
| 150 raise patch.UnsupportedPatchFormat( | 149 raise patch.UnsupportedPatchFormat( |
| 151 filename, 'File doesn\'t have a diff.') | 150 filename, 'File doesn\'t have a diff.') |
| 152 out.append(patch.FilePatchDiff(filename, diff, props)) | 151 out.append(patch.FilePatchDiff(filename, diff, svn_props)) |
| 153 if status[0] == 'A': | 152 if status[0] == 'A': |
| 154 # It won't be set for empty file. | 153 # It won't be set for empty file. |
| 155 out[-1].is_new = True | 154 out[-1].is_new = True |
| 156 else: | 155 else: |
| 157 # Line too long (N/80) | 156 # TODO(maruel): Add support for MM, A+, etc. Rietveld removes the svn |
| 158 # pylint: disable=C0301 | 157 # properties from the diff. |
| 159 # TODO: Add support for MM, A+, etc. Rietveld removes the svn properties | |
| 160 # from the diff. | |
| 161 # Example of mergeinfo across branches: | |
| 162 # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmlcat alog_dummy.cc | |
| 163 # svn:eol-style property that is lost in the diff | |
| 164 # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmllin t_dummy.cc | |
| 165 # Change with no diff, only svn property change: | |
| 166 # http://codereview.chromium.org/6462019/ | |
| 167 raise patch.UnsupportedPatchFormat(filename, status) | 158 raise patch.UnsupportedPatchFormat(filename, status) |
| 168 return patch.PatchSet(out) | 159 return patch.PatchSet(out) |
| 169 | 160 |
| 161 @staticmethod | |
| 162 def parse_svn_properties(rietveld_svn_props, filename): | |
| 163 """Returns a list of tuple [('property', 'newvalue')].""" | |
| 164 rietveld_svn_props = rietveld_svn_props.splitlines() | |
| 165 svn_props = [] | |
| 166 if not rietveld_svn_props: | |
| 167 return svn_props | |
| 168 # 1. Ignore svn:mergeinfo. | |
| 169 # 2. Accept svn:eol-style and svn:executable. | |
| 170 # 3. Refuse any other. | |
| 171 # \n | |
| 172 # Added: svn:ignore\n | |
| 173 # + LF\n | |
| 174 while rietveld_svn_props: | |
| 175 if rietveld_svn_props.pop(0) or not rietveld_svn_props: | |
|
Dirk Pranke
2011/08/30 20:44:58
What is the format of the the rietveld_svn_props l
M-A Ruel
2011/08/30 21:01:33
It's the exact format of a svn diff.
| |
| 176 raise patch.UnsupportedPatchFormat( | |
| 177 filename, 'Failed to parse svn properties.') | |
| 178 match = re.match(r'^(\w+): (.+)$', rietveld_svn_props.pop(0)) | |
| 179 if not match or not rietveld_svn_props: | |
| 180 raise patch.UnsupportedPatchFormat( | |
| 181 filename, 'Failed to parse svn properties.') | |
| 182 if match.group(2) == 'svn:mergeinfo': | |
| 183 # Silently ignore. | |
| 184 rietveld_svn_props.pop(0) | |
| 185 continue | |
| 186 if match.group(1) not in ('Added', 'Modified'): | |
| 187 raise patch.UnsupportedPatchFormat( | |
| 188 filename, 'Unsupported svn property operation.') | |
| 189 if match.group(2) in ('svn:eol-style', 'svn:executable'): | |
| 190 svn_props.append((match.group(2), rietveld_svn_props.pop(0)[5:])) | |
| 191 return svn_props | |
| 192 | |
| 170 def update_description(self, issue, description): | 193 def update_description(self, issue, description): |
| 171 """Sets the description for an issue on Rietveld.""" | 194 """Sets the description for an issue on Rietveld.""" |
| 172 logging.info('new description for issue %s' % issue) | 195 logging.info('new description for issue %s' % issue) |
| 173 self.post('/%s/description' % issue, [ | 196 self.post('/%s/description' % issue, [ |
| 174 ('description', description), | 197 ('description', description), |
| 175 ('xsrf_token', self.xsrf_token())]) | 198 ('xsrf_token', self.xsrf_token())]) |
| 176 | 199 |
| 177 def add_comment(self, issue, message): | 200 def add_comment(self, issue, message): |
| 178 logging.info('issue %s; comment: %s' % (issue, message)) | 201 logging.info('issue %s; comment: %s' % (issue, message)) |
| 179 return self.post('/%s/publish' % issue, [ | 202 return self.post('/%s/publish' % issue, [ |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 if retry >= (maxtries - 1): | 239 if retry >= (maxtries - 1): |
| 217 raise | 240 raise |
| 218 if not 'Name or service not known' in e.reason: | 241 if not 'Name or service not known' in e.reason: |
| 219 # Usually internal GAE flakiness. | 242 # Usually internal GAE flakiness. |
| 220 raise | 243 raise |
| 221 # If reaching this line, loop again. Uses a small backoff. | 244 # If reaching this line, loop again. Uses a small backoff. |
| 222 time.sleep(1+maxtries*2) | 245 time.sleep(1+maxtries*2) |
| 223 | 246 |
| 224 # DEPRECATED. | 247 # DEPRECATED. |
| 225 Send = get | 248 Send = get |
| OLD | NEW |