Blame tools/migrate-lp-user-to-github

Packit Service 11b429
#!/usr/bin/env python3
Packit Service a04d08
"""Link your Launchpad user to github, proposing branches to LP and Github"""
Packit Service a04d08
Packit Service a04d08
from argparse import ArgumentParser
Packit Service a04d08
from subprocess import Popen, PIPE
Packit Service a04d08
import os
Packit Service a04d08
import sys
Packit Service a04d08
Packit Service a04d08
try:
Packit Service a04d08
    from launchpadlib.launchpad import Launchpad
Packit Service a04d08
except ImportError:
Packit Service a04d08
    print("Missing python launchpadlib dependency to create branches for you."
Packit Service a04d08
          "Install with: sudo apt-get install python3-launchpadlib" )
Packit Service a04d08
    sys.exit(1)
Packit Service a04d08
Packit Service a04d08
if "avoid-pep8-E402-import-not-top-of-file":
Packit Service a04d08
    _tdir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
Packit Service a04d08
    sys.path.insert(0, _tdir)
Packit Service a04d08
    from cloudinit import util
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
DRYRUN = False
Packit Service a04d08
LP_TO_GIT_USER_FILE='.lp-to-git-user'
Packit Service a04d08
MIGRATE_BRANCH_NAME='migrate-lp-to-github'
Packit Service a04d08
GITHUB_PULL_URL='https://github.com/canonical/cloud-init/compare/master...{github_user}:{branch}'
Packit Service a04d08
GH_UPSTREAM_URL='https://github.com/canonical/cloud-init'
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def error(message):
Packit Service a04d08
    if isinstance(message, bytes):
Packit Service a04d08
        message = message.decode('utf-8')
Packit Service a04d08
    log('ERROR: {error}'.format(error=message))
Packit Service a04d08
    sys.exit(1)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def log(message):
Packit Service a04d08
    print(message)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def subp(cmd, skip=False):
Packit Service a04d08
    prefix = 'SKIPPED: ' if skip else '$ '
Packit Service a04d08
    log('{prefix}{command}'.format(prefix=prefix, command=' '.join(cmd)))
Packit Service a04d08
    if skip:
Packit Service a04d08
        return
Packit Service a04d08
    proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
Packit Service a04d08
    out, err = proc.communicate()
Packit Service a04d08
    if proc.returncode:
Packit Service a04d08
        error(err if err else out)
Packit Service a04d08
    return out.decode('utf-8')
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
LP_GIT_PATH_TMPL = 'git+ssh://{launchpad_user}@git.launchpad.net/'
Packit Service a04d08
LP_UPSTREAM_PATH_TMPL = LP_GIT_PATH_TMPL + 'cloud-init'
Packit Service a04d08
LP_REMOTE_PATH_TMPL = LP_GIT_PATH_TMPL + '~{launchpad_user}/cloud-init'
Packit Service a04d08
GITHUB_REMOTE_PATH_TMPL = 'git@github.com:{github_user}/cloud-init.git'
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
# Comment templates
Packit Service a04d08
COMMIT_MSG_TMPL = '''\
Packit Service a04d08
lp-to-git-users: adding {gh_username}
Packit Service a04d08
Packit Service a04d08
Mapped from {lp_username}
Packit Service a04d08
'''
Packit Service a04d08
PUBLISH_DIR='/tmp/cloud-init-lp-to-github-migration'
Packit Service a04d08
Packit Service a04d08
def get_parser():
Packit Service a04d08
    parser = ArgumentParser(description=__doc__)
Packit Service a04d08
    parser.add_argument(
Packit Service a04d08
        '--dryrun', required=False, default=False, action='store_true',
Packit Service a04d08
        help=('Run commands and review operation in dryrun mode, '
Packit Service a04d08
              'making not changes.'))
Packit Service a04d08
    parser.add_argument('launchpad_user', help='Your launchpad username.')
Packit Service a04d08
    parser.add_argument('github_user', help='Your github username.')
Packit Service a04d08
    parser.add_argument(
Packit Service a04d08
        '--local-repo-dir', required=False, dest='repo_dir',
Packit Service a04d08
        help=('The name of the local directory into which we clone.'
Packit Service a04d08
              ' Default: {}'.format(PUBLISH_DIR)))
Packit Service a04d08
    parser.add_argument(
Packit Service a04d08
        '--upstream-branch', required=False, dest='upstream',
Packit Service a04d08
        default='origin/master',
Packit Service a04d08
        help=('The name of remote branch target into which we will merge.'
Packit Service a04d08
              ' Default: origin/master'))
Packit Service a04d08
    parser.add_argument(
Packit Service a04d08
        '-v', '--verbose', required=False, default=False, action='store_true',
Packit Service a04d08
        help=('Print all actions.'))
Packit Service a04d08
    return parser
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def create_publish_branch(upstream, publish_branch):
Packit Service a04d08
    '''Create clean publish branch target in the current git repo.'''
Packit Service a04d08
    branches = subp(['git', 'branch'])
Packit Service a04d08
    upstream_remote, upstream_branch = upstream.split('/', 1)
Packit Service a04d08
    subp(['git', 'checkout', upstream_branch])
Packit Service a04d08
    subp(['git', 'pull'])
Packit Service a04d08
    if publish_branch in branches:
Packit Service a04d08
        subp(['git', 'branch', '-D', publish_branch])
Packit Service a04d08
    subp(['git', 'checkout', upstream, '-b', publish_branch])
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def add_lp_and_github_remotes(lp_user, gh_user):
Packit Service a04d08
    """Add lp and github remotes if not present.
Packit Service a04d08
Packit Service a04d08
    @return Tuple with (lp_remote_name, gh_remote_name)
Packit Service a04d08
    """
Packit Service a04d08
    lp_remote = LP_REMOTE_PATH_TMPL.format(launchpad_user=lp_user)
Packit Service a04d08
    gh_remote = GITHUB_REMOTE_PATH_TMPL.format(github_user=gh_user)
Packit Service a04d08
    remotes = subp(['git', 'remote', '-v'])
Packit Service a04d08
    lp_remote_name = gh_remote_name = None
Packit Service a04d08
    for remote in remotes.splitlines():
Packit Service a04d08
        if not remote:
Packit Service a04d08
            continue
Packit Service a04d08
        remote_name, remote_url, _operation = remote.split()
Packit Service a04d08
        if lp_remote == remote_url:
Packit Service a04d08
            lp_remote_name = remote_name
Packit Service a04d08
        elif gh_remote == remote_url:
Packit Service a04d08
            gh_remote_name = remote_name
Packit Service a04d08
    if not lp_remote_name:
Packit Service a04d08
        log("launchpad: Creating git remote launchpad-{} to point at your"
Packit Service a04d08
            " LP repo".format(lp_user))
Packit Service a04d08
        lp_remote_name = 'launchpad-{}'.format(lp_user)
Packit Service a04d08
        subp(['git', 'remote', 'add', lp_remote_name, lp_remote])
Packit Service 751c4a
    try:
Packit Service 751c4a
        subp(['git', 'fetch', lp_remote_name])
Packit Service 751c4a
    except:
Packit Service 751c4a
        log("launchpad: Pushing to ensure LP repo exists")
Packit Service 751c4a
        subp(['git', 'push', lp_remote_name, 'master:master'])
Packit Service 751c4a
        subp(['git', 'fetch', lp_remote_name])
Packit Service a04d08
    if not gh_remote_name:
Packit Service a04d08
        log("github: Creating git remote github-{} to point at your"
Packit Service a04d08
            " GH repo".format(gh_user))
Packit Service a04d08
        gh_remote_name = 'github-{}'.format(gh_user)
Packit Service a04d08
        subp(['git', 'remote', 'add', gh_remote_name, gh_remote])
Packit Service a04d08
    try:
Packit Service a04d08
        subp(['git', 'fetch', gh_remote_name])
Packit Service a04d08
    except:
Packit Service a04d08
        log("ERROR: [github] Could not fetch remote '{remote}'."
Packit Service a04d08
            "Please create a fork for your github user by clicking 'Fork'"
Packit Service a04d08
            " from {gh_upstream}".format(
Packit Service a04d08
                remote=gh_remote, gh_upstream=GH_UPSTREAM_URL))
Packit Service a04d08
        sys.exit(1)
Packit Service a04d08
    return (lp_remote_name, gh_remote_name)
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def create_migration_branch(
Packit Service a04d08
        branch_name, upstream, lp_user, gh_user, commit_msg):
Packit Service a04d08
    """Create an LP to Github migration branch and add lp_user->gh_user."""
Packit Service a04d08
    log("Creating a migration branch: {} adding your users".format(
Packit Service a04d08
            MIGRATE_BRANCH_NAME))
Packit Service a04d08
    create_publish_branch(upstream, MIGRATE_BRANCH_NAME)
Packit Service a04d08
    lp_to_git_map = {}
Packit Service a04d08
    lp_to_git_file = os.path.join(os.getcwd(), 'tools', LP_TO_GIT_USER_FILE)
Packit Service a04d08
    if os.path.exists(lp_to_git_file):
Packit Service a04d08
        with open(lp_to_git_file) as stream:
Packit Service a04d08
            lp_to_git_map = util.load_json(stream.read())
Packit Service a04d08
    if gh_user in lp_to_git_map.values():
Packit Service a04d08
        raise RuntimeError(
Packit Service a04d08
            "github user '{}' already in {}".format(gh_user, lp_to_git_file))
Packit Service a04d08
    if lp_user in lp_to_git_map:
Packit Service a04d08
        raise RuntimeError(
Packit Service a04d08
            "launchpad user '{}' already in {}".format(
Packit Service a04d08
                lp_user, lp_to_git_file))
Packit Service a04d08
    lp_to_git_map[lp_user] = gh_user
Packit Service a04d08
    with open(lp_to_git_file, 'w') as stream:
Packit Service a04d08
        stream.write(util.json_dumps(lp_to_git_map))
Packit Service a04d08
    subp(['git', 'add', lp_to_git_file])
Packit Service a04d08
    commit_file = os.path.join(os.path.dirname(os.getcwd()), 'commit.msg')
Packit Service a04d08
    with open(commit_file, 'wb') as stream:
Packit Service a04d08
        stream.write(commit_msg.encode('utf-8'))
Packit Service a04d08
    subp(['git', 'commit', '--all', '-F', commit_file])
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
def main():
Packit Service a04d08
    global DRYRUN
Packit Service a04d08
    global VERBOSITY
Packit Service a04d08
    parser = get_parser()
Packit Service a04d08
    args = parser.parse_args()
Packit Service a04d08
    DRYRUN = args.dryrun
Packit Service a04d08
    VERBOSITY = 1 if args.verbose else 0
Packit Service a04d08
    repo_dir = args.repo_dir or PUBLISH_DIR
Packit Service a04d08
    if not os.path.exists(repo_dir):
Packit Service a04d08
        cleanup_repo_dir = True
Packit Service a04d08
        subp(['git', 'clone',
Packit Service a04d08
              LP_UPSTREAM_PATH_TMPL.format(launchpad_user=args.launchpad_user),
Packit Service a04d08
              repo_dir])
Packit Service a04d08
    else:
Packit Service a04d08
        cleanup_repo_dir = False
Packit Service a04d08
    cwd = os.getcwd()
Packit Service a04d08
    os.chdir(repo_dir)
Packit Service 751c4a
    log("Syncing master branch with upstream")
