#!/usr/bin/python
# Copyright (c) 2009 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import getpass
import optparse
import os
import subprocess
import tempfile
import traceback
import urllib
import sys
import re
import trychange


def Backquote(cmd):
  """Like running `cmd` in a shell script."""
  return subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0].strip()


def GetTryServerConfig():
  """Returns the dictionary of try server options or None if they
  cannot be found."""
  script_path = 'tools/tryserver/tryserver.py'
  root_dir = Backquote(['git', 'rev-parse', '--show-cdup'])
  try:
    script_file = open(os.path.join(root_dir, script_path))
  except IOError:
    return None
  locals = {}
  try:
    exec(script_file, locals)
  except Exception, e:
    return None
  return locals


def GetBranchName():
  """Return name of current git branch."""
  branch = Backquote(['git', 'symbolic-ref', 'HEAD'])
  if not branch.startswith('refs/heads/'):
    raise "Couldn't figure out branch name"
  branch = branch[len('refs/heads/'):]
  return branch


def GetPatchName():
  """Construct a name for this patch."""
  short_sha = Backquote(['git', 'rev-parse', '--short=4', 'HEAD'])
  return GetBranchName() + '-' + short_sha


def GetRietveldIssueNumber():
  return Backquote(['git', 'config',
                    'branch.%s.rietveldissue' % GetBranchName()])


def GetRietveldPatchsetNumber():
  return Backquote(['git', 'config',
                    'branch.%s.rietveldpatchset' % GetBranchName()])


def GetMungedDiff(branch):
  """Get the diff we'll send to the try server.  We munge paths to match svn."""
  # Make the following changes:
  # - Prepend "src/" to paths as svn is expecting
  # - In the case of added files, replace /dev/null with the path to the file
  #   being added.
  output = []
  if not branch:
    # Try to guess the upstream branch.
    branch = Backquote(['git', 'cl', 'upstream'])
  diff = subprocess.Popen(['git', 'diff-tree', '-p', '--no-prefix',
                           branch, 'HEAD'],
                          stdout=subprocess.PIPE).stdout.readlines()
  for i in range(len(diff)):
    line = diff[i]
    if line.startswith('--- /dev/null'):
      line = '--- %s' % diff[i+1][4:]
    elif line.startswith('--- ') or line.startswith('+++ '):
      line = line[0:4] + 'src/' + line[4:]
    output.append(line)

  munged_diff = ''.join(output)
  if len(munged_diff.strip()) == 0:
    raise Exception("Patch was empty, did you give the right remote branch?")

  return munged_diff


def ValidEmail(email):
  return re.match(r"^[a-zA-Z0-9._%-+]+@[a-zA-Z0-9._%-]+.[a-zA-Z]{2,6}$", email)


def GetEmail():
  email = Backquote(['git', 'config', 'user.email'])
  runmsg = "Try: git config user.email <EMAIL>"
  assert ValidEmail(email), "Email '%s' is not valid.  %s" % (email, runmsg)
  return email


def TryChange(args):
  """Put a patch on the try server."""
  root_dir = Backquote(['git', 'rev-parse', '--show-cdup'])
  trychange.checkout_root = os.path.abspath(root_dir)
  trychange.TryChange(args, None, False)


if __name__ == '__main__':
  parser = optparse.OptionParser(
      usage='git try [options] [branch]',
      description='Upload the current diff of branch...HEAD to the try server.')
  parser.add_option("-b", "--bot",
                    help="Force the use of a specific build slave (eg mac, "
                         "win, or linux)")
  parser.add_option("-c", "--clobber", action="store_true",
                    help="Make the try run use be a clobber build")
  parser.add_option("-r", "--revision",
                    help="Specify the SVN base revision to use")
  (options, args) = parser.parse_args(sys.argv)

  branch = None
  if len(args) > 1:
    branch = args[1]

  patch_name = GetPatchName()
  diff = GetMungedDiff(branch)

  # Write the diff out to a temporary file
  diff_file = tempfile.NamedTemporaryFile()
  diff_file.write(diff)
  diff_file.flush()

  email = GetEmail()
  user = email.partition('@')[0]
  args = [
      '-u', user,
      '-e', email,
      '-n', patch_name,
      '--diff', diff_file.name,
  ]

  # Send to try server via HTTP if we can parse the config, otherwise
  # upload via SVN.
  config = GetTryServerConfig()
  if config is not None:
    sendmsg = "Sending %s using HTTP..." % patch_name
    args.extend(['--use_http'])
    if config['try_server_http_host'] is not None:
      args.extend(['--host', config['try_server_http_host']])
    if config['try_server_http_port'] is not None:
      args.extend(['--port', config['try_server_http_port']])

  else:
    print "Could not get server config -- if you're within Google, "
    print "do you have have src-internal checked out?"
    sendmsg = "Sending %s using SVN..." % patch_name
    args.extend([
        '--use_svn', '--svn_repo',
        'svn://svn.chromium.org/chrome-try/try',
    ])

  if options.bot:
    args.extend(['--bot', options.bot])
  if options.clobber:
    args.append('--clobber')
  if options.revision:
    args.extend(['-r', options.revision])
  if GetRietveldPatchsetNumber():
    args.extend([
        '--issue', GetRietveldIssueNumber(),
        '--patchset', GetRietveldPatchsetNumber(),
    ])

  print sendmsg
  TryChange(args=args)
