Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(56)

Side by Side Diff: static/upload.py

Issue 974: accept patches from mercurial SVN Base: http://rietveld.googlecode.com/svn/trunk
Patch Set: Created 3 months, 1 week ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # 2 #
3 # Copyright 2007 Google Inc. 3 # Copyright 2007 Google Inc.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
11 # Unless required by applicable law or agreed to in writing, software 11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS, 12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and 14 # See the License for the specific language governing permissions and
15 # limitations under the License. 15 # limitations under the License.
16 16
17 """Tool for uploading subversion diffs to the codereview app. 17 """Tool for uploading subversion diffs to the codereview app.
18 18
19 Usage summary: upload.py [options] [-- svn_diff_options] 19 Usage summary: upload.py [options] [-- svn_diff_options]
20 """ 20 """
21 # This code is derived from appcfg.py in the App Engine SDK (open source), 21 # This code is derived from appcfg.py in the App Engine SDK (open source),
22 # and from ASPN recipe #146306. 22 # and from ASPN recipe #146306.
23 23
24 import cookielib 24 import cookielib
25 import getpass 25 import getpass
26 import logging 26 import logging
27 import mimetypes 27 import mimetypes
28 import optparse 28 import optparse
29 import os 29 import os
30 import socket 30 import socket
31 import sys 31 import sys
32 import urllib 32 import urllib
33 import urllib2 33 import urllib2
34 import urlparse 34 import urlparse
35 35
36 36
37 # The logging verbosity: 37 # The logging verbosity:
38 # 0: Errors only. 38 # 0: Errors only.
39 # 1: Status messages. 39 # 1: Status messages.
40 # 2: Info logs. 40 # 2: Info logs.
41 # 3: Debug logs. 41 # 3: Debug logs.
42 verbosity = 1 42 verbosity = 1
43 43
44 44
45 def StatusUpdate(msg): 45 def StatusUpdate(msg):
46 """Print a status message to stdout. 46 """Print a status message to stdout.
47 47
48 If 'verbosity' is greater than 0, print the message. 48 If 'verbosity' is greater than 0, print the message.
49 49
50 Args: 50 Args:
(...skipping 382 matching lines...) Show 10 above Show 10 below
433 lines.append(value) 433 lines.append(value)
434 lines.append('--' + BOUNDARY + '--') 434 lines.append('--' + BOUNDARY + '--')
435 lines.append('') 435 lines.append('')
436 body = CRLF.join(lines) 436 body = CRLF.join(lines)
437 content_type = 'multipart/form-data; boundary=%s' % BOUNDARY 437 content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
438 return content_type, body 438 return content_type, body
439 439
440 440
441 def GetContentType(filename): 441 def GetContentType(filename):
442 """Helper to guess the content-type from the filename.""" 442 """Helper to guess the content-type from the filename."""
443 return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 443 return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
444 444
445 445
446 def RunShell(command, args=(), silent_ok=False): 446 def RunShell(command, args=(), silent_ok=False):
447 logging.info("Running %s", command) 447 logging.info("Running %s", command)
448 stream = os.popen("%s %s" % (command, " ".join(args)), "r") 448 stream = os.popen("%s %s" % (command, " ".join(args)), "r")
449 data = stream.read() 449 data = stream.read()
450 if stream.close(): 450 if stream.close():
451 ErrorExit("Got error status from %s" % command) 451 ErrorExit("Got error status from %s" % command)
452 if not silent_ok and not data: 452 if not silent_ok and not data:
453 ErrorExit("No output from %s" % command) 453 ErrorExit("No output from %s" % command)
454 return data 454 return data
455 455
456 456
457 def GuessBase(): 457 def GuessBase():
458 info = RunShell("svn info") 458 info = RunShell("svn info")
459 for line in info.splitlines(): 459 for line in info.splitlines():
460 words = line.split() 460 words = line.split()
461 if len(words) == 2 and words[0] == "URL:": 461 if len(words) == 2 and words[0] == "URL:":
462 url = words[1] 462 url = words[1]
463 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url) 463 scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
464 if netloc.endswith("svn.python.org"): 464 if netloc.endswith("svn.python.org"):
465 if netloc == "svn.python.org": 465 if netloc == "svn.python.org":
466 if path.startswith("/projects/"): 466 if path.startswith("/projects/"):
467 path = path[9:] 467 path = path[9:]
468 elif netloc != "pythondev@svn.python.org": 468 elif netloc != "pythondev@svn.python.org":
469 ErrorExit("Unrecognized Python URL: %s" % url) 469 ErrorExit("Unrecognized Python URL: %s" % url)
470 base = "http://svn.python.org/view/*checkout*%s/" % path 470 base = "http://svn.python.org/view/*checkout*%s/" % path
471 logging.info("Guessed Python base = %s", base) 471 logging.info("Guessed Python base = %s", base)
472 elif netloc.endswith("svn.collab.net"): 472 elif netloc.endswith("svn.collab.net"):
473 if path.startswith("/repos/"): 473 if path.startswith("/repos/"):
474 path = path[6:] 474 path = path[6:]
475 base = "http://svn.collab.net/viewvc/*checkout*%s/" % path 475 base = "http://svn.collab.net/viewvc/*checkout*%s/" % path
476 logging.info("Guessed CollabNet base = %s", base) 476 logging.info("Guessed CollabNet base = %s", base)
477 elif netloc.endswith(".googlecode.com"): 477 elif netloc.endswith(".googlecode.com"):
478 base = url + "/" 478 base = url + "/"
479 if base.startswith("https"): 479 if base.startswith("https"):
480 base = "http" + base[5:] 480 base = "http" + base[5:]
481 logging.info("Guessed Google Code base = %s", base) 481 logging.info("Guessed Google Code base = %s", base)
482 else: 482 else:
483 ErrorExit("Unrecognized svn project root: %s" % url) 483 base = url + "/"
484 #print netloc
485 #ErrorExit("Unrecognized svn project root: %s" % url)
484 return base 486 return base
485 ErrorExit("Can't find URL in output from svn info") 487 ErrorExit("Can't find URL in output from svn info")
486 488
487 489
488 def RealMain(argv): 490 def RealMain(argv):
489 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:" 491 logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:"
490 "%(lineno)s %(message)s ")) 492 "%(lineno)s %(message)s "))
491 options, args = parser.parse_args(sys.argv[1:]) 493 options, args = parser.parse_args(sys.argv[1:])
492 global verbosity 494 global verbosity
493 verbosity = options.verbose 495 verbosity = options.verbose
494 if verbosity >= 3: 496 if verbosity >= 3:
495 logging.getLogger().setLevel(logging.DEBUG) 497 logging.getLogger().setLevel(logging.DEBUG)
496 elif verbosity >= 2: 498 elif verbosity >= 2:
497 logging.getLogger().setLevel(logging.INFO) 499 logging.getLogger().setLevel(logging.INFO)
498 base = GuessBase() 500 #base = GuessBase()
499 CheckForUnknownFiles() 501 base = "http://svn.scipy.org/svn/numpy/trunk"
500 data = RunShell("svn diff", args) 502 #CheckForUnknownFiles()
503 data = RunShell("hg diff", args)
504 #print data
501 count = 0 505 count = 0
502 for line in data.splitlines(): 506 for line in data.splitlines():
503 if line.startswith("Index:"): 507 if line.startswith("diff"):
504 count += 1 508 count += 1
505 logging.info(line) 509 logging.info(line)
506 if not count: 510 if not count:
507 ErrorExit("No valid patches found in output from svn diff") 511 ErrorExit("No valid patches found in output from svn diff")
508 if options.issue: 512 if options.issue:
509 prompt = "Message describing this patch set: " 513 prompt = "Message describing this patch set: "
510 else: 514 else:
511 prompt = "New issue subject: " 515 prompt = "New issue subject: "
512 message = options.message or raw_input(prompt).strip() 516 message = options.message or raw_input(prompt).strip()
513 if not message: 517 if not message:
514 ErrorExit("A non-empty message is required") 518 ErrorExit("A non-empty message is required")
515 rpc_server = GetRpcServer(options) 519 rpc_server = GetRpcServer(options)
516 form_fields = [("base", base), ("subject", message)] 520 form_fields = [("base", base), ("subject", message)]
517 if options.issue: 521 if options.issue:
518 form_fields.append(("issue", str(options.issue))) 522 form_fields.append(("issue", str(options.issue)))
519 if options.email: 523 if options.email:
520 form_fields.append(("user", options.email)) 524 form_fields.append(("user", options.email))
521 ctype, body = EncodeMultipartFormData(form_fields, 525 ctype, body = EncodeMultipartFormData(form_fields,
522 [("data", "data.diff", data)]) 526 [("data", "data.diff", data)])
523 response_body = rpc_server.Send("/upload", body, content_type=ctype) 527 response_body = rpc_server.Send("/upload", body, content_type=ctype)
524 StatusUpdate(response_body) 528 StatusUpdate(response_body)
525 sys.exit(not response_body.startswith("Issue created.")) 529 sys.exit(not response_body.startswith("Issue created."))
526 530
527 def CheckForUnknownFiles(): 531 def CheckForUnknownFiles():
528 status = RunShell("svn status --ignore-externals", silent_ok=True) 532 status = RunShell("svn status --ignore-externals", silent_ok=True)
529 unknown_files = [] 533 unknown_files = []
530 for line in status.split("\n"): 534 for line in status.split("\n"):
531 if line and line[0] == "?": 535 if line and line[0] == "?":
532 unknown_files.append(line) 536 unknown_files.append(line)
533 if unknown_files: 537 if unknown_files:
534 print "The following files are not added to version control:" 538 print "The following files are not added to version control:"
535 for line in unknown_files: 539 for line in unknown_files:
536 print line 540 print line
537 prompt = "Are you sure to continue?(y/N) " 541 prompt = "Are you sure to continue?(y/N) "
538 answer = raw_input(prompt).strip() 542 answer = raw_input(prompt).strip()
539 if answer != "y": 543 if answer != "y":
540 ErrorExit("User aborted") 544 ErrorExit("User aborted")
541 545
542 def main(): 546 def main():
543 try: 547 try:
544 RealMain(sys.argv) 548 RealMain(sys.argv)
545 except KeyboardInterrupt: 549 except KeyboardInterrupt:
546 print 550 print
547 StatusUpdate("Interrupted.") 551 StatusUpdate("Interrupted.")
548 sys.exit(1) 552 sys.exit(1)
549 553
550 554
551 if __name__ == "__main__": 555 if __name__ == "__main__":
552 main() 556 main()
OLDNEW

Powered by Google App Engine
This is Rietveld r292