#!/usr/bin/python
# vim: set fileencoding=utf-8 :
#
# (C) 2009,2012,2015,2016 Guido Guenther <agx@sigxcpu.org>
#
# gbp-posttag-push: post tag hook to be called by git-buildpackage to push out
# the newly created tag and to forward the remote branch to that position
#
# it checks for explicit push destinations, if none are found it pushes back to
# where the branch got merged from. Before pushing it checks if the tag is
# signed.
#
# use:
# [buildpackage]
# posttag = gbp-posttag-push
#
# Options:
# -d: dry-run
# -u: push upstream branch too, if not on remote already
# --verbose: verbose command output

from __future__ import print_function

import glob
import os
import subprocess
import sys

import gbp.log
from gbp.command_wrappers import Command, CommandExecFailed
from gbp.config import GbpOptionParserDebian
from gbp.deb.git import DebianGitRepository
from gbp.deb.source import DebianSource
from gbp.errors import GbpError
from gbp.git.vfs import GitVfs
from gbp.scripts.common import ExitCodes


class Env(object):
    pass


def get_push_targets(env):
    """get a list of push targets"""
    dests = {}
    cmd = "git config --get-regexp 'remote\..*\.push' '^%s(:.*)?$'" % env.branch
    for remote in subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].split("\n"):
        if not len(remote):
            continue
        repo, refspec = remote.split()
        repo = ".".join(repo.split('.')[1:-1])  # remote.<repo>.push
        try:
            remote = refspec.split(':')[1]  # src:dest
        except IndexError:
            remote = refspec
        dests[repo] = remote
    return dests


def get_pull(env):
    """where did we pull from?"""
    cmd = 'git config --get branch."%s".remote' % env.branch
    remote = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).communicate()[0].strip()
    if not remote:
        remote = 'origin'
    return {remote: env.branch}


def git_push_sim(*args):
    print("git push %s" % " ".join(args))


def get_upstream_tag(repo, tag, tag_format):
    # FIXME: This assumes the debian version is the last part after the slash:
    version = tag.split('/')[-1]
    upstream = version.rsplit('-')[0]
    tag = tag_format % dict(version=upstream)
    if repo.has_tag(tag):
        return tag
    return None


def build_parser(name):

    # Until we moved this out of examples
    os.environ['GBP_DISABLE_SECTION_DEPRECTATION'] = '1'
    GbpOptionParserDebian.defaults['upload-cmd'] = ""
    GbpOptionParserDebian.help['upload-cmd'] = "Upload command, default is '%(upload-cmd)s'"

    try:
        parser = GbpOptionParserDebian(command=os.path.basename(name),
                                       usage='%prog [options]')
    except GbpError as err:
        gbp.log.err(err)
        return None

    parser.add_option("-d", "--dry-run", dest="dryrun", default=False,
                      action="store_true", help="dry run, don't push.")
    parser.add_option("-u", "--push-upstream", dest="push_upstream",
                      default=False,
                      action="store_true",
                      help="also push upstream branch changes")
    parser.add_config_file_option(option_name="upstream-branch",
                                  dest="upstream_branch")
    parser.add_config_file_option(option_name="upstream-tag",
                                  dest="upstream_tag")
    parser.add_config_file_option(option_name="debian-tag",
                                  dest="debian_tag")
    parser.add_config_file_option(option_name="upload-cmd",
                                  dest="upload_cmd")
    parser.add_config_file_option(option_name="color", dest="color", type='tristate')
    parser.add_config_file_option(option_name="color-scheme",
                                  dest="color_scheme")
    parser.add_option("--verbose", action="store_true", dest="verbose",
                      default=False, help="verbose command execution")
    return parser


def parse_args(argv):
    parser = build_parser(argv[0])
    if not parser:
        return None, None
    return parser.parse_args(argv)


def find_changes(sourcename, version):
    glob_ex = "../%s_%s_*.changes" % (sourcename, version)
    gbp.log.info("Looking for changes at %s" % glob_ex)
    return glob.glob(glob_ex)


def upload_changes(changes, cmd, dryrun):
    ret = 0
    for name in changes:
        gbp.log.info("Running %s" % " ".join([cmd, name]))
        try:
            if not dryrun:
                Command(cmd, [name], shell=False)()
        except CommandExecFailed:
            gbp.log.err("Upload of '%s' failed." % name)
            ret = 1
    return ret


def main(argv):
    retval = 0
    env = Env()
    upstream_sha1 = None

    (options, args) = parse_args(argv)
    if not options:
        return ExitCodes.parse_error

    gbp.log.setup(options.color, options.verbose, options.color_scheme)

    repo = DebianGitRepository('.')
    if options.dryrun:
        gbp.log.warn("Dry run mode. Not pushing, not uploading.")
        repo.push = git_push_sim
        repo.push_tag = git_push_sim

    for envvar in ["GBP_TAG", "GBP_BRANCH", "GBP_SHA1"]:
        var = os.getenv(envvar)
        if var:
            env.__dict__.setdefault("%s" % envvar.split("_")[1].lower(), var)
        else:
            gbp.log.err("%s not set." % envvar, file=sys.stderr)
            return 1

    dests = get_push_targets(env)
    if not dests:
        dests = get_pull(env)

    upstream_tag = get_upstream_tag(repo, env.tag, options.upstream_tag)
    if upstream_tag:
        upstream_sha1 = repo.rev_parse("%s^{}" % upstream_tag)

    if not repo.verify_tag(env.tag):
        gbp.log.info("Not pushing non-existent or unsigned tag '%s'." % env.tag, file=sys.stderr)
        return 0

    source = DebianSource(GitVfs(repo, env.tag))

    for dest in dests:
        gbp.log.info("Pushing %s to %s" % (env.tag, dest))
        repo.push_tag(dest, env.tag)
        gbp.log.info("Pushing %s to %s %s" % (env.sha1, dest, dests[dest]))
        repo.push(dest, env.sha1, dests[dest])
        if options.push_upstream and upstream_tag:
            gbp.log.info("Pushing %s to %s" % (upstream_tag, dest))
            repo.push_tag(dest, upstream_tag)
            if not repo.branch_contains("%s/%s" % (dest, options.upstream_branch),
                                        upstream_sha1, remote=True):
                gbp.log.info("Pushing %s to %s %s" % (upstream_sha1, dest, options.upstream_branch))
                repo.push(dest, upstream_sha1, options.upstream_branch)

    if options.upload_cmd:
        version = repo.tag_to_version(env.tag, options.debian_tag)
        changes = find_changes(source.sourcepkg, version)
        if len(changes):
            retval = upload_changes(changes, options.upload_cmd, options.dryrun)
        else:
            gbp.log.info("No changes file found, nothing to upload.")
    else:
        gbp.log.info("No upload command defined, not uploading to the archive")
    return retval


if __name__ == '__main__':
    sys.exit(main(sys.argv))

# vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·:
