lrossett / centos / centpkg

Forked from centos/centpkg 3 years ago
Clone
80f38d
# -*- coding: utf-8 -*-
80f38d
# utils.py - a module with support methods for centpkg
80f38d
#
80f38d
# Copyright (C) 2021 Red Hat Inc.
80f38d
# Author(s): Ondrej Nosek <onosek@redhat.com>
80f38d
#
80f38d
# This program is free software; you can redistribute it and/or modify it
80f38d
# under the terms of the GNU General Public License as published by the
80f38d
# Free Software Foundation; either version 2 of the License, or (at your
80f38d
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
80f38d
# the full text of the license.
80f38d
80f38d
import json
80f38d
80f38d
import git
80f38d
import requests
80f38d
from pyrpkg import rpkgError
80f38d
from requests.exceptions import ConnectionError
80f38d
from six.moves.configparser import NoOptionError, NoSectionError
80f38d
from six.moves.urllib.parse import quote_plus, urlparse
80f38d
80f38d
80f38d
def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
80f38d
    """
80f38d
    Creates a fork of the project.
80f38d
    :param logger: A logger object
80f38d
    :param base_url: a string of the URL repository
80f38d
    :param token: a string of the API token that has rights to make a fork
80f38d
    :param repo_name: a string of the repository name
80f38d
    :param namespace: a string determines a type of the repository
80f38d
    :param cli_name: string of the CLI's name (e.g. centpkg)
80f38d
    :return: a bool; True when fork was created, False when already exists
80f38d
    """
80f38d
    api_url = '{0}/api/v4'.format(base_url.rstrip('/'))
80f38d
    project_id = quote_plus("redhat/centos-stream/{0}/{1}".format(namespace, repo_name))
80f38d
    fork_url = '{0}/projects/{1}/fork'.format(api_url, project_id)
80f38d
80f38d
    headers = {
80f38d
        'PRIVATE-TOKEN': token,
80f38d
        'Accept': 'application/json',
80f38d
        'Content-Type': 'application/json'
80f38d
    }
9a602e
    # define a new repository name/path to avoid collision with other projects
9a602e
    safe_name = "centos_{0}_{1}".format(namespace, repo_name)
9a602e
    payload = json.dumps({
9a602e
        'name': safe_name,  # name of the project after forking
9a602e
        'path': safe_name,
9a602e
    })
80f38d
    try:
80f38d
        rv = requests.post(
80f38d
            fork_url, headers=headers, data=payload, timeout=60)
80f38d
    except ConnectionError as error:
80f38d
        error_msg = ('The connection to API failed while trying to '
80f38d
                     'create a new fork. The error was: {0}'.format(str(error)))
80f38d
        raise rpkgError(error_msg)
80f38d
80f38d
    try:
80f38d
        # Extract response json for debugging
80f38d
        rv_json = rv.json()
80f38d
        logger.debug("Pagure API response: '{0}'".format(rv_json))
80f38d
    except Exception:
80f38d
        pass
80f38d
80f38d
    base_error_msg = ('The following error occurred while creating a new fork: {0}')
80f38d
    if not rv.ok:
80f38d
        # fork was already created
80f38d
        if rv.status_code == 409 or rv.reason == "Conflict":
80f38d
            return False
80f38d
        # show hint for invalid, expired or revoked token
80f38d
        elif rv.status_code == 401 or rv.reason == "Unauthorized":
80f38d
            base_error_msg += '\nFor invalid or expired token refer to ' \
80f38d
                '"{0} fork -h" to set a token in your user ' \
80f38d
                'configuration.'.format(cli_name)
80f38d
        raise rpkgError(base_error_msg.format(rv.text))
80f38d
80f38d
    return True
80f38d
80f38d
80f38d
def do_add_remote(base_url, remote_base_url, username, repo, repo_name,
80f38d
                  namespace):
80f38d
    """
80f38d
    Adds remote tracked repository
80f38d
    :param base_url: a string of the URL repository
80f38d
    :param remote_base_url: a string of the remote tracked repository
80f38d
    :param username: a string of the (FAS) user name
80f38d
    :param repo: object, current project git repository
80f38d
    :param repo_name: a string of the repository name
80f38d
    :param namespace: a string determines a type of the repository
80f38d
    :return: a bool; True if remote was created, False when already exists
80f38d
    """
80f38d
    parsed_url = urlparse(remote_base_url)
9a602e
    remote_url = '{0}://{1}/{2}/centos_{3}_{4}.git'.format(
80f38d
        parsed_url.scheme,
80f38d
        parsed_url.netloc,
80f38d
        username,
9a602e
        namespace,
80f38d
        repo_name,
80f38d
    )
80f38d
80f38d
    # check already existing remote
80f38d
    for remote in repo.remotes:
80f38d
        if remote.name == username:
80f38d
            return False
80f38d
80f38d
    try:
80f38d
        # create remote with username as its name
80f38d
        repo.create_remote(username, url=remote_url)
80f38d
    except git.exc.GitCommandError as e:
80f38d
        error_msg = "During create remote:\n  {0}\n  {1}".format(
80f38d
            " ".join(e.command), e.stderr)
80f38d
        raise rpkgError(error_msg)
80f38d
    return True
80f38d
80f38d
80f38d
def config_get_safely(config, section, option):
80f38d
    """
80f38d
    Returns option from the user's configuration file. In case of missing
80f38d
    section or option method throws an exception with a human-readable
80f38d
    warning and a possible hint.
80f38d
    The method should be used especially in situations when there are newly
80f38d
    added sections/options into the config. In this case, there is a risk that
80f38d
    the user's config wasn't properly upgraded.
80f38d
80f38d
    :param config: ConfigParser object
80f38d
    :param section: section name in the config
80f38d
    :param option: name of the option
80f38d
    :return: option value from the right section
80f38d
    :rtype: str
80f38d
    """
80f38d
80f38d
    hint = (
80f38d
        "First (if possible), refer to the help of the current command "
80f38d
        "(-h/--help).\n"
80f38d
        "There also might be a new version of the config after upgrade.\n"
80f38d
        "Hint: you can check if you have 'centpkg.conf.rpmnew' or "
80f38d
        "'centpkg.conf.rpmsave' in the config directory. If yes, try to merge "
80f38d
        "your changes to the config with the maintainer provided version "
80f38d
        "(or replace centpkg.conf file with 'centpkg.conf.rpmnew')."
80f38d
    )
80f38d
80f38d
    try:
80f38d
        return config.get(section, option)
80f38d
    except NoSectionError:
80f38d
        msg = "Missing section '{0}' in the config file.".format(section)
80f38d
        raise rpkgError("{0}\n{1}".format(msg, hint))
80f38d
    except NoOptionError:
80f38d
        msg = "Missing option '{0}' in the section '{1}' of the config file.".format(
80f38d
            option, section
80f38d
        )
80f38d
        raise rpkgError("{0}\n{1}".format(msg, hint))
80f38d
    except Exception:
80f38d
        raise