OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # coding: utf-8 | 2 # coding: utf-8 |
3 # | 3 # |
4 # Copyright 2007 Google Inc. | 4 # Copyright 2007 Google Inc. |
5 # | 5 # |
6 # Licensed under the Apache License, Version 2.0 (the "License"); | 6 # Licensed under the Apache License, Version 2.0 (the "License"); |
7 # you may not use this file except in compliance with the License. | 7 # you may not use this file except in compliance with the License. |
8 # You may obtain a copy of the License at | 8 # You may obtain a copy of the License at |
9 # | 9 # |
10 # http://www.apache.org/licenses/LICENSE-2.0 | 10 # http://www.apache.org/licenses/LICENSE-2.0 |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 import keyring | 71 import keyring |
72 except ImportError: | 72 except ImportError: |
73 keyring = None | 73 keyring = None |
74 | 74 |
75 # The logging verbosity: | 75 # The logging verbosity: |
76 # 0: Errors only. | 76 # 0: Errors only. |
77 # 1: Status messages. | 77 # 1: Status messages. |
78 # 2: Info logs. | 78 # 2: Info logs. |
79 # 3: Debug logs. | 79 # 3: Debug logs. |
80 verbosity = 1 | 80 verbosity = 1 |
| 81 LOGGER = logging.getLogger('upload') |
81 | 82 |
82 # The account type used for authentication. | 83 # The account type used for authentication. |
83 # This line could be changed by the review server (see handler for | 84 # This line could be changed by the review server (see handler for |
84 # upload.py). | 85 # upload.py). |
85 AUTH_ACCOUNT_TYPE = "GOOGLE" | 86 AUTH_ACCOUNT_TYPE = "GOOGLE" |
86 | 87 |
87 # URL of the default review server. As for AUTH_ACCOUNT_TYPE, this line could be | 88 # URL of the default review server. As for AUTH_ACCOUNT_TYPE, this line could be |
88 # changed by the review server (see handler for upload.py). | 89 # changed by the review server (see handler for upload.py). |
89 DEFAULT_REVIEW_SERVER = "codereview.appspot.com" | 90 DEFAULT_REVIEW_SERVER = "codereview.appspot.com" |
90 | 91 |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
263 not self.host.startswith("https://")): | 264 not self.host.startswith("https://")): |
264 self.host = "http://" + self.host | 265 self.host = "http://" + self.host |
265 self.host_override = host_override | 266 self.host_override = host_override |
266 self.auth_function = auth_function | 267 self.auth_function = auth_function |
267 self.authenticated = False | 268 self.authenticated = False |
268 self.extra_headers = extra_headers or {} | 269 self.extra_headers = extra_headers or {} |
269 self.save_cookies = save_cookies | 270 self.save_cookies = save_cookies |
270 self.account_type = account_type | 271 self.account_type = account_type |
271 self.opener = self._GetOpener() | 272 self.opener = self._GetOpener() |
272 if self.host_override: | 273 if self.host_override: |
273 logging.info("Server: %s; Host: %s", self.host, self.host_override) | 274 LOGGER.info("Server: %s; Host: %s", self.host, self.host_override) |
274 else: | 275 else: |
275 logging.info("Server: %s", self.host) | 276 LOGGER.info("Server: %s", self.host) |
276 | 277 |
277 def _GetOpener(self): | 278 def _GetOpener(self): |
278 """Returns an OpenerDirector for making HTTP requests. | 279 """Returns an OpenerDirector for making HTTP requests. |
279 | 280 |
280 Returns: | 281 Returns: |
281 A urllib2.OpenerDirector object. | 282 A urllib2.OpenerDirector object. |
282 """ | 283 """ |
283 raise NotImplementedError() | 284 raise NotImplementedError() |
284 | 285 |
285 def _CreateRequest(self, url, data=None): | 286 def _CreateRequest(self, url, data=None): |
286 """Creates a new urllib request.""" | 287 """Creates a new urllib request.""" |
287 logging.debug("Creating request for: '%s' with payload:\n%s", url, data) | 288 LOGGER.debug("Creating request for: '%s' with payload:\n%s", url, data) |
288 req = urllib2.Request(url, data=data, headers={"Accept": "text/plain"}) | 289 req = urllib2.Request(url, data=data, headers={"Accept": "text/plain"}) |
289 if self.host_override: | 290 if self.host_override: |
290 req.add_header("Host", self.host_override) | 291 req.add_header("Host", self.host_override) |
291 for key, value in self.extra_headers.iteritems(): | 292 for key, value in self.extra_headers.iteritems(): |
292 req.add_header(key, value) | 293 req.add_header(key, value) |
293 return req | 294 return req |
294 | 295 |
295 def _GetAuthToken(self, email, password): | 296 def _GetAuthToken(self, email, password): |
296 """Uses ClientLogin to authenticate the user, returning an auth token. | 297 """Uses ClientLogin to authenticate the user, returning an auth token. |
297 | 298 |
(...skipping 626 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
924 OAuth, this opens a page in the user's browser to obtain a token. | 925 OAuth, this opens a page in the user's browser to obtain a token. |
925 | 926 |
926 Returns: | 927 Returns: |
927 A new HttpRpcServer, on which RPC calls can be made. | 928 A new HttpRpcServer, on which RPC calls can be made. |
928 """ | 929 """ |
929 # If this is the dev_appserver, use fake authentication. | 930 # If this is the dev_appserver, use fake authentication. |
930 host = (host_override or server).lower() | 931 host = (host_override or server).lower() |
931 if re.match(r'(http://)?localhost([:/]|$)', host): | 932 if re.match(r'(http://)?localhost([:/]|$)', host): |
932 if email is None: | 933 if email is None: |
933 email = "test@example.com" | 934 email = "test@example.com" |
934 logging.info("Using debug user %s. Override with --email" % email) | 935 LOGGER.info("Using debug user %s. Override with --email" % email) |
935 server = HttpRpcServer( | 936 server = HttpRpcServer( |
936 server, | 937 server, |
937 lambda: (email, "password"), | 938 lambda: (email, "password"), |
938 host_override=host_override, | 939 host_override=host_override, |
939 extra_headers={"Cookie": | 940 extra_headers={"Cookie": |
940 'dev_appserver_login="%s:False"' % email}, | 941 'dev_appserver_login="%s:False"' % email}, |
941 save_cookies=save_cookies, | 942 save_cookies=save_cookies, |
942 account_type=account_type) | 943 account_type=account_type) |
943 # Don't try to talk to ClientLogin. | 944 # Don't try to talk to ClientLogin. |
944 server.authenticated = True | 945 server.authenticated = True |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1010 | 1011 |
1011 Args: | 1012 Args: |
1012 command: Command to execute. | 1013 command: Command to execute. |
1013 print_output: If True, the output is printed to stdout. | 1014 print_output: If True, the output is printed to stdout. |
1014 If False, both stdout and stderr are ignored. | 1015 If False, both stdout and stderr are ignored. |
1015 universal_newlines: Use universal_newlines flag (default: True). | 1016 universal_newlines: Use universal_newlines flag (default: True). |
1016 | 1017 |
1017 Returns: | 1018 Returns: |
1018 Tuple (stdout, stderr, return code) | 1019 Tuple (stdout, stderr, return code) |
1019 """ | 1020 """ |
1020 logging.info("Running %s", command) | 1021 LOGGER.info("Running %s", command) |
1021 env = env.copy() | 1022 env = env.copy() |
1022 env['LC_MESSAGES'] = 'C' | 1023 env['LC_MESSAGES'] = 'C' |
1023 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 1024 p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
1024 shell=use_shell, universal_newlines=universal_newlines, | 1025 shell=use_shell, universal_newlines=universal_newlines, |
1025 env=env) | 1026 env=env) |
1026 if print_output: | 1027 if print_output: |
1027 output_array = [] | 1028 output_array = [] |
1028 while True: | 1029 while True: |
1029 line = p.stdout.readline() | 1030 line = p.stdout.readline() |
1030 if not line: | 1031 if not line: |
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1272 if netloc == "svn.python.org" and scheme == "svn+ssh": | 1273 if netloc == "svn.python.org" and scheme == "svn+ssh": |
1273 path = "projects" + path | 1274 path = "projects" + path |
1274 scheme = "http" | 1275 scheme = "http" |
1275 guess = "Python " | 1276 guess = "Python " |
1276 elif netloc.endswith(".googlecode.com"): | 1277 elif netloc.endswith(".googlecode.com"): |
1277 scheme = "http" | 1278 scheme = "http" |
1278 guess = "Google Code " | 1279 guess = "Google Code " |
1279 path = path + "/" | 1280 path = path + "/" |
1280 base = urlparse.urlunparse((scheme, netloc, path, params, | 1281 base = urlparse.urlunparse((scheme, netloc, path, params, |
1281 query, fragment)) | 1282 query, fragment)) |
1282 logging.info("Guessed %sbase = %s", guess, base) | 1283 LOGGER.info("Guessed %sbase = %s", guess, base) |
1283 return base | 1284 return base |
1284 if required: | 1285 if required: |
1285 ErrorExit("Can't find URL in output from svn info") | 1286 ErrorExit("Can't find URL in output from svn info") |
1286 return None | 1287 return None |
1287 | 1288 |
1288 def _GetInfo(self, key): | 1289 def _GetInfo(self, key): |
1289 """Parses 'svn info' for current dir. Returns value for key or None""" | 1290 """Parses 'svn info' for current dir. Returns value for key or None""" |
1290 for line in RunShell(["svn", "info"]).splitlines(): | 1291 for line in RunShell(["svn", "info"]).splitlines(): |
1291 if line.startswith(key + ": "): | 1292 if line.startswith(key + ": "): |
1292 return line.split(":", 1)[1].strip() | 1293 return line.split(":", 1)[1].strip() |
1293 | 1294 |
1294 def _EscapeFilename(self, filename): | 1295 def _EscapeFilename(self, filename): |
1295 """Escapes filename for SVN commands.""" | 1296 """Escapes filename for SVN commands.""" |
1296 if "@" in filename and not filename.endswith("@"): | 1297 if "@" in filename and not filename.endswith("@"): |
1297 filename = "%s@" % filename | 1298 filename = "%s@" % filename |
1298 return filename | 1299 return filename |
1299 | 1300 |
1300 def GenerateDiff(self, args): | 1301 def GenerateDiff(self, args): |
1301 cmd = ["svn", "diff"] | 1302 cmd = ["svn", "diff"] |
1302 if self.options.revision: | 1303 if self.options.revision: |
1303 cmd += ["-r", self.options.revision] | 1304 cmd += ["-r", self.options.revision] |
1304 cmd.extend(args) | 1305 cmd.extend(args) |
1305 data = RunShell(cmd) | 1306 data = RunShell(cmd) |
1306 count = 0 | 1307 count = 0 |
1307 for line in data.splitlines(): | 1308 for line in data.splitlines(): |
1308 if line.startswith("Index:") or line.startswith("Property changes on:"): | 1309 if line.startswith("Index:") or line.startswith("Property changes on:"): |
1309 count += 1 | 1310 count += 1 |
1310 logging.info(line) | 1311 LOGGER.info(line) |
1311 if not count: | 1312 if not count: |
1312 ErrorExit("No valid patches found in output from svn diff") | 1313 ErrorExit("No valid patches found in output from svn diff") |
1313 return data | 1314 return data |
1314 | 1315 |
1315 def _CollapseKeywords(self, content, keyword_str): | 1316 def _CollapseKeywords(self, content, keyword_str): |
1316 """Collapses SVN keywords.""" | 1317 """Collapses SVN keywords.""" |
1317 # svn cat translates keywords but svn diff doesn't. As a result of this | 1318 # svn cat translates keywords but svn diff doesn't. As a result of this |
1318 # behavior patching.PatchChunks() fails with a chunk mismatch error. | 1319 # behavior patching.PatchChunks() fails with a chunk mismatch error. |
1319 # This part was originally written by the Review Board development team | 1320 # This part was originally written by the Review Board development team |
1320 # who had the same problem (http://reviews.review-board.org/r/276/). | 1321 # who had the same problem (http://reviews.review-board.org/r/276/). |
(...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1729 if self.options.revision: | 1730 if self.options.revision: |
1730 cmd += ["-r", self.options.revision] | 1731 cmd += ["-r", self.options.revision] |
1731 | 1732 |
1732 cmd.extend(extra_args) | 1733 cmd.extend(extra_args) |
1733 data, retcode = RunShellWithReturnCode(cmd) | 1734 data, retcode = RunShellWithReturnCode(cmd) |
1734 count = 0 | 1735 count = 0 |
1735 if retcode in [0, 1]: | 1736 if retcode in [0, 1]: |
1736 for line in data.splitlines(): | 1737 for line in data.splitlines(): |
1737 if line.startswith("Index:"): | 1738 if line.startswith("Index:"): |
1738 count += 1 | 1739 count += 1 |
1739 logging.info(line) | 1740 LOGGER.info(line) |
1740 | 1741 |
1741 if not count: | 1742 if not count: |
1742 ErrorExit("No valid patches found in output from cvs diff") | 1743 ErrorExit("No valid patches found in output from cvs diff") |
1743 | 1744 |
1744 return data | 1745 return data |
1745 | 1746 |
1746 def GetUnknownFiles(self): | 1747 def GetUnknownFiles(self): |
1747 data, retcode = RunShellWithReturnCode(["cvs", "diff"]) | 1748 data, retcode = RunShellWithReturnCode(["cvs", "diff"]) |
1748 if retcode not in [0, 1]: | 1749 if retcode not in [0, 1]: |
1749 ErrorExit("Got error status from 'cvs diff':\n%s" % (data,)) | 1750 ErrorExit("Got error status from 'cvs diff':\n%s" % (data,)) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1791 if m: | 1792 if m: |
1792 # Modify line to make it look like as it comes from svn diff. | 1793 # Modify line to make it look like as it comes from svn diff. |
1793 # With this modification no changes on the server side are required | 1794 # With this modification no changes on the server side are required |
1794 # to make upload.py work with Mercurial repos. | 1795 # to make upload.py work with Mercurial repos. |
1795 # NOTE: for proper handling of moved/copied files, we have to use | 1796 # NOTE: for proper handling of moved/copied files, we have to use |
1796 # the second filename. | 1797 # the second filename. |
1797 filename = m.group(2) | 1798 filename = m.group(2) |
1798 svndiff.append("Index: %s" % filename) | 1799 svndiff.append("Index: %s" % filename) |
1799 svndiff.append("=" * 67) | 1800 svndiff.append("=" * 67) |
1800 filecount += 1 | 1801 filecount += 1 |
1801 logging.info(line) | 1802 LOGGER.info(line) |
1802 else: | 1803 else: |
1803 svndiff.append(line) | 1804 svndiff.append(line) |
1804 if not filecount: | 1805 if not filecount: |
1805 ErrorExit("No valid patches found in output from hg diff") | 1806 ErrorExit("No valid patches found in output from hg diff") |
1806 return "\n".join(svndiff) + "\n" | 1807 return "\n".join(svndiff) + "\n" |
1807 | 1808 |
1808 def GetUnknownFiles(self): | 1809 def GetUnknownFiles(self): |
1809 """Return a list of files unknown to the VCS.""" | 1810 """Return a list of files unknown to the VCS.""" |
1810 args = [] | 1811 args = [] |
1811 status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], | 1812 status = RunShell(["hg", "status", "--rev", self.base_rev, "-u", "."], |
(...skipping 708 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2520 "For more help, see " | 2521 "For more help, see " |
2521 "http://code.google.com/p/rietveld/wiki/CodeReviewHelp" | 2522 "http://code.google.com/p/rietveld/wiki/CodeReviewHelp" |
2522 ) | 2523 ) |
2523 parser.option_groups.remove(parser.get_option_group('--p4_port')) | 2524 parser.option_groups.remove(parser.get_option_group('--p4_port')) |
2524 parser.print_help() | 2525 parser.print_help() |
2525 sys.exit(0) | 2526 sys.exit(0) |
2526 | 2527 |
2527 global verbosity | 2528 global verbosity |
2528 verbosity = options.verbose | 2529 verbosity = options.verbose |
2529 if verbosity >= 3: | 2530 if verbosity >= 3: |
2530 logging.getLogger().setLevel(logging.DEBUG) | 2531 LOGGER.setLevel(logging.DEBUG) |
2531 elif verbosity >= 2: | 2532 elif verbosity >= 2: |
2532 logging.getLogger().setLevel(logging.INFO) | 2533 LOGGER.setLevel(logging.INFO) |
2533 | 2534 |
2534 vcs = GuessVCS(options) | 2535 vcs = GuessVCS(options) |
2535 | 2536 |
2536 base = options.base_url | 2537 base = options.base_url |
2537 if isinstance(vcs, SubversionVCS): | 2538 if isinstance(vcs, SubversionVCS): |
2538 # Guessing the base field is only supported for Subversion. | 2539 # Guessing the base field is only supported for Subversion. |
2539 # Note: Fetching base files may become deprecated in future releases. | 2540 # Note: Fetching base files may become deprecated in future releases. |
2540 guessed_base = vcs.GuessBase(options.download_base) | 2541 guessed_base = vcs.GuessBase(options.download_base) |
2541 if base: | 2542 if base: |
2542 if guessed_base and base != guessed_base: | 2543 if guessed_base and base != guessed_base: |
2543 print "Using base URL \"%s\" from --base_url instead of \"%s\"" % \ | 2544 print "Using base URL \"%s\" from --base_url instead of \"%s\"" % \ |
2544 (base, guessed_base) | 2545 (base, guessed_base) |
2545 else: | 2546 else: |
2546 base = guessed_base | 2547 base = guessed_base |
2547 | 2548 |
2548 if not base and options.download_base: | 2549 if not base and options.download_base: |
2549 options.download_base = True | 2550 options.download_base = True |
2550 logging.info("Enabled upload of base file") | 2551 LOGGER.info("Enabled upload of base file") |
2551 if not options.assume_yes: | 2552 if not options.assume_yes: |
2552 vcs.CheckForUnknownFiles() | 2553 vcs.CheckForUnknownFiles() |
2553 if data is None: | 2554 if data is None: |
2554 data = vcs.GenerateDiff(args) | 2555 data = vcs.GenerateDiff(args) |
2555 data = vcs.PostProcessDiff(data) | 2556 data = vcs.PostProcessDiff(data) |
2556 if options.print_diffs: | 2557 if options.print_diffs: |
2557 print "Rietveld diff start:*****" | 2558 print "Rietveld diff start:*****" |
2558 print data | 2559 print data |
2559 print "Rietveld diff end:*****" | 2560 print "Rietveld diff end:*****" |
2560 files = vcs.GetBaseFiles(data) | 2561 files = vcs.GetBaseFiles(data) |
(...skipping 11 matching lines...) Expand all Loading... |
2572 options.open_oauth2_local_webbrowser) | 2573 options.open_oauth2_local_webbrowser) |
2573 form_fields = [] | 2574 form_fields = [] |
2574 | 2575 |
2575 repo_guid = vcs.GetGUID() | 2576 repo_guid = vcs.GetGUID() |
2576 if repo_guid: | 2577 if repo_guid: |
2577 form_fields.append(("repo_guid", repo_guid)) | 2578 form_fields.append(("repo_guid", repo_guid)) |
2578 if base: | 2579 if base: |
2579 b = urlparse.urlparse(base) | 2580 b = urlparse.urlparse(base) |
2580 username, netloc = urllib.splituser(b.netloc) | 2581 username, netloc = urllib.splituser(b.netloc) |
2581 if username: | 2582 if username: |
2582 logging.info("Removed username from base URL") | 2583 LOGGER.info("Removed username from base URL") |
2583 base = urlparse.urlunparse((b.scheme, netloc, b.path, b.params, | 2584 base = urlparse.urlunparse((b.scheme, netloc, b.path, b.params, |
2584 b.query, b.fragment)) | 2585 b.query, b.fragment)) |
2585 form_fields.append(("base", base)) | 2586 form_fields.append(("base", base)) |
2586 if options.issue: | 2587 if options.issue: |
2587 form_fields.append(("issue", str(options.issue))) | 2588 form_fields.append(("issue", str(options.issue))) |
2588 if options.email: | 2589 if options.email: |
2589 form_fields.append(("user", options.email)) | 2590 form_fields.append(("user", options.email)) |
2590 if options.reviewers: | 2591 if options.reviewers: |
2591 for reviewer in options.reviewers.split(','): | 2592 for reviewer in options.reviewers.split(','): |
2592 CheckReviewer(reviewer) | 2593 CheckReviewer(reviewer) |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2699 os.environ['LC_ALL'] = 'C' | 2700 os.environ['LC_ALL'] = 'C' |
2700 RealMain(sys.argv) | 2701 RealMain(sys.argv) |
2701 except KeyboardInterrupt: | 2702 except KeyboardInterrupt: |
2702 print | 2703 print |
2703 StatusUpdate("Interrupted.") | 2704 StatusUpdate("Interrupted.") |
2704 sys.exit(1) | 2705 sys.exit(1) |
2705 | 2706 |
2706 | 2707 |
2707 if __name__ == "__main__": | 2708 if __name__ == "__main__": |
2708 main() | 2709 main() |
OLD | NEW |