Packit Service a04d08
    subp(['git', 'checkout', 'master'])
Packit Service a04d08
    subp(['git', 'pull'])
Packit Service a04d08
    try:
Packit Service a04d08
        lp_remote_name, gh_remote_name = add_lp_and_github_remotes(
Packit Service a04d08
            args.launchpad_user, args.github_user)
Packit Service a04d08
        commit_msg = COMMIT_MSG_TMPL.format(
Packit Service a04d08
            gh_username=args.github_user, lp_username=args.launchpad_user)
Packit Service a04d08
        create_migration_branch(
Packit Service a04d08
            MIGRATE_BRANCH_NAME, args.upstream, args.launchpad_user,
Packit Service a04d08
            args.github_user, commit_msg)
Packit Service a04d08
Packit Service a04d08
        for push_remote in (lp_remote_name, gh_remote_name):
Packit Service a04d08
            subp(['git', 'push', push_remote, MIGRATE_BRANCH_NAME, '--force'])
Packit Service a04d08
    except Exception as e:
Packit Service a04d08
        error('Failed setting up migration branches: {0}'.format(e))
Packit Service a04d08
    finally:
Packit Service a04d08
        os.chdir(cwd)
Packit Service a04d08
        if cleanup_repo_dir and os.path.exists(repo_dir):
Packit Service a04d08
            util.del_dir(repo_dir)
Packit Service a04d08
    # Make merge request on LP
Packit Service a04d08
    log("[launchpad] Automatically creating merge proposal using launchpadlib")
Packit Service a04d08
    lp = Launchpad.login_with(
Packit Service a04d08
        "server-team github-migration tool", 'production', version='devel')
Packit Service a04d08
    master = lp.git_repositories.getByPath(
Packit Service a04d08
        path='cloud-init').getRefByPath(path='master')
Packit Service a04d08
    LP_BRANCH_PATH='~{launchpad_user}/cloud-init/+git/cloud-init'
Packit Service a04d08
    lp_git_repo = lp.git_repositories.getByPath(
Packit Service a04d08
        path=LP_BRANCH_PATH.format(launchpad_user=args.launchpad_user))
Packit Service a04d08
    lp_user_migrate_branch = lp_git_repo.getRefByPath(
Packit Service a04d08
        path='refs/heads/migrate-lp-to-github')
Packit Service a04d08
    lp_merge_url = (
Packit Service a04d08
            'https://code.launchpad.net/' +
Packit Service a04d08
            LP_BRANCH_PATH.format(launchpad_user=args.launchpad_user) +
Packit Service a04d08
            '/+ref/' + MIGRATE_BRANCH_NAME)
Packit Service a04d08
    try:
Packit Service a04d08
        lp_user_migrate_branch.createMergeProposal(
Packit Service a04d08
            commit_message=commit_msg, merge_target=master, needs_review=True)
Packit Service a04d08
    except Exception:
Packit Service a04d08
        log('[launchpad] active merge proposal already exists at:\n'
Packit Service a04d08
            '{url}\n'.format(url=lp_merge_url))
Packit Service a04d08
    else:
Packit Service a04d08
        log("[launchpad] Merge proposal created at:\n{url}.\n".format(
Packit Service a04d08
            url=lp_merge_url))
Packit Service a04d08
    log("To link your account to github open your browser and"
Packit Service a04d08
        " click 'Create pull request' at the following URL:\n"
Packit Service a04d08
        "{url}".format(url=GITHUB_PULL_URL.format(
Packit Service a04d08
            github_user=args.github_user, branch=MIGRATE_BRANCH_NAME)))
Packit Service a04d08
    if os.path.exists(repo_dir):
Packit Service a04d08
        util.del_dir(repo_dir)
Packit Service a04d08
    return 0
Packit Service a04d08
Packit Service a04d08
Packit Service a04d08
if __name__ == '__main__':
Packit Service a04d08
    sys.exit(main())