#!/usr/bin/python3

# Given a merge proposal URL (eg.
# https://code.launchpad.net/~paelzer/ubuntu/+source/open-vm-tools/+git/open-vm-tools/+merge/352237)
# this script clones the proposer's branch, upload tags it, and pushes the
# upload tag to the official repository. This can be used by members of
# ~git-ubuntu-import to easily push an upload tag for contributors, until we
# have a mechanism to do this better.
#
# This does minimal checking. The target repository must be correct; otherwise
# the upload tag will be pushed to the wrong place. There are races (for
# example if the proposer pushes a new commit after you reviewed but before
# this script completes). However this script only pushes upload tags, so is
# generally safe since the importer always verifies before incorporating an
# upload tag. We intend to allow uploaders to push upload tags (or their
# equivalent) directly anyway; this is a stop-gap because using the current tag
# mechanism is awkward because Launchpad doesn't currently have per-ref ACLs.
#
# Prerequisites:
#   python3-launchpadlib installed
#   You have a Launchpad login that # belongs to ~git-ubuntu-import

import os
import subprocess
import sys
import tempfile

import launchpadlib.launchpad


def split_merge_url(url):
    """Given an LP merge proposal URL, extract its essential components

    This is a hack. Given something like
    https://code.launchpad.net/~paelzer/ubuntu/+source/open-vm-tools/+git/open-vm-tools/+merge/352237,
    this works out the lp username, package name and "merge id" of the
    corresponding MP, for use in further hacky API calls.

    Returns a tuple of (lpuser, pkgname, mergeid) (all strings).
    """
    assert url.startswith('https://code.launchpad.net/')
    trail = url[len('https://code.launchpad.net/'):]
    lpuser, ubuntu, source, pkgname1, plusgit, pkgname2, plusmerge, mergeid = trail.split('/')
    assert ubuntu == 'ubuntu'
    assert source == '+source'
    assert pkgname1 == pkgname2
    assert plusgit == '+git'
    assert plusmerge == '+merge'
    return lpuser, pkgname1, mergeid


def insert_username_into_git_ssh_url(lp, url):
    """Given a Launchpad object and a git_ssh repository URL, insert the user
    to authenticate with

    input url example: 'git+ssh://git.launchpad.net/ubuntu/+source/open-vm-tools'
    return value example: 'git+ssh://racb@git.launchpad.net/ubuntu/+source/open-vm-tools'
    """
    assert url.startswith('git+ssh://git.launchpad.net')
    trail = url[len('git+ssh://git.launchpad.net'):]
    return 'git+ssh://' + lp.me.name + '@git.launchpad.net' + trail


def get_merge_proposal(lp, url):
    """Given a Launchpad object and a web interface MP URL, return the
    corresponding Launchpad API branch_merge_proposal object
    """
    user, pkgname1, mergeid = split_merge_url(url)
    return lp.load('https://api.launchpad.net/devel/{user}/ubuntu/+source/{pkgname}/+git/{pkgname}/+merge/{mergeid}'.format(user=user, pkgname=pkgname1, mergeid=mergeid))


def clone_tag_and_push(url):
    # Log in and get our lp object
    lp = launchpadlib.launchpad.Launchpad.login_with(
        'git-ubuntu-auto-upload-tag', 'production', version='devel'
    )

    # Get the merge proposal lp object
    mp = get_merge_proposal(lp, url)

    # Figure out the proposal's git repository URL and branch name and clone it
    git_url = mp.source_git_repository.git_https_url
    git_ref = mp.source_git_path
    assert git_ref.startswith('refs/heads/')
    git_branch = git_ref[len('refs/heads/'):]
    subprocess.check_call(['git', 'clone', '-b', git_branch, git_url, 'git'])

    # Figure out the upload tag name and tag it
    tag = subprocess.check_output(['git', 'ubuntu', 'tag', '--print-name-only', '--upload'], cwd='git').strip().decode()
    subprocess.check_call(['git', 'tag', tag, 'HEAD'], cwd='git')

    # Figure out the target repository for the upload tag push
    lpuser, pkgname, mergeid = split_merge_url(url)
    target_git_repo = mp.target_git_repository
    target = target_git_repo.target

    # Check the target repository we've worked out is sane
    assert target.resource_type_link == 'https://api.launchpad.net/devel/#distribution_source_package'
    assert target.name == pkgname

    # Push the upload tag
    subprocess.check_call(['echo', 'git', 'push', insert_username_into_git_ssh_url(lp, target_git_repo.git_ssh_url), tag], cwd='git')


def main(url):
    with tempfile.TemporaryDirectory() as d:
        os.chdir(d)
        clone_tag_and_push(url)


if __name__ == '__main__':
    main(sys.argv[1])
