Blame pagure/lib/git.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
"""
Pierre-Yves Chibon c70df4
 (c) 2015-2016 - Copyright Red Hat Inc
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
 Authors:
Pierre-Yves Chibon a27356
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon a27356
Pierre-Yves Chibon a27356
"""
Pierre-Yves Chibon 67d1cc
from __future__ import print_function, unicode_literals, absolute_import
Pierre-Yves Chibon a27356
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-branches
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-arguments
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-locals
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-statements
Pierre-Yves Chibon 22a554
# pylint: disable=too-many-lines
Pierre-Yves Chibon 22a554
Pierre-Yves Chibon a27356
import datetime
Pierre-Yves Chibon a27356
import json
Pierre-Yves Chibon b73de8
import logging
Pierre-Yves Chibon a27356
import os
Pierre-Yves Chibon a27356
import shutil
Pierre-Yves Chibon 2ed535
import subprocess
Patrick Uiterwijk 3f97f6
import requests
Pierre-Yves Chibon a27356
import tempfile
Pierre-Yves Chibon ded1fa
import tarfile
Pierre-Yves Chibon ded1fa
import zipfile
Pierre-Yves Chibon a27356
Pierre-Yves Chibon 7772c6
import arrow
Pierre-Yves Chibon a27356
import pygit2
Aurélien Bompard 831553
import six
Pierre-Yves Chibon a27356
Pierre-Yves Chibon b1fc61
from sqlalchemy.exc import SQLAlchemyError
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
# from sqlalchemy.orm.session import Session
Pierre-Yves Chibon acd3eb
from pygit2.remote import RemoteCollection
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon b130e5
import pagure.utils
Pierre-Yves Chibon fe5017
import pagure.exceptions
Pierre-Yves Chibon 930073
import pagure.lib.query
Pierre-Yves Chibon fe5017
import pagure.lib.notify
Pierre-Yves Chibon eb5f49
import pagure.lib.tasks
Pierre-Yves Chibon b130e5
from pagure.config import config as pagure_config
Pierre-Yves Chibon fe5017
from pagure.lib import model
Pierre-Yves Chibon 609965
from pagure.lib.repo import PagureRepo
Patrick Uiterwijk d29158
import pagure.hooks
Pierre-Yves Chibon a27356
Patrick Uiterwijk 4012dc
# from pagure.hooks import run_project_hooks
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon c70df4
Pierre-Yves Chibon b73de8
_log = logging.getLogger(__name__)
Pierre-Yves Chibon b73de8
Pierre-Yves Chibon b73de8
Pierre-Yves Chibon 86e455
def commit_to_patch(
Pierre-Yves Chibon 9c2953
    repo_obj, commits, diff_view=False, find_similar=False, separated=False
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon 9c2953
    """ For a given commit (PyGit2 commit object) of a specified git repo,
Pierre-Yves Chibon a27356
    returns a string representation of the changes the commit did in a
Pierre-Yves Chibon a27356
    format that allows it to be used as patch.
Pierre-Yves Chibon 7f7053
Pierre-Yves Chibon 7f7053
    :arg repo_obj: the `pygit2.Repository` object of the git repo to
Pierre-Yves Chibon 7f7053
        retrieve the commits in
Pierre-Yves Chibon 7f7053
    :type repo_obj: `pygit2.Repository`
Pierre-Yves Chibon 7f7053
    :arg commits: the list of commits to convert to path
Pierre-Yves Chibon 7f7053
    :type commits: str or list
Pierre-Yves Chibon 7f7053
    :kwarg diff_view: a boolean specifying if what is returned is a git
Pierre-Yves Chibon 7f7053
        patch or a git diff
Pierre-Yves Chibon 7f7053
    :type diff_view: boolean
Ryan Lerch e2f045
    :kwarg find_similar: a boolean specifying if what we run find_similar
Ryan Lerch e2f045
        on the diff to group renamed files
Ryan Lerch e2f045
    :type find_similar: boolean
Pierre-Yves Chibon 86e455
    :kwarg separated: a boolean specifying if the data returned should be
Pierre-Yves Chibon 86e455
        returned as one text blob or not. If diff_view is True, then the diff
Pierre-Yves Chibon 86e455
        are also split by file, otherwise, the different patches are returned
Pierre-Yves Chibon 86e455
        as different text blob.
Pierre-Yves Chibon 86e455
    :type separated: boolean
Pierre-Yves Chibon 7f7053
    :return: the patch or diff representation of the provided commits
Pierre-Yves Chibon 7f7053
    :rtype: str
Pierre-Yves Chibon 7f7053
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon a27356
    if not isinstance(commits, list):
Pierre-Yves Chibon a27356
        commits = [commits]
Pierre-Yves Chibon a27356
Pierre-Yves Chibon 4f92f0
    patch = []
Pierre-Yves Chibon a27356
    for cnt, commit in enumerate(commits):
Pierre-Yves Chibon a27356
        if commit.parents:
Carlos Mogas da Silva dbd6dd
            diff = repo_obj.diff(commit.parents[0], commit)
Pierre-Yves Chibon a27356
        else:
Pierre-Yves Chibon a27356
            # First commit in the repo
Pierre-Yves Chibon a27356
            diff = commit.tree.diff_to_tree(swap=True)
Ryan Lerch e2f045
Julen Landa Alustiza 05156b
        if diff.patch is None:
Julen Landa Alustiza 05156b
            continue
Julen Landa Alustiza 05156b
Ryan Lerch e2f045
        if find_similar and diff:
Ryan Lerch e2f045
            diff.find_similar()
Ryan Lerch e2f045
Pierre-Yves Chibon 4b90cc
        if diff_view:
Pierre-Yves Chibon 86e455
            if separated:
Pierre-Yves Chibon 9c2953
                for el in diff.patch.split("\ndiff --git a/"):
Pierre-Yves Chibon 9c2953
                    if el and not el.startswith("diff --git a/"):
Pierre-Yves Chibon 9c2953
                        patch.append("\ndiff --git a/" + el)
Pierre-Yves Chibon ed25f0
                    elif el:
Pierre-Yves Chibon ed25f0
                        patch.append(el)
Pierre-Yves Chibon 86e455
            else:
Pierre-Yves Chibon 86e455
                patch.append(diff.patch)
Pierre-Yves Chibon a27356
        else:
Pierre-Yves Chibon a27356
Pierre-Yves Chibon 9c2953
            subject = message = ""
Pierre-Yves Chibon 9c2953
            if "\n" in commit.message:
Pierre-Yves Chibon 9c2953
                subject, message = commit.message.split("\n", 1)
Pierre-Yves Chibon 4b90cc
            else:
Pierre-Yves Chibon 4b90cc
                subject = commit.message
Pierre-Yves Chibon 4b90cc
Pierre-Yves Chibon 4b90cc
            if len(commits) > 1:
Pierre-Yves Chibon 9c2953
                subject = "[PATCH %s/%s] %s" % (cnt + 1, len(commits), subject)
Pierre-Yves Chibon a27356
Pierre-Yves Chibon 9c2953
            patch.append(
Pierre-Yves Chibon 9c2953
                """From {commit} Mon Sep 17 00:00:00 2001
Pierre-Yves Chibon b8c7ae
From: {author_name} <{author_email}>
Pierre-Yves Chibon b8c7ae
Date: {date}
Pierre-Yves Chibon b8c7ae
Subject: {subject}
Pierre-Yves Chibon a27356
Pierre-Yves Chibon b8c7ae
{msg}
Pierre-Yves Chibon a27356
---
Pierre-Yves Chibon a27356
Pierre-Yves Chibon b8c7ae
{patch}
Pierre-Yves Chibon 4b90cc
""".format(
Pierre-Yves Chibon 9c2953
                    commit=commit.oid.hex,
Pierre-Yves Chibon 9c2953
                    author_name=commit.author.name,
Pierre-Yves Chibon 9c2953
                    author_email=commit.author.email,
Pierre-Yves Chibon 9c2953
                    date=datetime.datetime.utcfromtimestamp(
Pierre-Yves Chibon 9c2953
                        commit.commit_time
Pierre-Yves Chibon 9c2953
                    ).strftime("%b %d %Y %H:%M:%S +0000"),
Pierre-Yves Chibon 9c2953
                    subject=subject,
Pierre-Yves Chibon 9c2953
                    msg=message,
Pierre-Yves Chibon 9c2953
                    patch=diff.patch,
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 86e455
Pierre-Yves Chibon 86e455
    if separated:
Pierre-Yves Chibon 86e455
        return patch
Pierre-Yves Chibon 86e455
    else:
Julen Landa Alustiza 05156b
        return "".join(filter(None, patch))
Pierre-Yves Chibon 459834
Pierre-Yves Chibon 459834
Pierre-Yves Chibon ccb160
def generate_gitolite_acls(project=None, group=None):
Pierre-Yves Chibon 274e60
    """ Generate the gitolite configuration file.
Pierre-Yves Chibon 274e60
Pierre-Yves Chibon 274e60
    :arg project: the project of which to update the ACLs. This argument
Pierre-Yves Chibon 274e60
            can take three values: ``-1``, ``None`` and a project.
Pierre-Yves Chibon 274e60
            If project is ``-1``, the configuration should be refreshed for
Pierre-Yves Chibon 274e60
            *all* projects.
Pierre-Yves Chibon 274e60
            If project is ``None``, there no specific project to refresh
Pierre-Yves Chibon 274e60
            but the ssh key of an user was added and updated.
Pierre-Yves Chibon 274e60
            If project is a pagure.lib.model.Project, the configuration of
Pierre-Yves Chibon 274e60
            this project should be updated.
Pierre-Yves Chibon 274e60
    :type project: None, int or pagure.lib.model.Project
Pierre-Yves Chibon ccb160
    :kwarg group: the group to refresh the members of
Pierre-Yves Chibon ccb160
    :type group: None or str
Pierre-Yves Chibon 274e60
Pierre-Yves Chibon 274e60
    """
Pierre-Yves Chibon 274e60
    if project != -1:
Pierre-Yves Chibon eb5f49
        task = pagure.lib.tasks.generate_gitolite_acls.delay(
Pierre-Yves Chibon 274e60
            namespace=project.namespace if project else None,
Pierre-Yves Chibon 274e60
            name=project.name if project else None,
Pierre-Yves Chibon ccb160
            user=project.user.user if project and project.is_fork else None,
Pierre-Yves Chibon 9c2953
            group=group,
Pierre-Yves Chibon 274e60
        )
Pierre-Yves Chibon 274e60
    else:
Pierre-Yves Chibon eb5f49
        task = pagure.lib.tasks.generate_gitolite_acls.delay(
Pierre-Yves Chibon eb5f49
            name=-1, group=group
Pierre-Yves Chibon eb5f49
        )
Pierre-Yves Chibon f629e8
    return task
Patrick Uiterwijk 4e3dbd
Patrick Uiterwijk 4e3dbd
Patrick Uiterwijk 3f97f6
def update_git(obj, repo):
Patrick Uiterwijk 8e1ee6
    """ Schedules an update_repo task after determining arguments. """
Patrick Uiterwijk 8e1ee6
    ticketuid = None
Patrick Uiterwijk 8e1ee6
    requestuid = None
Pierre-Yves Chibon 9c2953
    if obj.isa == "issue":
Patrick Uiterwijk 8e1ee6
        ticketuid = obj.uid
Pierre-Yves Chibon 9c2953
    elif obj.isa == "pull-request":
Patrick Uiterwijk 8e1ee6
        requestuid = obj.uid
Patrick Uiterwijk 8e1ee6
    else:
Pierre-Yves Chibon 9c2953
        raise NotImplementedError("Unknown object type %s" % obj.isa)
Patrick Uiterwijk 8e1ee6
Patrick Uiterwijk b71da2
    queued = pagure.lib.tasks.update_git.delay(
Pierre-Yves Chibon 9c2953
        repo.name,
Pierre-Yves Chibon 9c2953
        repo.namespace,
Patrick Uiterwijk eb9395
        repo.user.username if repo.is_fork else None,
Pierre-Yves Chibon 9c2953
        ticketuid,
Pierre-Yves Chibon 9c2953
        requestuid,
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk b71da2
    _maybe_wait(queued)
Patrick Uiterwijk b71da2
    return queued
Patrick Uiterwijk b71da2
Patrick Uiterwijk b71da2
Patrick Uiterwijk b71da2
def _maybe_wait(result):
Patrick Uiterwijk b71da2
    """ Function to patch if one wants to wait for finish.
Patrick Uiterwijk b71da2
Pierre-Yves Chibon 8312e7
    This function should only ever be overridden by a few tests that depend
Pierre-Yves Chibon 8312e7
    on counting and very precise timing. """
Patrick Uiterwijk b71da2
    pass
Patrick Uiterwijk 8e1ee6
Patrick Uiterwijk 8e1ee6
Aurélien Bompard 831553
def _make_signature(name, email):
Aurélien Bompard 831553
    if six.PY2:
Aurélien Bompard 831553
        if isinstance(name, six.text_type):
Aurélien Bompard 831553
            name = name.encode("utf-8")
Aurélien Bompard 831553
        if isinstance(email, six.text_type):
Aurélien Bompard 831553
            email = email.encode("utf-8")
Aurélien Bompard 831553
    return pygit2.Signature(name=name, email=email)
Aurélien Bompard 831553
Aurélien Bompard 831553
Patrick Uiterwijk 3f97f6
def _update_git(obj, repo):
Pierre-Yves Chibon c8b13b
    """ Update the given issue in its git.
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    This method forks the provided repo, add/edit the issue whose file name
Pierre-Yves Chibon c8b13b
    is defined by the uid field of the issue and if there are additions/
Pierre-Yves Chibon c8b13b
    changes commit them and push them back to the original repo.
Pierre-Yves Chibon c8b13b
Pierre-Yves Chibon c8b13b
    """
Pierre-Yves Chibon 9c2953
    _log.info("Update the git repo: %s for: %s", repo.path, obj)
Pierre-Yves Chibon c8b13b
Patrick Uiterwijk 3f97f6
    with TemporaryClone(repo, obj.repotype, "update_git") as tempclone:
Patrick Uiterwijk 3f97f6
        if tempclone is None:
Patrick Uiterwijk 3f97f6
            # Turns out we don't have a repo for this kind of object.
Patrick Uiterwijk 3f97f6
            return
Pierre-Yves Chibon c8b13b
Patrick Uiterwijk 3f97f6
        newpath = tempclone.repopath
Patrick Uiterwijk 3f97f6
        new_repo = tempclone.repo
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        file_path = os.path.join(newpath, obj.uid)
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # Get the current index
Patrick Uiterwijk 3f97f6
        index = new_repo.index
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # Are we adding files
Patrick Uiterwijk 3f97f6
        added = False
Patrick Uiterwijk 3f97f6
        if not os.path.exists(file_path):
Patrick Uiterwijk 3f97f6
            added = True
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # Write down what changed
Patrick Uiterwijk 3f97f6
        with open(file_path, "w") as stream:
Patrick Uiterwijk 3f97f6
            stream.write(
Patrick Uiterwijk 3f97f6
                json.dumps(
Patrick Uiterwijk 3f97f6
                    obj.to_json(),
Patrick Uiterwijk 3f97f6
                    sort_keys=True,
Patrick Uiterwijk 3f97f6
                    indent=4,
Patrick Uiterwijk 3f97f6
                    separators=(",", ": "),
Patrick Uiterwijk 3f97f6
                )
Pierre-Yves Chibon 9c2953
            )
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # Retrieve the list of files that changed
Patrick Uiterwijk 3f97f6
        diff = new_repo.diff()
Patrick Uiterwijk 3f97f6
        files = []
Patrick Uiterwijk 3f97f6
        for patch in diff:
Patrick Uiterwijk 3f97f6
            files.append(patch.delta.new_file.path)
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # Add the changes to the index
Patrick Uiterwijk 3f97f6
        if added:
Patrick Uiterwijk 3f97f6
            index.add(obj.uid)
Patrick Uiterwijk 3f97f6
        for filename in files:
Patrick Uiterwijk 3f97f6
            index.add(filename)
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # If not change, return
Patrick Uiterwijk 3f97f6
        if not files and not added:
Patrick Uiterwijk 3f97f6
            return
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        # See if there is a parent to this commit
Patrick Uiterwijk 3f97f6
        parent = None
Patrick Uiterwijk 3f97f6
        try:
Pierre-Yves Chibon 29ff0a
            parent = new_repo.head.peel().oid
Patrick Uiterwijk 3f97f6
        except pygit2.GitError:
Patrick Uiterwijk 3f97f6
            pass
Pierre-Yves Chibon 08eaf8
Patrick Uiterwijk 3f97f6
        parents = []
Patrick Uiterwijk 3f97f6
        if parent:
Patrick Uiterwijk 3f97f6
            parents.append(parent)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Author/commiter will always be this one
Patrick Uiterwijk 3f97f6
        author = _make_signature(name="pagure", email="pagure")
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Actually commit
Patrick Uiterwijk 3f97f6
        new_repo.create_commit(
Patrick Uiterwijk 3f97f6
            "refs/heads/master",
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            "Updated %s %s: %s" % (obj.isa, obj.uid, obj.title),
Patrick Uiterwijk 3f97f6
            new_repo.index.write_tree(),
Patrick Uiterwijk 3f97f6
            parents,
Patrick Uiterwijk 3f97f6
        )
Patrick Uiterwijk 3f97f6
        index.write()
Pierre-Yves Chibon c7f9fa
Patrick Uiterwijk 3f97f6
        # And push it back
Patrick Uiterwijk 03a519
        tempclone.push("pagure", "master", internal="yes")
Pierre-Yves Chibon c7f9fa
Patrick Uiterwijk 84b223
Patrick Uiterwijk 3f97f6
def clean_git(repo, obj_repotype, obj_uid):
Patrick Uiterwijk 3f97f6
    if repo is None:
Patrick Uiterwijk 3f97f6
        return
Patrick Uiterwijk 189fa8
Patrick Uiterwijk 3f97f6
    task = pagure.lib.tasks.clean_git.delay(
Pierre-Yves Chibon 9c2953
        repo.name,
Pierre-Yves Chibon 9c2953
        repo.namespace,
Patrick Uiterwijk fce947
        repo.user.username if repo.is_fork else None,
Patrick Uiterwijk 3f97f6
        obj_repotype,
Patrick Uiterwijk 3f97f6
        obj_uid,
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 3f97f6
    _maybe_wait(task)
Patrick Uiterwijk 3f97f6
    return task
Patrick Uiterwijk 189fa8
Patrick Uiterwijk 189fa8
Patrick Uiterwijk 3f97f6
def _clean_git(repo, obj_repotype, obj_uid):
Pierre-Yves Chibon 83115e
    """ Update the given issue remove it from its git.
Pierre-Yves Chibon 83115e
Pierre-Yves Chibon 83115e
    """
Patrick Uiterwijk 3f97f6
    _log.info("Update the git repo: %s to remove: %s", repo.path, obj_uid)
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
    with TemporaryClone(repo, obj_repotype, "clean_git") as tempclone:
Patrick Uiterwijk 3f97f6
        if tempclone is None:
Patrick Uiterwijk 3f97f6
            # This repo is not tracked on disk
Patrick Uiterwijk 3f97f6
            return
Pierre-Yves Chibon b73de8
Patrick Uiterwijk 3f97f6
        newpath = tempclone.repopath
Patrick Uiterwijk 3f97f6
        new_repo = tempclone.repo
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
        file_path = os.path.join(newpath, obj_uid)
Pierre-Yves Chibon eccb1c
Patrick Uiterwijk 3f97f6
        # Get the current index
Patrick Uiterwijk 3f97f6
        index = new_repo.index
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
        # Are we adding files
Patrick Uiterwijk 3f97f6
        if not os.path.exists(file_path):
Patrick Uiterwijk 3f97f6
            return
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
        # Remove the file
Patrick Uiterwijk 3f97f6
        os.unlink(file_path)
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
        # Add the changes to the index
Patrick Uiterwijk 3f97f6
        index.remove(obj_uid)
Pierre-Yves Chibon 83115e
Patrick Uiterwijk 3f97f6
        # See if there is a parent to this commit
Patrick Uiterwijk 3f97f6
        parent = None
Patrick Uiterwijk 3f97f6
        if not new_repo.is_empty:
Pierre-Yves Chibon 29ff0a
            parent = new_repo.head.peel().oid
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        parents = []
Patrick Uiterwijk 3f97f6
        if parent:
Patrick Uiterwijk 3f97f6
            parents.append(parent)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Author/commiter will always be this one
Patrick Uiterwijk 3f97f6
        author = _make_signature(name="pagure", email="pagure")
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Actually commit
Patrick Uiterwijk 3f97f6
        new_repo.create_commit(
Patrick Uiterwijk 3f97f6
            "refs/heads/master",
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            "Removed object %s: %s" % (obj_repotype, obj_uid),
Patrick Uiterwijk 3f97f6
            new_repo.index.write_tree(),
Patrick Uiterwijk 3f97f6
            parents,
Patrick Uiterwijk 3f97f6
        )
Patrick Uiterwijk 3f97f6
        index.write()
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
        master_ref = new_repo.lookup_reference("HEAD").resolve().name
Patrick Uiterwijk 03a519
        tempclone.push("pagure", master_ref, internal="yes")
Pierre-Yves Chibon 774fee
Pierre-Yves Chibon 83115e
Pierre-Yves Chibon 9c2953
def get_user_from_json(session, jsondata, key="user"):
Pierre-Yves Chibon c7f9fa
    """ From the given json blob, retrieve the user info and search for it
Pierre-Yves Chibon c7f9fa
    in the db and create the user if it does not already exist.
Pierre-Yves Chibon c7f9fa
    """
Pierre-Yves Chibon c7f9fa
    user = None
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon ae1e93
    username = fullname = useremails = default_email = None
Pierre-Yves Chibon 67357e
Pierre-Yves Chibon 441f62
    data = jsondata.get(key, None)
Pierre-Yves Chibon 67357e
Pierre-Yves Chibon ae1e93
    if data:
Pierre-Yves Chibon 9c2953
        username = data.get("name")
Pierre-Yves Chibon 9c2953
        fullname = data.get("fullname")
Pierre-Yves Chibon 9c2953
        useremails = data.get("emails")
Pierre-Yves Chibon 9c2953
        default_email = data.get("default_email")
Pierre-Yves Chibon 67357e
Pierre-Yves Chibon 67357e
    if not default_email and useremails:
Pierre-Yves Chibon 67357e
        default_email = useremails[0]
Pierre-Yves Chibon 67357e
Pierre-Yves Chibon ac2bce
    if not username and not useremails:
Pierre-Yves Chibon ac2bce
        return
Pierre-Yves Chibon ac2bce
Pierre-Yves Chibon 930073
    user = pagure.lib.query.search_user(session, username=username)
Pierre-Yves Chibon c7f9fa
    if not user:
Pierre-Yves Chibon c7f9fa
        for email in useremails:
Pierre-Yves Chibon 930073
            user = pagure.lib.query.search_user(session, email=email)
Pierre-Yves Chibon c7f9fa
            if user:
Pierre-Yves Chibon c7f9fa
                break
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    if not user:
Pierre-Yves Chibon 930073
        user = pagure.lib.query.set_up_user(
Pierre-Yves Chibon c7f9fa
            session=session,
Pierre-Yves Chibon c7f9fa
            username=username,
Pierre-Yves Chibon c7f9fa
            fullname=fullname or username,
Pierre-Yves Chibon 96eb13
            default_email=default_email,
Pierre-Yves Chibon 96eb13
            emails=useremails,
Pierre-Yves Chibon 9c2953
            keydir=pagure_config.get("GITOLITE_KEYDIR", None),
Pierre-Yves Chibon c7f9fa
        )
Pierre-Yves Chibon c7f9fa
        session.commit()
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    return user
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon 50d6b6
def get_project_from_json(session, jsondata):
Pierre-Yves Chibon a6c9f2
    """ From the given json blob, retrieve the project info and search for
Pierre-Yves Chibon a6c9f2
    it in the db and create the projec if it does not already exist.
Pierre-Yves Chibon a6c9f2
    """
Pierre-Yves Chibon a6c9f2
    project = None
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    user = get_user_from_json(session, jsondata)
Pierre-Yves Chibon 9c2953
    name = jsondata.get("name")
Pierre-Yves Chibon 9c2953
    namespace = jsondata.get("namespace")
Pierre-Yves Chibon a6c9f2
    project_user = None
Pierre-Yves Chibon 9c2953
    if jsondata.get("parent"):
Pierre-Yves Chibon a6c9f2
        project_user = user.username
Pierre-Yves Chibon edbdc9
Pierre-Yves Chibon 930073
    project = pagure.lib.query._get_project(
Pierre-Yves Chibon 9c2953
        session, name, user=project_user, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    if not project:
Pierre-Yves Chibon a6c9f2
        parent = None
Pierre-Yves Chibon 9c2953
        if jsondata.get("parent"):
Pierre-Yves Chibon 9c2953
            parent = get_project_from_json(session, jsondata.get("parent"))
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 930073
            pagure.lib.query.fork_project(
Patrick Uiterwijk 3f97f6
                session=session, repo=parent, user=user.username
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 824828
Pierre-Yves Chibon 824828
        else:
Pierre-Yves Chibon 930073
            pagure.lib.query.new_project(
Pierre-Yves Chibon 824828
                session,
Pierre-Yves Chibon 824828
                user=user.username,
Pierre-Yves Chibon 824828
                name=name,
clime afed57
                namespace=namespace,
Patrick Uiterwijk 3f97f6
                repospanner_region=None,
Pierre-Yves Chibon 9c2953
                description=jsondata.get("description"),
Pierre-Yves Chibon 824828
                parent_id=parent.id if parent else None,
Pierre-Yves Chibon 9c2953
                blacklist=pagure_config.get("BLACKLISTED_PROJECTS", []),
Pierre-Yves Chibon 9c2953
                allowed_prefix=pagure_config.get("ALLOWED_PREFIX", []),
Pierre-Yves Chibon b130e5
                prevent_40_chars=pagure_config.get(
Pierre-Yves Chibon 9c2953
                    "OLD_VIEW_COMMIT_ENABLED", False
Pierre-Yves Chibon 9c2953
                ),
Pierre-Yves Chibon 824828
            )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
        session.commit()
Pierre-Yves Chibon 930073
        project = pagure.lib.query._get_project(
Pierre-Yves Chibon 9c2953
            session, name, user=user.username, namespace=namespace
Pierre-Yves Chibon 9c2953
        )
clime afed57
Pierre-Yves Chibon 9c2953
        tags = jsondata.get("tags", None)
Pierre-Yves Chibon 513113
        if tags:
Pierre-Yves Chibon 930073
            pagure.lib.query.add_tag_obj(
Patrick Uiterwijk 3f97f6
                session, project, tags=tags, user=user.username
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    return project
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 1878ce
def update_custom_field_from_json(session, repo, issue, json_data):
Pierre-Yves Chibon 9c2953
    """ Update the custom fields according to the custom fields of
Vivek Anand 50c877
    the issue. If the custom field is not present for the repo in
Vivek Anand 50c877
    it's settings, this will create them.
Vivek Anand 50c877
Vivek Anand 50c877
    :arg session: the session to connect to the database with.
Vivek Anand 50c877
    :arg repo: the sqlalchemy object of the project
Vivek Anand 50c877
    :arg issue: the sqlalchemy object of the issue
Vivek Anand 50c877
    :arg json_data: the json representation of the issue taken from the git
Vivek Anand 50c877
        and used to update the data in the database.
Pierre-Yves Chibon 9c2953
    """
Vivek Anand 50c877
Vivek Anand 50c877
    # Update custom key value, if present
Pierre-Yves Chibon 9c2953
    custom_fields = json_data.get("custom_fields")
Vivek Anand 50c877
    if not custom_fields:
Vivek Anand 50c877
        return
Vivek Anand 50c877
Vivek Anand 50c877
    current_keys = []
Vivek Anand 50c877
    for key in repo.issue_keys:
Vivek Anand 50c877
        current_keys.append(key.name)
Vivek Anand 50c877
Vivek Anand 50c877
    for new_key in custom_fields:
Pierre-Yves Chibon 9c2953
        if new_key["name"] not in current_keys:
Vivek Anand 50c877
            issuekey = model.IssueKeys(
Vivek Anand 50c877
                project_id=repo.id,
Pierre-Yves Chibon 9c2953
                name=new_key["name"],
Pierre-Yves Chibon 9c2953
                key_type=new_key["key_type"],
Vivek Anand 50c877
            )
Vivek Anand 50c877
            try:
Vivek Anand 50c877
                session.add(issuekey)
Vivek Anand 50c877
                session.commit()
Vivek Anand 50c877
            except SQLAlchemyError:
Vivek Anand 50c877
                session.rollback()
Vivek Anand 50c877
                continue
Vivek Anand 50c877
Vivek Anand 50c877
        # The key should be present in the database now
Pierre-Yves Chibon 930073
        key_obj = pagure.lib.query.get_custom_key(
Pierre-Yves Chibon 930073
            session, repo, new_key["name"]
Pierre-Yves Chibon 930073
        )
Vivek Anand 50c877
Pierre-Yves Chibon 9c2953
        value = new_key.get("value")
Vivek Anand 50c877
        if value:
Vivek Anand 50c877
            value = value.strip()
Pierre-Yves Chibon 930073
        pagure.lib.query.set_custom_key_value(
Pierre-Yves Chibon 9c2953
            session, issue=issue, key=key_obj, value=value
Vivek Anand 50c877
        )
Vivek Anand 50c877
        try:
Vivek Anand 50c877
            session.commit()
Vivek Anand 50c877
        except SQLAlchemyError:
Vivek Anand 50c877
            session.rollback()
Vivek Anand 50c877
Vivek Anand 50c877
Pierre-Yves Chibon c7f9fa
def update_ticket_from_git(
Pierre-Yves Chibon 9c2953
    session, reponame, namespace, username, issue_uid, json_data, agent
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon c7f9fa
    """ Update the specified issue (identified by its unique identifier)
Pierre-Yves Chibon c7f9fa
    with the data present in the json blob provided.
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    :arg session: the session to connect to the database with.
Pierre-Yves Chibon c7f9fa
    :arg repo: the name of the project to update
Pierre-Yves Chibon b7eec1
    :arg namespace: the namespace of the project to update
Pierre-Yves Chibon b7eec1
    :arg username: the username of the project to update (if the project
Pierre-Yves Chibon b7eec1
        is a fork)
Pierre-Yves Chibon c7f9fa
    :arg issue_uid: the unique identifier of the issue to update
Pierre-Yves Chibon c7f9fa
    :arg json_data: the json representation of the issue taken from the git
Pierre-Yves Chibon c7f9fa
        and used to update the data in the database.
Pierre-Yves Chibon b7eec1
    :arg agent: the username of the person who pushed the changes (and thus
Pierre-Yves Chibon b7eec1
        is assumed did the action).
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    """
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon 930073
    repo = pagure.lib.query._get_project(
Pierre-Yves Chibon 9c2953
        session, reponame, user=username, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Farhaan Bukhsh 8267d1
Pierre-Yves Chibon c7f9fa
    if not repo:
Pierre-Yves Chibon fe5017
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9c2953
            "Unknown repo %s of username: %s in namespace: %s"
Pierre-Yves Chibon 9c2953
            % (reponame, username, namespace)
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    user = get_user_from_json(session, json_data)
Pierre-Yves Chibon b7eec1
    # rely on the agent provided, but if something goes wrong, behave as
Pierre-Yves Chibon b7eec1
    # ticket creator
Pierre-Yves Chibon 930073
    agent = pagure.lib.query.search_user(session, username=agent) or user
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon 930073
    issue = pagure.lib.query.get_issue_by_uid(session, issue_uid=issue_uid)
Pierre-Yves Chibon 1878ce
    messages = []
Pierre-Yves Chibon c7f9fa
    if not issue:
Pierre-Yves Chibon c7f9fa
        # Create new issue
Pierre-Yves Chibon 930073
        pagure.lib.query.new_issue(
Pierre-Yves Chibon c7f9fa
            session,
Pierre-Yves Chibon c7f9fa
            repo=repo,
Pierre-Yves Chibon 9c2953
            title=json_data.get("title"),
Pierre-Yves Chibon 9c2953
            content=json_data.get("content"),
Pierre-Yves Chibon 9c2953
            priority=json_data.get("priority"),
Pierre-Yves Chibon c7f9fa
            user=user.username,
Pierre-Yves Chibon 9c2953
            issue_id=json_data.get("id"),
Pierre-Yves Chibon c7f9fa
            issue_uid=issue_uid,
Pierre-Yves Chibon 9c2953
            private=json_data.get("private"),
Pierre-Yves Chibon 9c2953
            status=json_data.get("status"),
Pierre-Yves Chibon 9c2953
            close_status=json_data.get("close_status"),
Clement Verna f45e89
            date_created=datetime.datetime.utcfromtimestamp(
Pierre-Yves Chibon 9c2953
                float(json_data.get("date_created"))
Pierre-Yves Chibon 9c2953
            ),
Pierre-Yves Chibon c7f9fa
            notify=False,
Pierre-Yves Chibon c7f9fa
        )
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon c7f9fa
    else:
Pierre-Yves Chibon c7f9fa
        # Edit existing issue
Pierre-Yves Chibon 930073
        msgs = pagure.lib.query.edit_issue(
Pierre-Yves Chibon c7f9fa
            session,
Pierre-Yves Chibon c7f9fa
            issue=issue,
Pierre-Yves Chibon b7eec1
            user=agent.username,
Pierre-Yves Chibon 9c2953
            title=json_data.get("title"),
Pierre-Yves Chibon 9c2953
            content=json_data.get("content"),
Pierre-Yves Chibon 9c2953
            priority=json_data.get("priority"),
Pierre-Yves Chibon 9c2953
            status=json_data.get("status"),
Pierre-Yves Chibon 9c2953
            close_status=json_data.get("close_status"),
Pierre-Yves Chibon 9c2953
            private=json_data.get("private"),
Pierre-Yves Chibon 74ab5f
        )
Pierre-Yves Chibon 74ab5f
        if msgs:
Pierre-Yves Chibon 74ab5f
            messages.extend(msgs)
Pierre-Yves Chibon 1878ce
Pierre-Yves Chibon c7f9fa
    session.commit()
Pierre-Yves Chibon c7f9fa
Pierre-Yves Chibon 930073
    issue = pagure.lib.query.get_issue_by_uid(session, issue_uid=issue_uid)
Pierre-Yves Chibon c7f9fa
Vivek Anand 50c877
    update_custom_field_from_json(
Pierre-Yves Chibon 9c2953
        session, repo=repo, issue=issue, json_data=json_data
Vivek Anand 50c877
    )
Vivek Anand 50c877
Vivek Anand 7bf8e2
    # Update milestone
Pierre-Yves Chibon 9c2953
    milestone = json_data.get("milestone")
Vivek Anand 7bf8e2
Vivek Anand 7bf8e2
    # If milestone is not in the repo settings, add it
Vivek Anand 7bf8e2
    if milestone:
Vivek Anand 7bf8e2
        if milestone.strip() not in repo.milestones:
Vivek Anand 7bf8e2
            try:
Clement Verna b30f33
                tmp_milestone = repo.milestones.copy()
Clement Verna b30f33
                tmp_milestone[milestone.strip()] = None
Clement Verna b30f33
                repo.milestones = tmp_milestone
Vivek Anand 7bf8e2
                session.add(repo)
Vivek Anand 7bf8e2
                session.commit()
Vivek Anand 7bf8e2
            except SQLAlchemyError:
Vivek Anand 7bf8e2
                session.rollback()
Vivek Anand 7bf8e2
    try:
Pierre-Yves Chibon 930073
        msgs = pagure.lib.query.edit_issue(
Vivek Anand 7bf8e2
            session,
Vivek Anand 7bf8e2
            issue=issue,
Pierre-Yves Chibon b7eec1
            user=agent.username,
Vivek Anand 7bf8e2
            milestone=milestone,
Pierre-Yves Chibon 9c2953
            title=json_data.get("title"),
Pierre-Yves Chibon 9c2953
            content=json_data.get("content"),
Pierre-Yves Chibon 9c2953
            status=json_data.get("status"),
Pierre-Yves Chibon 9c2953
            close_status=json_data.get("close_status"),
Pierre-Yves Chibon 9c2953
            private=json_data.get("private"),
Vivek Anand 7bf8e2
        )
Pierre-Yves Chibon 1878ce
        if msgs:
Pierre-Yves Chibon 1878ce
            messages.extend(msgs)
Vivek Anand 7bf8e2
    except SQLAlchemyError:
Vivek Anand 7bf8e2
        session.rollback()
Vivek Anand 7bf8e2
Vivek Anand 1db9ed
    # Update close_status
Pierre-Yves Chibon 9c2953
    close_status = json_data.get("close_status")
Vivek Anand 1db9ed
Vivek Anand 1db9ed
    if close_status:
Vivek Anand 1db9ed
        if close_status.strip() not in repo.close_status:
Vivek Anand 1db9ed
            try:
Vivek Anand 1db9ed
                repo.close_status.append(close_status.strip())
Vivek Anand 1db9ed
                session.add(repo)
Vivek Anand 1db9ed
                session.commit()
Vivek Anand 1db9ed
            except SQLAlchemyError:
Vivek Anand 1db9ed
                session.rollback()
Vivek Anand 1db9ed
Pierre-Yves Chibon afa3e7
    # Update tags
Pierre-Yves Chibon 9c2953
    tags = json_data.get("tags", [])
Pierre-Yves Chibon 930073
    msgs = pagure.lib.query.update_tags(
Pierre-Yves Chibon 930073
        session, issue, tags, username=user.user
Pierre-Yves Chibon 930073
    )
Pierre-Yves Chibon 74ab5f
    if msgs:
Pierre-Yves Chibon 74ab5f
        messages.extend(msgs)
Pierre-Yves Chibon afa3e7
Pierre-Yves Chibon 95e2ea
    # Update assignee
Pierre-Yves Chibon 9c2953
    assignee = get_user_from_json(session, json_data, key="assignee")
Pierre-Yves Chibon 5622fc
    if assignee:
Pierre-Yves Chibon 930073
        msg = pagure.lib.query.add_issue_assignee(
Patrick Uiterwijk 3f97f6
            session, issue, assignee.username, user=agent.user, notify=False
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 74ab5f
        if msg:
Pierre-Yves Chibon 74ab5f
            messages.append(msg)
Pierre-Yves Chibon 95e2ea
Pierre-Yves Chibon afa3e7
    # Update depends
Pierre-Yves Chibon 9c2953
    depends = json_data.get("depends", [])
Pierre-Yves Chibon 930073
    msgs = pagure.lib.query.update_dependency_issue(
Patrick Uiterwijk 3f97f6
        session, issue.project, issue, depends, username=agent.user
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 74ab5f
    if msgs:
Pierre-Yves Chibon 74ab5f
        messages.extend(msgs)
Pierre-Yves Chibon afa3e7
Pierre-Yves Chibon afa3e7
    # Update blocks
Pierre-Yves Chibon 9c2953
    blocks = json_data.get("blocks", [])
Pierre-Yves Chibon 930073
    msgs = pagure.lib.query.update_blocked_issue(
Patrick Uiterwijk 3f97f6
        session, issue.project, issue, blocks, username=agent.user
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 74ab5f
    if msgs:
Pierre-Yves Chibon 74ab5f
        messages.extend(msgs)
Pierre-Yves Chibon afa3e7
Pierre-Yves Chibon 9c2953
    for comment in json_data["comments"]:
Pierre-Yves Chibon 74ab5f
        usercomment = get_user_from_json(session, comment)
Pierre-Yves Chibon 930073
        commentobj = pagure.lib.query.get_issue_comment_by_user_and_comment(
Pierre-Yves Chibon 9c2953
            session, issue_uid, usercomment.id, comment["comment"]
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon c7f9fa
        if not commentobj:
Pierre-Yves Chibon 930073
            pagure.lib.query.add_issue_comment(
Pierre-Yves Chibon c7f9fa
                session,
Pierre-Yves Chibon c7f9fa
                issue=issue,
Pierre-Yves Chibon 9c2953
                comment=comment["comment"],
Pierre-Yves Chibon 74ab5f
                user=usercomment.username,
Pierre-Yves Chibon c7f9fa
                notify=False,
Pierre-Yves Chibon 7b6a9d
                date_created=datetime.datetime.fromtimestamp(
Pierre-Yves Chibon 9c2953
                    float(comment["date_created"])
Pierre-Yves Chibon 9c2953
                ),
Pierre-Yves Chibon c7f9fa
            )
Pierre-Yves Chibon 74ab5f
Pierre-Yves Chibon 1878ce
    if messages:
Pierre-Yves Chibon 930073
        pagure.lib.query.add_metadata_update_notif(
Patrick Uiterwijk 3f97f6
            session=session, obj=issue, messages=messages, user=agent.username
Pierre-Yves Chibon 1878ce
        )
Pierre-Yves Chibon c7f9fa
    session.commit()
Pierre-Yves Chibon 852918
Pierre-Yves Chibon cac223
Pierre-Yves Chibon a6c9f2
def update_request_from_git(
Pierre-Yves Chibon 9c2953
    session, reponame, namespace, username, request_uid, json_data
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon a6c9f2
    """ Update the specified request (identified by its unique identifier)
Pierre-Yves Chibon a6c9f2
    with the data present in the json blob provided.
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    :arg session: the session to connect to the database with.
Pierre-Yves Chibon a6c9f2
    :arg repo: the name of the project to update
Pierre-Yves Chibon a6c9f2
    :arg username: the username to find the repo, is not None for forked
Pierre-Yves Chibon a6c9f2
        projects
Pierre-Yves Chibon a6c9f2
    :arg request_uid: the unique identifier of the issue to update
Pierre-Yves Chibon a6c9f2
    :arg json_data: the json representation of the issue taken from the git
Pierre-Yves Chibon a6c9f2
        and used to update the data in the database.
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    """
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 930073
    repo = pagure.lib.query._get_project(
Pierre-Yves Chibon 9c2953
        session, reponame, user=username, namespace=namespace
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon edbdc9
Pierre-Yves Chibon a6c9f2
    if not repo:
Pierre-Yves Chibon fe5017
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9c2953
            "Unknown repo %s of username: %s in namespace: %s"
Pierre-Yves Chibon 9c2953
            % (reponame, username, namespace)
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    user = get_user_from_json(session, json_data)
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 930073
    request = pagure.lib.query.get_request_by_uid(
Pierre-Yves Chibon 930073
        session, request_uid=request_uid
Pierre-Yves Chibon 930073
    )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a6c9f2
    if not request:
Pierre-Yves Chibon 9c2953
        repo_from = get_project_from_json(session, json_data.get("repo_from"))
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 9c2953
        repo_to = get_project_from_json(session, json_data.get("project"))
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 9c2953
        status = json_data.get("status")
Aurélien Bompard 619e2a
        if pagure.utils.is_true(status):
Pierre-Yves Chibon 9c2953
            status = "Open"
Pierre-Yves Chibon 9c2953
        elif pagure.utils.is_true(status, ["false"]):
Pierre-Yves Chibon 9c2953
            status = "Merged"
Pierre-Yves Chibon 1c34ea
Pierre-Yves Chibon a6c9f2
        # Create new request
Pierre-Yves Chibon 930073
        pagure.lib.query.new_pull_request(
Pierre-Yves Chibon a6c9f2
            session,
Pierre-Yves Chibon a6c9f2
            repo_from=repo_from,
Pierre-Yves Chibon 9c2953
            branch_from=json_data.get("branch_from"),
Pierre-Yves Chibon 9a5539
            repo_to=repo_to if repo_to else None,
Pierre-Yves Chibon 9c2953
            remote_git=json_data.get("remote_git"),
Pierre-Yves Chibon 9c2953
            branch_to=json_data.get("branch"),
Pierre-Yves Chibon 9c2953
            title=json_data.get("title"),
Pierre-Yves Chibon a6c9f2
            user=user.username,
Pierre-Yves Chibon 9c2953
            requestuid=json_data.get("uid"),
Pierre-Yves Chibon 9c2953
            requestid=json_data.get("id"),
Pierre-Yves Chibon 1c34ea
            status=status,
Pierre-Yves Chibon a6c9f2
            notify=False,
Pierre-Yves Chibon a6c9f2
        )
Pierre-Yves Chibon a6c9f2
        session.commit()
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 930073
    request = pagure.lib.query.get_request_by_uid(
Pierre-Yves Chibon 930073
        session, request_uid=request_uid
Pierre-Yves Chibon 930073
    )
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon a5fbfd
    # Update start and stop commits
Pierre-Yves Chibon 9c2953
    request.commit_start = json_data.get("commit_start")
Pierre-Yves Chibon 9c2953
    request.commit_stop = json_data.get("commit_stop")
Pierre-Yves Chibon 7a6b27
Pierre-Yves Chibon a5fbfd
    # Update assignee
Pierre-Yves Chibon 9c2953
    assignee = get_user_from_json(session, json_data, key="assignee")
Pierre-Yves Chibon a5fbfd
    if assignee:
Pierre-Yves Chibon 930073
        pagure.lib.query.add_pull_request_assignee(
Patrick Uiterwijk 3f97f6
            session, request, assignee.username, user=user.user
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon a5fbfd
Pierre-Yves Chibon 9c2953
    for comment in json_data["comments"]:
Pierre-Yves Chibon a6c9f2
        user = get_user_from_json(session, comment)
Pierre-Yves Chibon 930073
        commentobj = pagure.lib.query.get_request_comment(
Pierre-Yves Chibon 9c2953
            session, request_uid, comment["id"]
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon a6c9f2
        if not commentobj:
Pierre-Yves Chibon 930073
            pagure.lib.query.add_pull_request_comment(
Pierre-Yves Chibon a6c9f2
                session,
Pierre-Yves Chibon a6c9f2
                request,
Pierre-Yves Chibon 9c2953
                commit=comment["commit"],
Pierre-Yves Chibon 9c2953
                tree_id=comment.get("tree_id") or None,
Pierre-Yves Chibon 9c2953
                filename=comment["filename"],
Pierre-Yves Chibon 9c2953
                row=comment["line"],
Pierre-Yves Chibon 9c2953
                comment=comment["comment"],
Pierre-Yves Chibon a6c9f2
                user=user.username,
Pierre-Yves Chibon 519dc0
                notify=False,
Pierre-Yves Chibon a6c9f2
            )
Pierre-Yves Chibon 31a638
Pierre-Yves Chibon 31a638
    # Add/update tags:
Pierre-Yves Chibon 31a638
    tags = json_data.get("tags") or []
Pierre-Yves Chibon 31a638
    if tags:
Pierre-Yves Chibon 31a638
        user = get_user_from_json(session, json_data)
Pierre-Yves Chibon 31a638
        pagure.lib.query.add_tag_obj(session, request, tags, user.username)
Pierre-Yves Chibon 31a638
Pierre-Yves Chibon a6c9f2
    session.commit()
Pierre-Yves Chibon a6c9f2
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
def _add_file_to_git(repo, issue, attachmentfolder, user, filename):
Pierre-Yves Chibon 9c2953
    """ Add a given file to the specified ticket git repository.
Pierre-Yves Chibon 852918
Pierre-Yves Chibon 852918
    :arg repo: the Project object from the database
Patrick Uiterwijk 96c928
    :arg attachmentfolder: the folder on the filesystem where the attachments
Patrick Uiterwijk 96c928
        are stored
Pierre-Yves Chibon 852918
    :arg ticketfolder: the folder on the filesystem where the git repo for
Pierre-Yves Chibon 852918
        tickets are stored
Pierre-Yves Chibon 852918
    :arg user: the user object with its username and email
Pierre-Yves Chibon 852918
    :arg filename: the name of the file to save
Pierre-Yves Chibon 852918
Pierre-Yves Chibon 9c2953
    """
Patrick Uiterwijk 3f97f6
    with TemporaryClone(repo, issue.repotype, "add_file_to_git") as tempclone:
Patrick Uiterwijk 3f97f6
        newpath = tempclone.repopath
Patrick Uiterwijk 3f97f6
        new_repo = tempclone.repo
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        folder_path = os.path.join(newpath, "files")
Patrick Uiterwijk 3f97f6
        file_path = os.path.join(folder_path, filename)
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        # Get the current index
Patrick Uiterwijk 3f97f6
        index = new_repo.index
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        # Are we adding files
Patrick Uiterwijk 3f97f6
        if os.path.exists(file_path):
Patrick Uiterwijk 3f97f6
            # File exists, remove the clone and return
Patrick Uiterwijk 3f97f6
            shutil.rmtree(newpath)
Patrick Uiterwijk 3f97f6
            return os.path.join("files", filename)
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        if not os.path.exists(folder_path):
Patrick Uiterwijk 3f97f6
            os.mkdir(folder_path)
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        # Copy from attachments directory
Patrick Uiterwijk 3f97f6
        src = os.path.join(attachmentfolder, repo.fullname, "files", filename)
Patrick Uiterwijk 3f97f6
        shutil.copyfile(src, file_path)
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        # Retrieve the list of files that changed
Patrick Uiterwijk 3f97f6
        diff = new_repo.diff()
Patrick Uiterwijk 3f97f6
        files = [patch.new_file_path for patch in diff]
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        # Add the changes to the index
Patrick Uiterwijk 3f97f6
        index.add(os.path.join("files", filename))
Patrick Uiterwijk 3f97f6
        for filename in files:
Patrick Uiterwijk 3f97f6
            index.add(filename)
Pierre-Yves Chibon 5e105c
Patrick Uiterwijk 3f97f6
        # See if there is a parent to this commit
Patrick Uiterwijk 3f97f6
        parent = None
Patrick Uiterwijk 3f97f6
        try:
Pierre-Yves Chibon 29ff0a
            parent = new_repo.head.peel().oid
Patrick Uiterwijk 3f97f6
        except pygit2.GitError:
Patrick Uiterwijk 3f97f6
            pass
Pierre-Yves Chibon 5e105c
Patrick Uiterwijk 3f97f6
        parents = []
Patrick Uiterwijk 3f97f6
        if parent:
Patrick Uiterwijk 3f97f6
            parents.append(parent)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Author/commiter will always be this one
Patrick Uiterwijk 3f97f6
        author = _make_signature(name=user.username, email=user.default_email)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Actually commit
Patrick Uiterwijk 3f97f6
        new_repo.create_commit(
Patrick Uiterwijk 3f97f6
            "refs/heads/master",
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            "Add file %s to ticket %s: %s"
Patrick Uiterwijk 3f97f6
            % (filename, issue.uid, issue.title),
Patrick Uiterwijk 3f97f6
            new_repo.index.write_tree(),
Patrick Uiterwijk 3f97f6
            parents,
Patrick Uiterwijk 3f97f6
        )
Patrick Uiterwijk 3f97f6
        index.write()
Pierre-Yves Chibon 852918
Patrick Uiterwijk 3f97f6
        master_ref = new_repo.lookup_reference("HEAD").resolve()
Patrick Uiterwijk 03a519
        tempclone.push(user.username, master_ref.name)
Patrick Uiterwijk 0a0334
Patrick Uiterwijk 3f97f6
    return os.path.join("files", filename)
Patrick Uiterwijk 0a0334
Pierre-Yves Chibon 774fee
Patrick Uiterwijk 3f97f6
class TemporaryClone(object):
Patrick Uiterwijk 3f97f6
    _project = None
Patrick Uiterwijk 3f97f6
    _action = None
Patrick Uiterwijk 3f97f6
    _repotype = None
Patrick Uiterwijk 3f97f6
    _origpath = None
Pierre-Yves Chibon ded1fa
    _origrepopath = None
Patrick Uiterwijk 3f97f6
    repopath = None
Patrick Uiterwijk 3f97f6
    repo = None
Patrick Uiterwijk 3f97f6
Pierre-Yves Chibon ded1fa
    def __init__(self, project, repotype, action, path=None, parent=None):
Patrick Uiterwijk 3f97f6
        """ Initializes a TempoaryClone instance.
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        Args:
Patrick Uiterwijk 3f97f6
            project (model.Project): A project instance
Patrick Uiterwijk 3f97f6
            repotype (string): The type of repo to clone, one of:
Patrick Uiterwijk 3f97f6
                main, docs, requests, tickets
Patrick Uiterwijk 3f97f6
            action (string): Type of action performing, used in the
Patrick Uiterwijk 3f97f6
                temporary directory name
Pierre-Yves Chibon ded1fa
            path (string or None): the path to clone, allows cloning, for
Pierre-Yves Chibon ded1fa
                example remote git repo for remote PRs instead of the
Pierre-Yves Chibon ded1fa
                default one
Pierre-Yves Chibon ded1fa
            parent (string or None): Adds this directory to the path in
Pierre-Yves Chibon ded1fa
                which the project is cloned
Pierre-Yves Chibon ded1fa
Patrick Uiterwijk 3f97f6
        """
Slavek Kabrda 0915d0
        if repotype not in pagure.lib.query.get_repotypes():
Patrick Uiterwijk 3f97f6
            raise NotImplementedError("Repotype %s not known" % repotype)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        self._project = project
Patrick Uiterwijk 3f97f6
        self._repotype = repotype
Patrick Uiterwijk 3f97f6
        self._action = action
Pierre-Yves Chibon 8bced8
        self._path = path
Pierre-Yves Chibon ded1fa
        self._parent = parent
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    def __enter__(self):
Patrick Uiterwijk 3f97f6
        """ Enter the context manager, creating the clone. """
Patrick Uiterwijk 3f97f6
        self.repopath = tempfile.mkdtemp(prefix="pagure-%s-" % self._action)
Pierre-Yves Chibon ded1fa
        self._origrepopath = self.repopath
Pierre-Yves Chibon ded1fa
        if self._parent:
Pierre-Yves Chibon ded1fa
            self.repopath = os.path.join(self.repopath, self._parent)
Pierre-Yves Chibon ded1fa
            os.makedirs(self.repopath)
Patrick Uiterwijk 3f97f6
        if not self._project.is_on_repospanner:
Patrick Uiterwijk 3f97f6
            # This is the simple case. Just do a local clone
Pierre-Yves Chibon 97d19d
            # use either the specified path or the use the path of the
Pierre-Yves Chibon 97d19d
            # specified project
Pierre-Yves Chibon 8bced8
            self._origpath = self._path or self._project.repopath(
Pierre-Yves Chibon 8bced8
                self._repotype
Pierre-Yves Chibon 8bced8
            )
Patrick Uiterwijk 3f97f6
            if self._origpath is None:
Patrick Uiterwijk 3f97f6
                # No repository of this type
Patrick Uiterwijk 3f97f6
                # 'main' is already caught and returns an error in repopath()
Patrick Uiterwijk 3f97f6
                return None
Patrick Uiterwijk 3f97f6
            if not os.path.exists(self._origpath):
Patrick Uiterwijk 3f97f6
                return None
Pierre-Yves Chibon a87742
            PagureRepo.clone(self._origpath, self.repopath)
Patrick Uiterwijk 3f97f6
            # Because for whatever reason, one pygit2.Repository is not
Patrick Uiterwijk 3f97f6
            # equal to another.... The pygit2.Repository returned from
Patrick Uiterwijk 3f97f6
            # pygit2.clone_repository does not have the "branches" attribute.
Patrick Uiterwijk 3f97f6
            self.repo = pygit2.Repository(self.repopath)
Pierre-Yves Chibon c96124
            self._origrepo = pygit2.Repository(self._origpath)
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            repourl, regioninfo = self._project.repospanner_repo_info(
Patrick Uiterwijk 3f97f6
                self._repotype
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            command = [
Patrick Uiterwijk 3f97f6
                "git",
Patrick Uiterwijk 3f97f6
                "-c",
Patrick Uiterwijk 8174a4
                "protocol.ext.allow=always",
Patrick Uiterwijk 3f97f6
                "clone",
Patrick Uiterwijk 8174a4
                "ext::%s %s"
Patrick Uiterwijk 8174a4
                % (
Patrick Uiterwijk 8174a4
                    pagure_config["REPOBRIDGE_BINARY"],
Patrick Uiterwijk 8174a4
                    self._project._repospanner_repo_name(self._repotype),
Patrick Uiterwijk 8174a4
                ),
Patrick Uiterwijk 3f97f6
                self.repopath,
Patrick Uiterwijk 3f97f6
            ]
Patrick Uiterwijk 8174a4
            environ = os.environ.copy()
Patrick Uiterwijk 8174a4
            environ.update(
Patrick Uiterwijk 8174a4
                {
Patrick Uiterwijk 8174a4
                    "USER": "pagure",
Patrick Uiterwijk 8174a4
                    "REPOBRIDGE_CONFIG": ":environment:",
Patrick Uiterwijk 8174a4
                    "REPOBRIDGE_BASEURL": regioninfo["url"],
Patrick Uiterwijk 8174a4
                    "REPOBRIDGE_CA": regioninfo["ca"],
Patrick Uiterwijk 8174a4
                    "REPOBRIDGE_CERT": regioninfo["push_cert"]["cert"],
Patrick Uiterwijk 8174a4
                    "REPOBRIDGE_KEY": regioninfo["push_cert"]["key"],
Patrick Uiterwijk 8174a4
                }
Patrick Uiterwijk 8174a4
            )
Patrick Uiterwijk 3f97f6
            with open(os.devnull, "w") as devnull:
Patrick Uiterwijk 3f97f6
                subprocess.check_call(
Patrick Uiterwijk 8174a4
                    command,
Patrick Uiterwijk 8174a4
                    stdout=devnull,
Patrick Uiterwijk 8174a4
                    stderr=subprocess.STDOUT,
Patrick Uiterwijk 8174a4
                    env=environ,
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
            self.repo = pygit2.Repository(self.repopath)
Patrick Uiterwijk c58901
            self._origrepo = self.repo
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Make sure that all remote refs are mapped to local ones.
Pierre-Yves Chibon dd1ea3
        headname = None
Pierre-Yves Chibon dd1ea3
        if not self.repo.is_empty and not self.repo.head_is_unborn:
Pierre-Yves Chibon dd1ea3
            headname = self.repo.head.shorthand
Pierre-Yves Chibon c96124
Pierre-Yves Chibon c96124
        # Sync up all the references, branches and PR heads
Pierre-Yves Chibon c96124
        for ref in self._origrepo.listall_references():
Pierre-Yves Chibon c96124
            if ref.startswith("refs/heads/"):
Pierre-Yves Chibon c96124
                localname = ref.replace("refs/heads/", "")
Pierre-Yves Chibon c96124
                if localname in (headname, "HEAD"):
Pierre-Yves Chibon c96124
                    # This gets checked out by default
Pierre-Yves Chibon c96124
                    continue
Pierre-Yves Chibon c96124
                branch = self.repo.branches.remote.get("origin/%s" % localname)
Pierre-Yves Chibon 29ff0a
                self.repo.branches.local.create(localname, branch.peel())
Pierre-Yves Chibon c96124
            elif ref.startswith("refs/pull/"):
Pierre-Yves Chibon c96124
                reference = self._origrepo.references.get(ref)
Pierre-Yves Chibon 29ff0a
                self.repo.references.create(ref, reference.peel().oid.hex)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        return self
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    def __exit__(self, exc_type, exc_value, traceback):
Patrick Uiterwijk 3f97f6
        """ Exit the context manager, removing the temorary clone. """
Patrick Uiterwijk 3f97f6
        shutil.rmtree(self.repopath)
Patrick Uiterwijk 3f97f6
Slavek Kabrda 29bdb9
    def change_project_association(self, new_project):
Slavek Kabrda 29bdb9
        """ Make this instance "belong" to another project.
Slavek Kabrda 29bdb9
Slavek Kabrda 29bdb9
        This is useful when you want to create TemporaryClone of one project
Slavek Kabrda 29bdb9
        and then push some of its content into a different project just
Slavek Kabrda 29bdb9
        by running ``push`` or ``mirror`` methods.
Slavek Kabrda 29bdb9
Slavek Kabrda 29bdb9
        Args:
Slavek Kabrda 29bdb9
            new_project (pagure.lib.model.Project): project to associate
Slavek Kabrda 29bdb9
                this TemporaryClone instance with
Slavek Kabrda 29bdb9
        """
Slavek Kabrda 29bdb9
        self._project = new_project
Slavek Kabrda 29bdb9
        if not new_project.is_on_repospanner:
Slavek Kabrda 29bdb9
            self.repo.remotes.set_push_url(
Slavek Kabrda 29bdb9
                "origin", new_project.repopath("main")
Slavek Kabrda 29bdb9
            )
Slavek Kabrda 29bdb9
Slavek Kabrda 29bdb9
    def mirror(self, username, force=False, **extra):
Slavek Kabrda 29bdb9
        """ Run ``git push --mirror`` of the repo to its origin.
Slavek Kabrda 29bdb9
Slavek Kabrda 29bdb9
        Args:
Slavek Kabrda 29bdb9
            username (string): The user on who's account this push is
Slavek Kabrda 29bdb9
            force (bool): whether or not to use ``--force`` when pushing
Slavek Kabrda 29bdb9
            extra (dict): Extra fields passed to the remote side. Either via
Slavek Kabrda 29bdb9
                environment variables, or as X-Extra-<key> HTTP headers.</key>
Slavek Kabrda 29bdb9
        """
Slavek Kabrda 29bdb9
        # git push --mirror fails if there are no branches
Slavek Kabrda 29bdb9
        if len(list(self.repo.branches)) > 0:
Slavek Kabrda 29bdb9
            self._push(username, "--mirror", force, **extra)
Slavek Kabrda 29bdb9
Pierre-Yves Chibon da3640
    def push(self, username, sbranch, tbranch=None, force=False, **extra):
Patrick Uiterwijk 3f97f6
        """ Push the repo back to its origin.
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        Args:
Patrick Uiterwijk 03a519
            username (string): The user on who's account this push is
Patrick Uiterwijk 3f97f6
            sbranch (string): Source branch to push
Patrick Uiterwijk 3f97f6
            tbranch (string): Target branch if different from sbranch
Slavek Kabrda 29bdb9
            force (bool): whether or not to use ``--force`` when pushing
Patrick Uiterwijk 03a519
            extra (dict): Extra fields passed to the remote side. Either via
Patrick Uiterwijk 03a519
                environment variables, or as X-Extra-<key> HTTP headers.</key>
Patrick Uiterwijk 3f97f6
        """
Patrick Uiterwijk 3f97f6
        pushref = "%s:%s" % (sbranch, tbranch if tbranch else sbranch)
Slavek Kabrda 29bdb9
        self._push(username, pushref, force, **extra)
Slavek Kabrda 29bdb9
Slavek Kabrda 29bdb9
    def _push(self, username, pushref, force, **extra):
Slavek Kabrda 29bdb9
        """ Push the repo back to its origin.
Patrick Uiterwijk 3f97f6
Slavek Kabrda 29bdb9
        Args:
Slavek Kabrda 29bdb9
            username (string): The user on who's account this push is
Slavek Kabrda 29bdb9
            pushref(string): either ``<sbranch>:<tbranch>`` or ``--mirror``</tbranch></sbranch>
Slavek Kabrda 29bdb9
            force (bool): whether or not to use ``--force`` when pushing
Slavek Kabrda 29bdb9
            extra (dict): Extra fields passed to the remote side. Either via
Slavek Kabrda 29bdb9
                environment variables, or as X-Extra-<key> HTTP headers.</key>
Slavek Kabrda 29bdb9
        """
Patrick Uiterwijk 03a519
        if "pull_request" in extra:
Patrick Uiterwijk 03a519
            extra["pull_request_uid"] = extra["pull_request"].uid
Patrick Uiterwijk 03a519
            del extra["pull_request"]
Patrick Uiterwijk 03a519
Patrick Uiterwijk 3f97f6
        if self._project.is_on_repospanner:
Patrick Uiterwijk 3f97f6
            regioninfo = pagure_config["REPOSPANNER_REGIONS"][
Patrick Uiterwijk 3f97f6
                self._project.repospanner_region
Patrick Uiterwijk 3f97f6
            ]
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
            extra.update(
Patrick Uiterwijk d29158
                {
Patrick Uiterwijk d29158
                    "username": username,
Patrick Uiterwijk d29158
                    "repotype": self._repotype,
Patrick Uiterwijk d29158
                    "project_name": self._project.name,
Patrick Uiterwijk d29158
                    "project_user": self._project.user.username
Patrick Uiterwijk d29158
                    if self._project.is_fork
Patrick Uiterwijk d29158
                    else "",
Patrick Uiterwijk d29158
                    "project_namespace": self._project.namespace or "",
Patrick Uiterwijk d29158
                }
Patrick Uiterwijk d29158
            )
Patrick Uiterwijk 8174a4
            args = []
Patrick Uiterwijk 8174a4
            for opt in extra:
Patrick Uiterwijk 8174a4
                args.extend(["--extra", opt, extra[opt]])
Patrick Uiterwijk 8174a4
            command = [
Patrick Uiterwijk 8174a4
                "git",
Patrick Uiterwijk 3f97f6
                "-c",
Patrick Uiterwijk 8174a4
                "protocol.ext.allow=always",
Patrick Uiterwijk 8174a4
                "push",
Patrick Uiterwijk 8174a4
                "ext::%s %s %s"
Patrick Uiterwijk 8174a4
                % (
Patrick Uiterwijk 8174a4
                    pagure_config["REPOBRIDGE_BINARY"],
Patrick Uiterwijk 8174a4
                    " ".join(args),
Patrick Uiterwijk 8174a4
                    self._project._repospanner_repo_name(self._repotype),
Patrick Uiterwijk 8174a4
                ),
Patrick Uiterwijk 8174a4
                "--repo",
Patrick Uiterwijk 8174a4
                self.repopath,
Patrick Uiterwijk 3f97f6
            ]
Patrick Uiterwijk 8174a4
            environ = {
Patrick Uiterwijk 8174a4
                "USER": "pagure",
Patrick Uiterwijk 8174a4
                "REPOBRIDGE_CONFIG": ":environment:",
Patrick Uiterwijk 8174a4
                "REPOBRIDGE_BASEURL": regioninfo["url"],
Patrick Uiterwijk 8174a4
                "REPOBRIDGE_CA": regioninfo["ca"],
Patrick Uiterwijk 8174a4
                "REPOBRIDGE_CERT": regioninfo["push_cert"]["cert"],
Patrick Uiterwijk 8174a4
                "REPOBRIDGE_KEY": regioninfo["push_cert"]["key"],
Patrick Uiterwijk 8174a4
            }
Patrick Uiterwijk 8174a4
        else:
Patrick Uiterwijk 8174a4
            command = ["git", "push", "origin"]
Pierre-Yves Chibon da3640
            if force:
Pierre-Yves Chibon da3640
                command.append("--force")
Patrick Uiterwijk 8174a4
            environ = {}
Patrick Uiterwijk 3f97f6
Slavek Kabrda be430b
        command.append("--follow-tags")
Slavek Kabrda be430b
Patrick Uiterwijk 3f97f6
        try:
Patrick Uiterwijk b2cb9c
            _log.debug(
Patrick Uiterwijk b2cb9c
                "Running a git push of %s to %s"
Pierre-Yves Chibon 8bced8
                % (pushref, self._path or self._project.fullname)
Patrick Uiterwijk b2cb9c
            )
Patrick Uiterwijk 03a519
            env = os.environ.copy()
Patrick Uiterwijk 03a519
            env["GL_USER"] = username
Pierre-Yves Chibon c26740
            env["GL_BYPASS_ACCESS_CHECKS"] = "1"
Pierre-Yves Chibon 00d0c4
            if pagure_config.get("GITOLITE_HOME"):
Pierre-Yves Chibon 00d0c4
                env["HOME"] = pagure_config["GITOLITE_HOME"]
Patrick Uiterwijk 8174a4
            env.update(environ)
Patrick Uiterwijk 03a519
            env.update(extra)
Patrick Uiterwijk b2cb9c
            out = subprocess.check_output(
Patrick Uiterwijk 8174a4
                command + [pushref],
Patrick Uiterwijk 3f97f6
                cwd=self.repopath,
Patrick Uiterwijk 3f97f6
                stderr=subprocess.STDOUT,
Patrick Uiterwijk 03a519
                env=env,
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk b2cb9c
            _log.debug("Output: %s" % out)
Patrick Uiterwijk 3f97f6
        except subprocess.CalledProcessError as ex:
Patrick Uiterwijk 3f97f6
            # This should never really happen, since we control the repos, but
Patrick Uiterwijk 3f97f6
            # this way, we can be sure to get the output logged
Patrick Uiterwijk b2cb9c
            remotes = []
Patrick Uiterwijk b2cb9c
            for line in ex.output.decode("utf-8").split("\n"):
Patrick Uiterwijk d29158
                _log.info("Remote line: %s", line)
Patrick Uiterwijk b2cb9c
                if line.startswith("remote: "):
Patrick Uiterwijk b2cb9c
                    _log.debug("Remote: %s" % line)
Patrick Uiterwijk b2cb9c
                    remotes.append(line[len("remote: ") :].strip())
Patrick Uiterwijk b2cb9c
            if remotes:
Patrick Uiterwijk b2cb9c
                _log.info("Remote rejected with: %s" % remotes)
Patrick Uiterwijk b2cb9c
                raise pagure.exceptions.PagurePushDenied(
Patrick Uiterwijk b2cb9c
                    "Remote hook declined the push: %s" % "\n".join(remotes)
Patrick Uiterwijk b2cb9c
                )
Patrick Uiterwijk b2cb9c
            else:
Patrick Uiterwijk b2cb9c
                # Something else happened, pass the original
Patrick Uiterwijk b2cb9c
                _log.exception("Error pushing. Output: %s", ex.output)
Patrick Uiterwijk b2cb9c
                raise
Patrick Uiterwijk 3f97f6
Pierre-Yves Chibon 2ed535
Patrick Uiterwijk 8cb224
def _update_file_in_git(
Patrick Uiterwijk 03a519
    repo, branch, branchto, filename, content, message, user, email
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon 9c2953
    """ Update a specific file in the specified repository with the content
Pierre-Yves Chibon 54335c
    given and commit the change under the user's name.
Pierre-Yves Chibon 54335c
Pierre-Yves Chibon 54335c
    :arg repo: the Project object from the database
Pierre-Yves Chibon e59791
    :arg branch: the branch from which the edit is made
Pierre-Yves Chibon e59791
    :arg branchto: the name of the branch into which to edit the file
Pierre-Yves Chibon 54335c
    :arg filename: the name of the file to save
Pierre-Yves Chibon 54335c
    :arg content: the new content of the file
Pierre-Yves Chibon 54335c
    :arg message: the message of the git commit
Pierre-Yves Chibon e59791
    :arg user: the user name, to use in the commit
Pierre-Yves Chibon e59791
    :arg email: the email of the user, to use in the commit
Pierre-Yves Chibon 54335c
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon 9c2953
    _log.info("Updating file: %s in the repo: %s", filename, repo.path)
Pierre-Yves Chibon 54335c
Patrick Uiterwijk 3f97f6
    with TemporaryClone(repo, "main", "edit_file") as tempclone:
Patrick Uiterwijk 3f97f6
        newpath = tempclone.repopath
Patrick Uiterwijk 3f97f6
        new_repo = tempclone.repo
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        new_repo.checkout("refs/heads/%s" % branch)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        file_path = os.path.join(newpath, filename)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Get the current index
Patrick Uiterwijk 3f97f6
        index = new_repo.index
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Write down what changed
Patrick Uiterwijk 3f97f6
        with open(file_path, "wb") as stream:
Patrick Uiterwijk 3f97f6
            stream.write(content.replace("\r", "").encode("utf-8"))
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Retrieve the list of files that changed
Patrick Uiterwijk 3f97f6
        diff = new_repo.diff()
Patrick Uiterwijk 3f97f6
        files = []
Patrick Uiterwijk 3f97f6
        for patch in diff:
Patrick Uiterwijk 3f97f6
            files.append(patch.delta.new_file.path)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Add the changes to the index
Patrick Uiterwijk 3f97f6
        added = False
Patrick Uiterwijk 3f97f6
        for filename in files:
Patrick Uiterwijk 3f97f6
            added = True
Patrick Uiterwijk 3f97f6
            index.add(filename)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # If not change, return
Patrick Uiterwijk 3f97f6
        if not files and not added:
Patrick Uiterwijk 3f97f6
            return
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # See if there is a parent to this commit
Patrick Uiterwijk 3f97f6
        branch_ref = get_branch_ref(new_repo, branch)
Pierre-Yves Chibon 29ff0a
        parent = branch_ref.peel()
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # See if we need to create the branch
Patrick Uiterwijk 3f97f6
        nbranch_ref = None
Patrick Uiterwijk 3f97f6
        if branchto not in new_repo.listall_branches():
Patrick Uiterwijk 3f97f6
            nbranch_ref = new_repo.create_branch(branchto, parent)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        parents = []
Patrick Uiterwijk 3f97f6
        if parent:
Patrick Uiterwijk 3f97f6
            parents.append(parent.hex)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Author/commiter will always be this one
Patrick Uiterwijk 3f97f6
        name = user.fullname or user.username
Patrick Uiterwijk 3f97f6
        author = _make_signature(name=name, email=email)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        # Actually commit
Patrick Uiterwijk 03a519
        new_repo.create_commit(
Patrick Uiterwijk 3f97f6
            nbranch_ref.name if nbranch_ref else branch_ref.name,
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            author,
Patrick Uiterwijk 3f97f6
            message.strip(),
Patrick Uiterwijk 3f97f6
            new_repo.index.write_tree(),
Patrick Uiterwijk 3f97f6
            parents,
Pierre-Yves Chibon 9c2953
        )
Patrick Uiterwijk 3f97f6
        index.write()
Pierre-Yves Chibon 08eaf8
Patrick Uiterwijk 3f97f6
        tempclone.push(
Patrick Uiterwijk 03a519
            user.username,
Patrick Uiterwijk 03a519
            nbranch_ref.name if nbranch_ref else branch_ref.name,
Patrick Uiterwijk 03a519
            branchto,
Pierre-Yves Chibon e59791
        )
Pierre-Yves Chibon e59791
Pierre-Yves Chibon 9c2953
    return os.path.join("files", filename)
Pierre-Yves Chibon 54335c
Pierre-Yves Chibon 54335c
Pierre-Yves Chibon 86e98d
def read_output(cmd, abspath, input=None, keepends=False, error=False, **kw):
Pierre-Yves Chibon 86e98d
    """ Read the output from the given command to run.
Pierre-Yves Chibon 86e98d
Pierre-Yves Chibon 86e98d
    cmd:
Pierre-Yves Chibon 86e98d
        The command to run, this is a list with each space separated into an
Pierre-Yves Chibon 86e98d
        element of the list.
Pierre-Yves Chibon 86e98d
    abspath:
Pierre-Yves Chibon 86e98d
        The absolute path where the command should be ran.
Pierre-Yves Chibon 86e98d
    input:
Pierre-Yves Chibon 86e98d
        Whether the command should take input from stdin or not.
Pierre-Yves Chibon 86e98d
        (Defaults to False)
Pierre-Yves Chibon 86e98d
    keepends:
Pierre-Yves Chibon 86e98d
        Whether to strip the newline characters at the end of the standard
Pierre-Yves Chibon 86e98d
        output or not.
Pierre-Yves Chibon 86e98d
    error:
Pierre-Yves Chibon 86e98d
        Whether to return both the standard output and the standard error,
Pierre-Yves Chibon 86e98d
        or just the standard output.
Pierre-Yves Chibon 86e98d
        (Defaults to False).
Pierre-Yves Chibon 86e98d
    kw*:
Pierre-Yves Chibon 86e98d
        Any other arguments to be passed onto the subprocess.Popen command,
Pierre-Yves Chibon 86e98d
        such as env, shell, executable...
Pierre-Yves Chibon 86e98d
Pierre-Yves Chibon 86e98d
    """
Pierre-Yves Chibon 2ed535
    if input:
Pierre-Yves Chibon 2ed535
        stdin = subprocess.PIPE
Pierre-Yves Chibon 2ed535
    else:
Pierre-Yves Chibon 2ed535
        stdin = None
Pierre-Yves Chibon 164a57
    procs = subprocess.Popen(
Pierre-Yves Chibon 2ed535
        cmd,
Pierre-Yves Chibon 2ed535
        stdin=stdin,
Pierre-Yves Chibon 2ed535
        stdout=subprocess.PIPE,
Pierre-Yves Chibon 2ed535
        stderr=subprocess.PIPE,
Pierre-Yves Chibon 2ed535
        cwd=abspath,
Pierre-Yves Chibon 9c2953
        **kw
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 164a57
    retcode = procs.wait()
Pierre-Yves Chibon 227f4c
    (out, err) = procs.communicate(input)
Pierre-Yves Chibon 6e566a
    if isinstance(out, six.binary_type):
Pierre-Yves Chibon 227f4c
        out = out.decode("utf-8")
Pierre-Yves Chibon 6e566a
    if isinstance(err, six.binary_type):
Pierre-Yves Chibon 227f4c
        err = err.decode("utf-8")
Pierre-Yves Chibon 2ed535
    if retcode:
Pierre-Yves Chibon 9c2953
        print("ERROR: %s =-- %s" % (cmd, retcode))
Aurélien Bompard 831553
        print(out)
Aurélien Bompard 831553
        print(err)
Pierre-Yves Chibon 2ed535
    if not keepends:
Pierre-Yves Chibon 7943c2
        out = out.rstrip("\n\r")
Pierre-Yves Chibon 86e98d
Pierre-Yves Chibon 86e98d
    if error:
Pierre-Yves Chibon 86e98d
        return (out, err)
Pierre-Yves Chibon 86e98d
    else:
Pierre-Yves Chibon 86e98d
        return out
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 86e98d
def read_git_output(
Pierre-Yves Chibon 9c2953
    args, abspath, input=None, keepends=False, error=False, **kw
Pierre-Yves Chibon 9c2953
):
Pierre-Yves Chibon 2ed535
    """Read the output of a Git command."""
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 432e7d
    return read_output(
Pierre-Yves Chibon 9c2953
        ["git"] + args,
Pierre-Yves Chibon 9c2953
        abspath,
Pierre-Yves Chibon 9c2953
        input=input,
Pierre-Yves Chibon 9c2953
        keepends=keepends,
Pierre-Yves Chibon 9c2953
        error=error,
Pierre-Yves Chibon 9c2953
        **kw
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 86e98d
def read_git_lines(args, abspath, keepends=False, error=False, **kw):
Pierre-Yves Chibon 2ed535
    """Return the lines output by Git command.
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
    Return as single lines, with newlines stripped off."""
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 86e98d
    if error:
Pierre-Yves Chibon 86e98d
        return read_git_output(
Pierre-Yves Chibon 86e98d
            args, abspath, keepends=keepends, error=error, **kw
Pierre-Yves Chibon 86e98d
        )
Pierre-Yves Chibon 86e98d
    else:
Pierre-Yves Chibon 86e98d
        return read_git_output(
Pierre-Yves Chibon 86e98d
            args, abspath, keepends=keepends, **kw
Pierre-Yves Chibon 86e98d
        ).splitlines(keepends)
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
Clement Verna f202e4
def get_revs_between(oldrev, newrev, abspath, refname, forced=False):
Pierre-Yves Chibon 2ed535
    """ Yield revisions between HEAD and BASE. """
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 9c2953
    cmd = ["rev-list", "%s...%s" % (oldrev, newrev)]
Pierre-Yves Chibon 72398f
    if forced:
Pierre-Yves Chibon 72398f
        head = get_default_branch(abspath)
Pierre-Yves Chibon 9c2953
        cmd.append("^%s" % head)
Pierre-Yves Chibon 9c2953
    if set(newrev) == set("0"):
Pierre-Yves Chibon 9c2953
        cmd = ["rev-list", "%s" % oldrev]
Pierre-Yves Chibon 9c2953
    elif set(oldrev) == set("0") or set(oldrev) == set("^0"):
Pierre-Yves Chibon 75a9b6
        head = get_default_branch(abspath)
Pierre-Yves Chibon 9c2953
        cmd = ["rev-list", "%s" % newrev, "^%s" % head]
Clement Verna f202e4
        if head in refname:
Pierre-Yves Chibon 9c2953
            cmd = ["rev-list", "%s" % newrev]
Pierre-Yves Chibon 432e7d
    return pagure.lib.git.read_git_lines(cmd, abspath)
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon b1f2a1
def is_forced_push(oldrev, newrev, abspath):
Abhijeet Kasurde a6221b
    """ Returns whether there was a force push between HEAD and BASE.
Pierre-Yves Chibon b1f2a1
    Doc: http://stackoverflow.com/a/12258773
Pierre-Yves Chibon b1f2a1
    """
Pierre-Yves Chibon 599a9d
Pierre-Yves Chibon 9c2953
    if set(oldrev) == set("0"):
Slavek Kabrda 595b6f
        # This is a push that's creating a new branch => certainly ok
Slavek Kabrda 595b6f
        return False
Pierre-Yves Chibon 599a9d
    # Returns if there was any commits deleted in the changeset
Pierre-Yves Chibon 9c2953
    cmd = ["rev-list", "%s" % oldrev, "^%s" % newrev]
Pierre-Yves Chibon 599a9d
    out = pagure.lib.git.read_git_lines(cmd, abspath)
Pierre-Yves Chibon 599a9d
    return len(out) > 0
Pierre-Yves Chibon 599a9d
Pierre-Yves Chibon 599a9d
Pierre-Yves Chibon 20364a
def get_base_revision(torev, fromrev, abspath):
Pierre-Yves Chibon 20364a
    """ Return the base revision between HEAD and BASE.
Pierre-Yves Chibon 20364a
    This is useful in case of force-push.
Pierre-Yves Chibon 20364a
    """
Pierre-Yves Chibon 9c2953
    cmd = ["merge-base", fromrev, torev]
Pierre-Yves Chibon 20364a
    return pagure.lib.git.read_git_lines(cmd, abspath)
Pierre-Yves Chibon 20364a
Pierre-Yves Chibon 20364a
Pierre-Yves Chibon 2a5cba
def get_default_branch(abspath):
Pierre-Yves Chibon 2a5cba
    """ Return the default branch of a repo. """
Pierre-Yves Chibon 9c2953
    cmd = ["rev-parse", "--abbrev-ref", "HEAD"]
Pierre-Yves Chibon 2a5cba
    out = pagure.lib.git.read_git_lines(cmd, abspath)
Pierre-Yves Chibon 2a5cba
    if out:
Pierre-Yves Chibon 2a5cba
        return out[0]
Pierre-Yves Chibon 2a5cba
    else:
Pierre-Yves Chibon 9c2953
        return "master"
Pierre-Yves Chibon 2a5cba
Pierre-Yves Chibon 2a5cba
Pierre-Yves Chibon 00d774
def get_author(commit, abspath):
Pierre-Yves Chibon 9c2953
    """ Return the name of the person that authored the commit. """
Pierre-Yves Chibon 2ed535
    user = pagure.lib.git.read_git_lines(
Pierre-Yves Chibon 9c2953
        ["log", "-1", '--pretty=format:"%an"', commit], abspath
Pierre-Yves Chibon 9c2953
    )[0].replace('"', "")
Pierre-Yves Chibon 2ed535
    return user
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 2ed535
Pierre-Yves Chibon 00d774
def get_author_email(commit, abspath):
Pierre-Yves Chibon 9c2953
    """ Return the email of the person that authored the commit. """
Pierre-Yves Chibon 2ed535
    user = pagure.lib.git.read_git_lines(
Pierre-Yves Chibon 9c2953
        ["log", "-1", '--pretty=format:"%ae"', commit], abspath
Pierre-Yves Chibon 9c2953
    )[0].replace('"', "")
Pierre-Yves Chibon 2ed535
    return user
Pierre-Yves Chibon 432e7d
Pierre-Yves Chibon 432e7d
Matt Prahl b49f93
def get_commit_subject(commit, abspath):
Pierre-Yves Chibon 9c2953
    """ Return the subject of the commit. """
Matt Prahl b49f93
    subject = pagure.lib.git.read_git_lines(
Pierre-Yves Chibon 9c2953
        ["log", "-1", '--pretty=format:"%s"', commit], abspath
Pierre-Yves Chibon 9c2953
    )[0].replace('"', "")
Matt Prahl b49f93
    return subject
Matt Prahl b49f93
Matt Prahl b49f93
Patrick Uiterwijk 7b7c23
def get_repo_info_from_path(gitdir, hide_notfound=False):
Patrick Uiterwijk 4012dc
    """ Returns the name, username, namespace and type of a git directory
Pierre-Yves Chibon 432e7d
Patrick Uiterwijk 4012dc
    This gets computed based on the *_FOLDER's in the config file,
Patrick Uiterwijk 4012dc
    and as such only works for the central file-based repositories.
Pierre-Yves Chibon 432e7d
Patrick Uiterwijk 4012dc
    Args:
Patrick Uiterwijk 4012dc
        gitdir (string): Path of the canonical git repository
Patrick Uiterwijk 7b7c23
        hide_notfound (bool): Whether to return a tuple with None's instead of
Patrick Uiterwijk 7b7c23
            raising an error if the regenerated repo didn't exist.
Patrick Uiterwijk 7b7c23
            Can be used to hide the difference between no project access vs not
Patrick Uiterwijk 7b7c23
            existing when looking up private repos.
Patrick Uiterwijk 4012dc
    Return: (tuple): Tuple with (repotype, username, namespace, repo)
Patrick Uiterwijk 4012dc
        Some of these elements may be None if not applicable.
Pierre-Yves Chibon 9c2953
    """
Patrick Uiterwijk 4012dc
    if not os.path.isabs(gitdir):
Patrick Uiterwijk 4012dc
        raise ValueError("Tried to locate non-absolute gitdir %s" % gitdir)
Patrick Uiterwijk 4012dc
    gitdir = os.path.normpath(gitdir)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    types = {
Patrick Uiterwijk d75948
        "main": pagure_config["GIT_FOLDER"],
Patrick Uiterwijk d75948
        "docs": pagure_config["DOCS_FOLDER"],
Patrick Uiterwijk d75948
        "tickets": pagure_config["TICKETS_FOLDER"],
Patrick Uiterwijk d75948
        "requests": pagure_config["REQUESTS_FOLDER"],
Patrick Uiterwijk 4012dc
    }
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    match = None
Patrick Uiterwijk 4012dc
    matchlen = None
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    # First find the longest match in types. This makes sure that even if the
Patrick Uiterwijk 4012dc
    # non-main repos are in a subdir of main (i.e. repos/ and repos/tickets/),
Patrick Uiterwijk 4012dc
    # we find the correct type.
Patrick Uiterwijk 4012dc
    for typename in types:
Patrick Uiterwijk d75948
        if not types[typename]:
Patrick Uiterwijk d75948
            continue
Patrick Uiterwijk d75948
        types[typename] = os.path.abspath(types[typename])
Patrick Uiterwijk 4012dc
        path = types[typename] + "/"
Patrick Uiterwijk 4012dc
        if gitdir.startswith(path) and (
Patrick Uiterwijk 4012dc
            matchlen is None or len(path) > matchlen
Patrick Uiterwijk 4012dc
        ):
Patrick Uiterwijk 4012dc
            match = typename
Patrick Uiterwijk 4012dc
            matchlen = len(path)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    if match is None:
Pierre-Yves Chibon 4cc343
        raise ValueError("Gitdir %s could not be located" % gitdir)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    typepath = types[match]
Patrick Uiterwijk 4012dc
    guesspath = gitdir[len(typepath) + 1 :]
Patrick Uiterwijk 4012dc
    if len(guesspath) < 5:
Patrick Uiterwijk 4012dc
        # At least 4 characters for ".git" is required plus one for project
Patrick Uiterwijk 4012dc
        # name
Patrick Uiterwijk 4012dc
        raise ValueError("Invalid gitdir %s located" % gitdir)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    # Just in the case we run on a non-*nix system...
Patrick Uiterwijk 4012dc
    guesspath = guesspath.replace("\\", "/")
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    # Now guesspath should be one of:
Patrick Uiterwijk 4012dc
    # - reponame.git
Patrick Uiterwijk 4012dc
    # - namespace/reponame.git
Patrick Uiterwijk 4012dc
    # - forks/username/reponame.git
Patrick Uiterwijk 4012dc
    # - forks/username/namespace/reponame.git
Patrick Uiterwijk 4012dc
    repotype = match
Patrick Uiterwijk 4012dc
    username = None
Pierre-Yves Chibon a4db36
    namespace = None
Patrick Uiterwijk 4012dc
    repo = None
Pierre-Yves Chibon a4db36
Patrick Uiterwijk 4012dc
    guesspath, repo = os.path.split(guesspath)
Patrick Uiterwijk 4012dc
    if not repo.endswith(".git"):
Patrick Uiterwijk 4012dc
        raise ValueError("Git dir looks to not be a bare repo")
Patrick Uiterwijk 4012dc
    repo = repo[: -len(".git")]
Patrick Uiterwijk 4012dc
    if not repo:
Patrick Uiterwijk 4012dc
        raise ValueError("Gitdir %s seems to not be a bare repo" % gitdir)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    # Split the guesspath up, throwing out any empty strings
Patrick Uiterwijk 4012dc
    splitguess = [part for part in guesspath.split("/") if part]
Patrick Uiterwijk 4012dc
    if splitguess and splitguess[0] == "forks":
Patrick Uiterwijk 4012dc
        if len(splitguess) < 2:
Patrick Uiterwijk 4012dc
            raise ValueError("Invalid gitdir %s" % gitdir)
Patrick Uiterwijk 4012dc
        username = splitguess[1]
Patrick Uiterwijk 4012dc
        splitguess = splitguess[2:]
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    if splitguess:
Patrick Uiterwijk 4012dc
        # At this point, we've cut off the repo name at the end, and any forks/
Patrick Uiterwijk 4012dc
        # indicators and their usernames are also removed, so remains just the
Patrick Uiterwijk 4012dc
        # namespace
Patrick Uiterwijk 4012dc
        namespace = os.path.join(*splitguess)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    # Okay, we think we have everything. Let's make doubly sure the path is
Patrick Uiterwijk 4012dc
    # correct and exists
Patrick Uiterwijk 4012dc
    rebuiltpath = os.path.join(
Patrick Uiterwijk 4012dc
        typepath,
Patrick Uiterwijk 4012dc
        "forks/" if username else "",
Patrick Uiterwijk 4012dc
        username if username else "",
Patrick Uiterwijk 4012dc
        namespace if namespace else "",
Patrick Uiterwijk 4012dc
        repo + ".git",
Pierre-Yves Chibon 9c2953
    )
Patrick Uiterwijk 4012dc
    if os.path.normpath(rebuiltpath) != gitdir:
Patrick Uiterwijk 4012dc
        raise ValueError(
Patrick Uiterwijk 4012dc
            "Rebuilt %s path not identical to gitdir %s"
Patrick Uiterwijk 4012dc
            % (rebuiltpath, gitdir)
Patrick Uiterwijk 4012dc
        )
Patrick Uiterwijk d75948
    if not os.path.exists(rebuiltpath) and not hide_notfound:
Patrick Uiterwijk d75948
        raise ValueError("Splitting gitdir %s failed" % gitdir)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    return (repotype, username, namespace, repo)
Pierre-Yves Chibon 09d70b
Pierre-Yves Chibon 09d70b
Patrick Uiterwijk 4012dc
def get_repo_name(abspath):
Patrick Uiterwijk 4012dc
    """ Return the name of the git repo based on its path.
Patrick Uiterwijk 4012dc
    """
Patrick Uiterwijk 4012dc
    _, _, _, name = get_repo_info_from_path(abspath)
Patrick Uiterwijk 4012dc
    return name
Pierre-Yves Chibon a4db36
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
def get_repo_namespace(abspath, gitfolder=None):
Patrick Uiterwijk 4012dc
    """ Return the name of the git repo based on its path.
Patrick Uiterwijk 4012dc
    """
Patrick Uiterwijk 4012dc
    _, _, namespace, _ = get_repo_info_from_path(abspath)
Pierre-Yves Chibon a4db36
    return namespace
Pierre-Yves Chibon a4db36
Pierre-Yves Chibon a4db36
Pierre-Yves Chibon 432e7d
def get_username(abspath):
Pierre-Yves Chibon 9c2953
    """ Return the username of the git repo based on its path.
Pierre-Yves Chibon 9c2953
    """
Patrick Uiterwijk 4012dc
    _, username, _, _ = get_repo_info_from_path(abspath)
Pierre-Yves Chibon 432e7d
    return username
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon c57267
def get_branch_ref(repo, branchname):
Pierre-Yves Chibon 9c2953
    """ Return the reference to the specified branch or raises an exception.
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon 9126bb
    location = pygit2.GIT_BRANCH_LOCAL
Pierre-Yves Chibon 9126bb
    if branchname not in repo.listall_branches():
Pierre-Yves Chibon 9c2953
        branchname = "origin/%s" % branchname
Pierre-Yves Chibon 9126bb
        location = pygit2.GIT_BRANCH_REMOTE
Pierre-Yves Chibon e30013
    branch_ref = repo.lookup_branch(branchname, location)
Pierre-Yves Chibon 9126bb
Pierre-Yves Chibon e30013
    if not branch_ref or not branch_ref.resolve():
Pierre-Yves Chibon c57267
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9c2953
            "No refs found for %s" % branchname
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon e30013
    return branch_ref.resolve()
Pierre-Yves Chibon c57267
Pierre-Yves Chibon c57267
Patrick Uiterwijk 3f97f6
def merge_pull_request(session, request, username, domerge=True):
Pierre-Yves Chibon 9c2953
    """ Merge the specified pull-request.
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon b73de8
    if domerge:
Pierre-Yves Chibon 9c2953
        _log.info("%s asked to merge the pull-request: %s", username, request)
Pierre-Yves Chibon b73de8
    else:
Pierre-Yves Chibon 9c2953
        _log.info("%s asked to diff the pull-request: %s", username, request)
Pierre-Yves Chibon b73de8
Pierre-Yves Chibon c96124
    repopath = None
Pierre-Yves Chibon 28fa3d
    if request.remote:
Pierre-Yves Chibon 28fa3d
        # Get the fork
Pierre-Yves Chibon b130e5
        repopath = pagure.utils.get_remote_repo_path(
Pierre-Yves Chibon 9c2953
            request.remote_git, request.branch_from
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 59f36c
    elif request.project_from:
Pierre-Yves Chibon 28fa3d
        # Get the fork
Pierre-Yves Chibon b130e5
        repopath = pagure.utils.get_repo_path(request.project_from)
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon c96124
    fork_obj = None
Pierre-Yves Chibon c96124
    if repopath:
Pierre-Yves Chibon c96124
        fork_obj = PagureRepo(repopath)
Pierre-Yves Chibon 0aaeb1
Patrick Uiterwijk 3f97f6
    with TemporaryClone(request.project, "main", "merge_pr") as tempclone:
Patrick Uiterwijk 3f97f6
        new_repo = tempclone.repo
Pierre-Yves Chibon d10db0
Patrick Uiterwijk 3f97f6
        # Update the start and stop commits in the DB, one last time
Patrick Uiterwijk 3f97f6
        diff_commits = diff_pull_request(
Pierre-Yves Chibon b983f9
            session,
Pierre-Yves Chibon b983f9
            request,
Pierre-Yves Chibon b983f9
            fork_obj,
Pierre-Yves Chibon b983f9
            new_repo,
Pierre-Yves Chibon b983f9
            with_diff=False,
Pierre-Yves Chibon b983f9
            username=username,
Pierre-Yves Chibon 9c2953
        )
Patrick Uiterwijk 3f97f6
        _log.info("  %s commit to merge", len(diff_commits))
Pierre-Yves Chibon bef1de
Patrick Uiterwijk 3f97f6
        if request.project.settings.get(
Patrick Uiterwijk 3f97f6
            "Enforce_signed-off_commits_in_pull-request", False
Patrick Uiterwijk 3f97f6
        ):
Patrick Uiterwijk 3f97f6
            for commit in diff_commits:
Patrick Uiterwijk 3f97f6
                if "signed-off-by" not in commit.message.lower():
Patrick Uiterwijk 3f97f6
                    _log.info("  Missing a required: signed-off-by: Bailing")
Patrick Uiterwijk 3f97f6
                    raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                        "This repo enforces that all commits are "
Patrick Uiterwijk 3f97f6
                        "signed off by their author. "
Patrick Uiterwijk 3f97f6
                    )
Pierre-Yves Chibon 28fa3d
Pierre-Yves Chibon c96124
        if not new_repo.is_empty and not new_repo.head_is_unborn:
Pierre-Yves Chibon c96124
            try:
Pierre-Yves Chibon c96124
                branch_ref = get_branch_ref(new_repo, request.branch)
Pierre-Yves Chibon c96124
            except pagure.exceptions.PagureException:
Pierre-Yves Chibon c96124
                branch_ref = None
Pierre-Yves Chibon c96124
            if not branch_ref:
Pierre-Yves Chibon c96124
                _log.info("  Target branch could not be found")
Pierre-Yves Chibon c96124
                raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon c96124
                    "Branch %s could not be found in the repo %s"
Pierre-Yves Chibon c96124
                    % (request.branch, request.project.fullname)
Pierre-Yves Chibon c96124
                )
Pierre-Yves Chibon c96124
Pierre-Yves Chibon c96124
            new_repo.checkout(branch_ref)
Pierre-Yves Chibon c96124
Pierre-Yves Chibon c96124
        if fork_obj:
Pierre-Yves Chibon c96124
            # Check/Get the branch from
Patrick Uiterwijk 3f97f6
            branch = None
Pierre-Yves Chibon c96124
            try:
Pierre-Yves Chibon c96124
                branch = get_branch_ref(fork_obj, request.branch_from)
Pierre-Yves Chibon c96124
            except pagure.exceptions.PagureException:
Pierre-Yves Chibon c96124
                pass
Pierre-Yves Chibon c96124
            if not branch:
Pierre-Yves Chibon c96124
                _log.info("  Branch of origin could not be found")
Pierre-Yves Chibon c96124
                raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon c96124
                    "Branch %s could not be found in the repo %s"
Pierre-Yves Chibon c96124
                    % (
Pierre-Yves Chibon c96124
                        request.branch_from,
Pierre-Yves Chibon c96124
                        request.project_from.fullname
Pierre-Yves Chibon c96124
                        if request.project_from
Pierre-Yves Chibon c96124
                        else request.remote_git,
Pierre-Yves Chibon c96124
                    )
Patrick Uiterwijk 3f97f6
                )
Pierre-Yves Chibon c96124
Pierre-Yves Chibon c96124
            # Add the fork as remote repo
Pierre-Yves Chibon c96124
            reponame = "%s_%s" % (request.user.user, request.uid)
Pierre-Yves Chibon c96124
Pierre-Yves Chibon c96124
            _log.info(
Pierre-Yves Chibon c96124
                "  Adding remote: %s pointing to: %s", reponame, repopath
Patrick Uiterwijk 3f97f6
            )
Pierre-Yves Chibon c96124
            remote = new_repo.create_remote(reponame, repopath)
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon c96124
            # Fetch the commits
Pierre-Yves Chibon c96124
            remote.fetch()
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon 29ff0a
            # repo_commit = fork_obj[branch.peel().hex]
Pierre-Yves Chibon 29ff0a
            repo_commit = new_repo[branch.peel().hex]
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
            # Checkout the correct branch
Pierre-Yves Chibon c96124
            if new_repo.is_empty or new_repo.head_is_unborn:
Pierre-Yves Chibon c96124
                _log.debug(
Pierre-Yves Chibon c96124
                    "  target repo is empty, so PR can be merged using "
Pierre-Yves Chibon c96124
                    "fast-forward, reporting it"
Pierre-Yves Chibon c96124
                )
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
                if domerge:
Pierre-Yves Chibon c96124
                    _log.info("  PR merged using fast-forward")
Pierre-Yves Chibon c96124
                    if not request.project.settings.get("always_merge", False):
Pierre-Yves Chibon c96124
                        new_repo.create_branch(request.branch, repo_commit)
Pierre-Yves Chibon c96124
                        commit = repo_commit.oid.hex
Pierre-Yves Chibon c96124
                    else:
Pierre-Yves Chibon c96124
                        tree = new_repo.index.write_tree()
Pierre-Yves Chibon c96124
                        user_obj = pagure.lib.query.get_user(session, username)
Pierre-Yves Chibon c96124
                        commitname = user_obj.fullname or user_obj.user
Pierre-Yves Chibon c96124
                        author = _make_signature(
Pierre-Yves Chibon c96124
                            commitname, user_obj.default_email
Pierre-Yves Chibon c96124
                        )
Pierre-Yves Chibon c96124
                        commit = new_repo.create_commit(
Pierre-Yves Chibon c96124
                            "refs/heads/%s" % request.branch,
Pierre-Yves Chibon c96124
                            author,
Pierre-Yves Chibon c96124
                            author,
Pierre-Yves Chibon c96124
                            "Merge #%s `%s`" % (request.id, request.title),
Pierre-Yves Chibon c96124
                            tree,
Pierre-Yves Chibon c96124
                            [repo_commit.oid.hex],
Pierre-Yves Chibon c96124
                        )
Patrick Uiterwijk 3f97f6
Pierre-Yves Chibon c96124
                    _log.info("  New head: %s", commit)
Pierre-Yves Chibon c96124
                    tempclone.push(
Pierre-Yves Chibon c96124
                        username,
Pierre-Yves Chibon c96124
                        request.branch,
Pierre-Yves Chibon c96124
                        request.branch,
Pierre-Yves Chibon c96124
                        pull_request=request,
Patrick Uiterwijk 3f97f6
                    )
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
                    # Update status
Pierre-Yves Chibon c96124
                    _log.info("  Closing the PR in the DB")
Pierre-Yves Chibon c96124
                    pagure.lib.query.close_pull_request(
Pierre-Yves Chibon c96124
                        session, request, username
Pierre-Yves Chibon c96124
                    )
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
                    return "Changes merged!"
Pierre-Yves Chibon c96124
                else:
Pierre-Yves Chibon c96124
                    _log.info(
Pierre-Yves Chibon c96124
                        "  PR can be merged using fast-forward, reporting it"
Pierre-Yves Chibon c96124
                    )
Pierre-Yves Chibon c96124
                    request.merge_status = "FFORWARD"
Pierre-Yves Chibon c96124
                    session.commit()
Pierre-Yves Chibon c96124
                    return "FFORWARD"
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
        else:
Pierre-Yves Chibon c96124
            try:
Pierre-Yves Chibon c96124
                ref = new_repo.lookup_reference(
Pierre-Yves Chibon c96124
                    "refs/pull/%s/head" % request.id
Pierre-Yves Chibon 97d19d
                )
Pierre-Yves Chibon c96124
                repo_commit = new_repo[ref.target.hex]
Pierre-Yves Chibon c96124
            except KeyError:
Pierre-Yves Chibon c96124
                pass
Pierre-Yves Chibon 99d493
Patrick Uiterwijk 3f97f6
        merge = new_repo.merge(repo_commit.oid)
Patrick Uiterwijk 3f97f6
        _log.debug("  Merge: %s", merge)
Patrick Uiterwijk 3f97f6
        if merge is None:
Patrick Uiterwijk 3f97f6
            mergecode = new_repo.merge_analysis(repo_commit.oid)[0]
Patrick Uiterwijk 3f97f6
            _log.debug("  Mergecode: %s", mergecode)
Pierre-Yves Chibon a19f20
Patrick Uiterwijk 3f97f6
        # Wait until the last minute then check if the PR was already closed
Patrick Uiterwijk 3f97f6
        # by someone else in the mean while and if so, just bail
Patrick Uiterwijk 3f97f6
        if request.status != "Open":
Patrick Uiterwijk 3f97f6
            _log.info(
Patrick Uiterwijk 3f97f6
                "  This pull-request has already been merged or closed by %s "
Patrick Uiterwijk 3f97f6
                "on %s" % (request.closed_by.user, request.closed_at)
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 0aaeb1
            raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                "This pull-request was merged or closed by %s"
Patrick Uiterwijk 3f97f6
                % request.closed_by.user
Pierre-Yves Chibon 9c2953
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        if (merge is not None and merge.is_uptodate) or (  # noqa
Patrick Uiterwijk 3f97f6
            merge is None and mergecode & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE
Patrick Uiterwijk 3f97f6
        ):
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            if domerge:
Patrick Uiterwijk 3f97f6
                _log.info("  PR up to date, closing it")
Pierre-Yves Chibon 930073
                pagure.lib.query.close_pull_request(session, request, username)
Patrick Uiterwijk 3f97f6
                try:
Patrick Uiterwijk 3f97f6
                    session.commit()
Karsten Hopp 3b333e
                except SQLAlchemyError:  # pragma: no cover
Patrick Uiterwijk 3f97f6
                    session.rollback()
Patrick Uiterwijk 3f97f6
                    _log.exception("  Could not merge the PR in the DB")
Patrick Uiterwijk 3f97f6
                    raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                        "Could not close this pull-request"
Patrick Uiterwijk 3f97f6
                    )
Patrick Uiterwijk 3f97f6
                raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                    "Nothing to do, changes were already merged"
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
            else:
Patrick Uiterwijk 3f97f6
                _log.info("  PR up to date, reporting it")
Patrick Uiterwijk 3f97f6
                request.merge_status = "NO_CHANGE"
Patrick Uiterwijk 3f97f6
                session.commit()
Patrick Uiterwijk 3f97f6
                return "NO_CHANGE"
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        elif (merge is not None and merge.is_fastforward) or (  # noqa
Patrick Uiterwijk 3f97f6
            merge is None and mergecode & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD
Patrick Uiterwijk 3f97f6
        ):
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            if domerge:
Patrick Uiterwijk 3f97f6
                _log.info("  PR merged using fast-forward")
Pierre-Yves Chibon 29ff0a
                head = new_repo.lookup_reference("HEAD").peel()
Patrick Uiterwijk 3f97f6
                if not request.project.settings.get("always_merge", False):
Patrick Uiterwijk 3f97f6
                    if merge is not None:
Patrick Uiterwijk 3f97f6
                        # This is depending on the pygit2 version
Patrick Uiterwijk 3f97f6
                        branch_ref.target = merge.fastforward_oid
Patrick Uiterwijk 3f97f6
                    elif merge is None and mergecode is not None:
Patrick Uiterwijk 3f97f6
                        branch_ref.set_target(repo_commit.oid.hex)
Patrick Uiterwijk 3f97f6
                    commit = repo_commit.oid.hex
Patrick Uiterwijk 3f97f6
                else:
Patrick Uiterwijk 3f97f6
                    tree = new_repo.index.write_tree()
Pierre-Yves Chibon 930073
                    user_obj = pagure.lib.query.get_user(session, username)
Patrick Uiterwijk 3f97f6
                    commitname = user_obj.fullname or user_obj.user
Patrick Uiterwijk 3f97f6
                    author = _make_signature(
Patrick Uiterwijk 3f97f6
                        commitname, user_obj.default_email
Patrick Uiterwijk 3f97f6
                    )
Pierre-Yves Chibon 707873
Pierre-Yves Chibon 707873
                    commit_message = "Merge #%s `%s`" % (
Pierre-Yves Chibon 707873
                        request.id,
Pierre-Yves Chibon 707873
                        request.title,
Pierre-Yves Chibon 707873
                    )
Pierre-Yves Chibon 707873
                    if request.project.settings.get(
Pierre-Yves Chibon 707873
                        "Enforce_signed-off_commits_in_pull-request", False
Pierre-Yves Chibon 707873
                    ):
Pierre-Yves Chibon 7bf987
                        commit_message += "\n\nSigned-off-by: %s <%s>" % (
Pierre-Yves Chibon 707873
                            commitname,
Pierre-Yves Chibon 707873
                            user_obj.default_email,
Pierre-Yves Chibon 707873
                        )
Pierre-Yves Chibon 707873
Patrick Uiterwijk 3f97f6
                    commit = new_repo.create_commit(
Patrick Uiterwijk 3f97f6
                        "refs/heads/%s" % request.branch,
Patrick Uiterwijk 3f97f6
                        author,
Patrick Uiterwijk 3f97f6
                        author,
Pierre-Yves Chibon 707873
                        commit_message,
Patrick Uiterwijk 3f97f6
                        tree,
Patrick Uiterwijk 3f97f6
                        [head.hex, repo_commit.oid.hex],
Patrick Uiterwijk 3f97f6
                    )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
                _log.info("  New head: %s", commit)
Patrick Uiterwijk 03a519
                tempclone.push(
Patrick Uiterwijk 03a519
                    username,
Patrick Uiterwijk 03a519
                    branch_ref.name,
Patrick Uiterwijk 03a519
                    request.branch,
Patrick Uiterwijk 03a519
                    pull_request=request,
Patrick Uiterwijk 03a519
                )
Pierre-Yves Chibon ccc85e
            else:
Pierre-Yves Chibon 97d19d
                _log.info(
Pierre-Yves Chibon 97d19d
                    "  PR can be merged using fast-forward, reporting it"
Pierre-Yves Chibon 97d19d
                )
Patrick Uiterwijk 3f97f6
                request.merge_status = "FFORWARD"
Patrick Uiterwijk 3f97f6
                session.commit()
Patrick Uiterwijk 3f97f6
                return "FFORWARD"
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            tree = None
Patrick Uiterwijk 3f97f6
            try:
Pierre-Yves Chibon ccc85e
                tree = new_repo.index.write_tree()
Patrick Uiterwijk 3f97f6
            except pygit2.GitError as err:
Patrick Uiterwijk 3f97f6
                _log.debug(
Patrick Uiterwijk 3f97f6
                    "  Could not write down the new tree: " "merge conflicts"
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
                _log.debug(err)
Patrick Uiterwijk 3f97f6
                if domerge:
Patrick Uiterwijk 3f97f6
                    _log.info("  Merge conflict: Bailing")
Patrick Uiterwijk 3f97f6
                    raise pagure.exceptions.PagureException("Merge conflicts!")
Patrick Uiterwijk 3f97f6
                else:
Patrick Uiterwijk 3f97f6
                    _log.info("  Merge conflict, reporting it")
Patrick Uiterwijk 3f97f6
                    request.merge_status = "CONFLICTS"
Patrick Uiterwijk 3f97f6
                    session.commit()
Patrick Uiterwijk 3f97f6
                    return "CONFLICTS"
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            if domerge:
Slavek Kabrda cdb053
                if request.project.settings.get(
Slavek Kabrda cdb053
                    "disable_non_fast-forward_merges", False
Slavek Kabrda cdb053
                ):
Slavek Kabrda cdb053
                    _log.info("  Merge non-FF PR is disabled for this project")
Slavek Kabrda cdb053
                    return "MERGE"
Patrick Uiterwijk 3f97f6
                _log.info("  Writing down merge commit")
Pierre-Yves Chibon 29ff0a
                head = new_repo.lookup_reference("HEAD").peel()
Patrick Uiterwijk 3f97f6
                _log.info(
Patrick Uiterwijk 3f97f6
                    "  Basing on: %s - %s", head.hex, repo_commit.oid.hex
Patrick Uiterwijk 3f97f6
                )
Pierre-Yves Chibon 930073
                user_obj = pagure.lib.query.get_user(session, username)
Pierre-Yves Chibon 157c26
                commitname = user_obj.fullname or user_obj.user
Pierre-Yves Chibon 9c2953
                author = _make_signature(commitname, user_obj.default_email)
Pierre-Yves Chibon 707873
Pierre-Yves Chibon 707873
                commit_message = "Merge #%s `%s`" % (request.id, request.title)
Pierre-Yves Chibon 707873
                if request.project.settings.get(
Pierre-Yves Chibon 707873
                    "Enforce_signed-off_commits_in_pull-request", False
Pierre-Yves Chibon 707873
                ):
Pierre-Yves Chibon 7bf987
                    commit_message += "\n\nSigned-off-by: %s <%s>" % (
Pierre-Yves Chibon 707873
                        commitname,
Pierre-Yves Chibon 707873
                        user_obj.default_email,
Pierre-Yves Chibon 707873
                    )
Pierre-Yves Chibon 707873
Pierre-Yves Chibon 5a8c6a
                commit = new_repo.create_commit(
Pierre-Yves Chibon 9c2953
                    "refs/heads/%s" % request.branch,
Pierre-Yves Chibon ccc85e
                    author,
Pierre-Yves Chibon ccc85e
                    author,
Pierre-Yves Chibon 707873
                    commit_message,
Pierre-Yves Chibon ccc85e
                    tree,
Pierre-Yves Chibon 9c2953
                    [head.hex, repo_commit.oid.hex],
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon a19f20
Patrick Uiterwijk 3f97f6
                _log.info("  New head: %s", commit)
Patrick Uiterwijk 3f97f6
                local_ref = "refs/heads/_pagure_topush"
Patrick Uiterwijk 3f97f6
                new_repo.create_reference(local_ref, commit)
Patrick Uiterwijk 03a519
                tempclone.push(
Patrick Uiterwijk 03a519
                    username, local_ref, request.branch, pull_request=request
Patrick Uiterwijk 3f97f6
                )
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon a19f20
            else:
Patrick Uiterwijk 3f97f6
                _log.info(
Patrick Uiterwijk 3f97f6
                    "  PR can be merged with a merge commit, " "reporting it"
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
                request.merge_status = "MERGE"
Pierre-Yves Chibon a19f20
                session.commit()
Patrick Uiterwijk 3f97f6
                return "MERGE"
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon 0aaeb1
    # Update status
Pierre-Yves Chibon 9c2953
    _log.info("  Closing the PR in the DB")
Pierre-Yves Chibon 930073
    pagure.lib.query.close_pull_request(session, request, username)
Pierre-Yves Chibon 0aaeb1
Pierre-Yves Chibon 9c2953
    return "Changes merged!"
Pierre-Yves Chibon dbf3e1
Pierre-Yves Chibon dbf3e1
Pierre-Yves Chibon 011b16
def rebase_pull_request(session, request, username):
Pierre-Yves Chibon 9b2a96
    """ Rebase the specified pull-request.
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    Args:
Pierre-Yves Chibon 9b2a96
        session (sqlalchemy): the session to connect to the database with
Pierre-Yves Chibon 9b2a96
        request (pagure.lib.model.PullRequest): the database object
Pierre-Yves Chibon 9b2a96
            corresponding to the pull-request to rebase
Pierre-Yves Chibon 9b2a96
        username (string): the name of the user asking for the pull-request
Pierre-Yves Chibon 9b2a96
            to be rebased
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    Returns: (string or None): Pull-request rebased
Pierre-Yves Chibon 9b2a96
    Raises: pagure.exceptions.PagureException
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    """
Pierre-Yves Chibon 0b785c
    _log.info("%s asked to rebase the pull-request: %s", username, request)
Pierre-Yves Chibon 011b16
    user = pagure.lib.query.get_user(session, username)
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    if request.remote:
Pierre-Yves Chibon 9b2a96
        # Get the fork
Pierre-Yves Chibon 9b2a96
        repopath = pagure.utils.get_remote_repo_path(
Pierre-Yves Chibon 9b2a96
            request.remote_git, request.branch_from
Pierre-Yves Chibon 9b2a96
        )
Pierre-Yves Chibon 9b2a96
    elif request.project_from:
Pierre-Yves Chibon 9b2a96
        # Get the fork
Pierre-Yves Chibon 9b2a96
        repopath = pagure.utils.get_repo_path(request.project_from)
Pierre-Yves Chibon 9b2a96
    else:
Pierre-Yves Chibon d91c53
        _log.info(
Pierre-Yves Chibon d91c53
            "PR is neither from a remote git repo or an existing local "
Pierre-Yves Chibon accad9
            "repo, bailing"
Pierre-Yves Chibon accad9
        )
Pierre-Yves Chibon 9b2a96
        return
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    if not request.project or not os.path.exists(
Pierre-Yves Chibon 9b2a96
        pagure.utils.get_repo_path(request.project)
Pierre-Yves Chibon 9b2a96
    ):
Pierre-Yves Chibon d91c53
        _log.info(
Pierre-Yves Chibon d91c53
            "Could not find the targeted git repository for %s",
Pierre-Yves Chibon accad9
            request.project.fullname,
Pierre-Yves Chibon accad9
        )
Pierre-Yves Chibon 9b2a96
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9b2a96
            "Could not find the targeted git repository for %s"
Pierre-Yves Chibon 9b2a96
            % request.project.fullname
Pierre-Yves Chibon 9b2a96
        )
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    with TemporaryClone(
Pierre-Yves Chibon 9b2a96
        project=request.project,
Pierre-Yves Chibon 9b2a96
        repotype="main",
Pierre-Yves Chibon 9b2a96
        action="rebase_pr",
Pierre-Yves Chibon 9b2a96
        path=repopath,
Pierre-Yves Chibon 9b2a96
    ) as tempclone:
Pierre-Yves Chibon 9b2a96
        new_repo = tempclone.repo
Pierre-Yves Chibon 9b2a96
        new_repo.checkout("refs/heads/%s" % request.branch_from)
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Add the upstream repo as remote
Pierre-Yves Chibon 9b2a96
        upstream = "%s_%s" % (request.user.user, request.uid)
Pierre-Yves Chibon 9b2a96
        upstream_path = pagure.utils.get_repo_path(request.project)
Pierre-Yves Chibon 9b2a96
        _log.info(
Pierre-Yves Chibon 9b2a96
            "  Adding remote: %s pointing to: %s", upstream, upstream_path
Pierre-Yves Chibon 9b2a96
        )
Pierre-Yves Chibon 9b2a96
        remote = new_repo.create_remote(upstream, upstream_path)
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Fetch the commits
Pierre-Yves Chibon 9b2a96
        remote.fetch()
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        def _run_command(command):
Pierre-Yves Chibon d91c53
            _log.info("Running command: %s", command)
Pierre-Yves Chibon 9b2a96
            try:
Pierre-Yves Chibon 9b2a96
                out = subprocess.check_output(
Pierre-Yves Chibon 9b2a96
                    command, cwd=tempclone.repopath, stderr=subprocess.STDOUT
Pierre-Yves Chibon 9b2a96
                )
Pierre-Yves Chibon d91c53
                _log.info("   command ran successfully")
Pierre-Yves Chibon 9b2a96
                _log.debug("Output: %s" % out)
Pierre-Yves Chibon 9b2a96
            except subprocess.CalledProcessError as err:
Pierre-Yves Chibon 9b2a96
                _log.debug(
Pierre-Yves Chibon 9b2a96
                    "Rebase FAILED: {cmd} returned code {code} with the "
Pierre-Yves Chibon 9b2a96
                    "following output: {output}".format(
Pierre-Yves Chibon 9b2a96
                        cmd=err.cmd, code=err.returncode, output=err.output
Pierre-Yves Chibon 9b2a96
                    )
Pierre-Yves Chibon 9b2a96
                )
Pierre-Yves Chibon 9b2a96
                raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9b2a96
                    "Did not manage to rebase this pull-request"
Pierre-Yves Chibon 9b2a96
                )
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Configure git for that user
Pierre-Yves Chibon 9b2a96
        command = ["git", "config", "user.name", username]
Pierre-Yves Chibon 9b2a96
        _run_command(command)
Pierre-Yves Chibon 011b16
        command = ["git", "config", "user.email", user.default_email]
Pierre-Yves Chibon 9b2a96
        _run_command(command)
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Do the rebase
Pierre-Yves Chibon 9b2a96
        command = ["git", "pull", "--rebase", upstream, request.branch]
Pierre-Yves Chibon 9b2a96
        _run_command(command)
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Retrieve the reference of the branch we're working on
Pierre-Yves Chibon 9b2a96
        try:
Pierre-Yves Chibon 9b2a96
            branch_ref = get_branch_ref(new_repo, request.branch_from)
Pierre-Yves Chibon 9b2a96
        except pagure.exceptions.PagureException:
Pierre-Yves Chibon 9b2a96
            branch_ref = None
Pierre-Yves Chibon 9b2a96
        if not branch_ref:
Pierre-Yves Chibon 9b2a96
            _log.debug("  Target branch could not be found")
Pierre-Yves Chibon 9b2a96
            raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon 9b2a96
                "Branch %s could not be found in the repo %s"
Pierre-Yves Chibon 9b2a96
                % (request.branch, request.project.fullname)
Pierre-Yves Chibon 9b2a96
            )
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
        # Push the changes
Pierre-Yves Chibon 9b2a96
        _log.info("Pushing %s to %s", branch_ref.name, request.branch_from)
Pierre-Yves Chibon 9b2a96
        try:
Pierre-Yves Chibon e180e7
            if request.allow_rebase:
Pierre-Yves Chibon e180e7
                tempclone.push(
Pierre-Yves Chibon e180e7
                    username,
Pierre-Yves Chibon e180e7
                    branch_ref.name,
Pierre-Yves Chibon e180e7
                    request.branch_from,
Pierre-Yves Chibon e180e7
                    pull_request=request,
Pierre-Yves Chibon e180e7
                    force=True,
Pierre-Yves Chibon e180e7
                    internal="yes",
Pierre-Yves Chibon e180e7
                )
Pierre-Yves Chibon e180e7
            else:
Pierre-Yves Chibon e180e7
                tempclone.push(
Pierre-Yves Chibon e180e7
                    username,
Pierre-Yves Chibon e180e7
                    branch_ref.name,
Pierre-Yves Chibon e180e7
                    request.branch_from,
Pierre-Yves Chibon e180e7
                    pull_request=request,
Pierre-Yves Chibon e180e7
                    force=True,
Pierre-Yves Chibon e180e7
                )
Pierre-Yves Chibon 9b2a96
        except subprocess.CalledProcessError as err:
Pierre-Yves Chibon 9b2a96
            _log.debug(
Pierre-Yves Chibon 9b2a96
                "Rebase FAILED: {cmd} returned code {code} with the "
Pierre-Yves Chibon 9b2a96
                "following output: {output}".format(
Pierre-Yves Chibon 9b2a96
                    cmd=err.cmd, code=err.returncode, output=err.output
Pierre-Yves Chibon 9b2a96
                )
Pierre-Yves Chibon 9b2a96
            )
Pierre-Yves Chibon 9b2a96
            raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9b2a96
                "Did not manage to rebase this pull-request"
Pierre-Yves Chibon 9b2a96
            )
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
    return "Pull-request rebased"
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon 9b2a96
Pierre-Yves Chibon ff9832
def get_diff_info(repo_obj, orig_repo, branch_from, branch_to, prid=None):
Pierre-Yves Chibon 9c2953
    """ Return the info needed to see a diff or make a Pull-Request between
Pierre-Yves Chibon 875a08
    the two specified repo.
Pierre-Yves Chibon dbf3e1
Pierre-Yves Chibon 875a08
    :arg repo_obj: The pygit2.Repository object of the first git repo
Pierre-Yves Chibon 875a08
    :arg orig_repo:  The pygit2.Repository object of the second git repo
Pierre-Yves Chibon 875a08
    :arg branch_from: the name of the branch having the changes, in the
Pierre-Yves Chibon 875a08
        first git repo
Pierre-Yves Chibon 41dbac
    :arg branch_to: the name of the branch in which we want to merge the
Pierre-Yves Chibon 875a08
        changes in the second git repo
Pierre-Yves Chibon ff9832
    :kwarg prid: the identifier of the pull-request to
Pierre-Yves Chibon dbf3e1
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon e3d601
    try:
Pierre-Yves Chibon e3d601
        frombranch = repo_obj.lookup_branch(branch_from)
Pierre-Yves Chibon e3d601
    except ValueError:
Pierre-Yves Chibon e3d601
        raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon 9c2953
            "Branch %s does not exist" % branch_from
Pierre-Yves Chibon e3d601
        )
Pierre-Yves Chibon c96124
    except AttributeError:
Pierre-Yves Chibon c96124
        frombranch = None
Pierre-Yves Chibon c96124
    if not frombranch and prid is None and repo_obj and not repo_obj.is_empty:
Pierre-Yves Chibon 875a08
        raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon 9c2953
            "Branch %s does not exist" % branch_from
Stanislav Laznicka 14c8c3
        )
Pierre-Yves Chibon 0bc5ac
Pierre-Yves Chibon 875a08
    branch = None
Pierre-Yves Chibon 875a08
    if branch_to:
Pierre-Yves Chibon e3d601
        try:
Pierre-Yves Chibon e3d601
            branch = orig_repo.lookup_branch(branch_to)
Pierre-Yves Chibon e3d601
        except ValueError:
Pierre-Yves Chibon e3d601
            raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon 9c2953
                "Branch %s does not exist" % branch_to
Pierre-Yves Chibon e3d601
            )
Pierre-Yves Chibon 189430
        local_branches = orig_repo.listall_branches(pygit2.GIT_BRANCH_LOCAL)
Pierre-Yves Chibon 189430
        if not branch and local_branches:
Pierre-Yves Chibon 875a08
            raise pagure.exceptions.BranchNotFoundException(
Pierre-Yves Chibon 9c2953
                "Branch %s could not be found in the target repo" % branch_to
Pierre-Yves Chibon 875a08
            )
Pierre-Yves Chibon 5e3931
Pierre-Yves Chibon 875a08
    commitid = None
Pierre-Yves Chibon 875a08
    if frombranch:
Pierre-Yves Chibon 29ff0a
        commitid = frombranch.peel().hex
Pierre-Yves Chibon ff9832
    elif prid is not None:
Pierre-Yves Chibon ff9832
        # If there is not branch found but there is a PR open, use the ref
Pierre-Yves Chibon ff9832
        # of that PR in the main repo
Pierre-Yves Chibon ff9832
        try:
Pierre-Yves Chibon ff9832
            ref = orig_repo.lookup_reference("refs/pull/%s/head" % prid)
Pierre-Yves Chibon ff9832
            commitid = ref.target.hex
Pierre-Yves Chibon 0f7101
        except KeyError:
Pierre-Yves Chibon ff9832
            pass
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon c96124
    if not commitid and repo_obj and not repo_obj.is_empty:
Pierre-Yves Chibon 8ffbe4
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9c2953
            "No branch from which to pull or local PR reference were found"
Pierre-Yves Chibon 8ffbe4
        )
Pierre-Yves Chibon 8ffbe4
Pierre-Yves Chibon 875a08
    diff_commits = []
Pierre-Yves Chibon 875a08
    diff = None
Pierre-Yves Chibon 875a08
    orig_commit = None
Pierre-Yves Chibon ff9832
Pierre-Yves Chibon ff9832
    # If the fork is empty but there is a PR open, use the main repo
Pierre-Yves Chibon c96124
    if (not repo_obj or repo_obj.is_empty) and prid is not None:
Pierre-Yves Chibon ff9832
        repo_obj = orig_repo
Pierre-Yves Chibon ff9832
Pierre-Yves Chibon dbf3e1
    if not repo_obj.is_empty and not orig_repo.is_empty:
Pierre-Yves Chibon 6bd26b
        _log.info(
Pierre-Yves Chibon 6bd26b
            "pagure.lib.git.get_diff_info: Pulling into a non-empty repo"
Pierre-Yves Chibon 6bd26b
        )
Pierre-Yves Chibon 875a08
        if branch:
Pierre-Yves Chibon 29ff0a
            orig_commit = orig_repo[branch.peel().hex]
Pierre-Yves Chibon 875a08
            main_walker = orig_repo.walk(
Slavek Kabrda 0dd0cd
                orig_commit.oid.hex, pygit2.GIT_SORT_NONE
Pierre-Yves Chibon 9c2953
            )
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 875a08
        repo_commit = repo_obj[commitid]
Pierre-Yves Chibon 875a08
        branch_walker = repo_obj.walk(
Slavek Kabrda 0dd0cd
            repo_commit.oid.hex, pygit2.GIT_SORT_NONE
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 38062e
Pierre-Yves Chibon 38062e
        main_commits = set()
Pierre-Yves Chibon 38062e
        branch_commits = set()
Pierre-Yves Chibon 38062e
Pierre-Yves Chibon 38062e
        while 1:
Pierre-Yves Chibon 875a08
            com = None
Pierre-Yves Chibon 875a08
            if branch:
Pierre-Yves Chibon 875a08
                try:
Aurélien Bompard 831553
                    com = next(main_walker)
Pierre-Yves Chibon 875a08
                    main_commits.add(com.oid.hex)
Pierre-Yves Chibon 875a08
                except StopIteration:
Pierre-Yves Chibon 875a08
                    com = None
Pierre-Yves Chibon de491b
Pierre-Yves Chibon 38062e
            try:
Aurélien Bompard 831553
                branch_commit = next(branch_walker)
Pierre-Yves Chibon 38062e
            except StopIteration:
Pierre-Yves Chibon 38062e
                branch_commit = None
Pierre-Yves Chibon 38062e
Pierre-Yves Chibon de491b
            # We sure never end up here but better safe than sorry
Pierre-Yves Chibon de491b
            if com is None and branch_commit is None:
Pierre-Yves Chibon de491b
                break
Pierre-Yves Chibon de491b
Pierre-Yves Chibon de491b
            if branch_commit:
Pierre-Yves Chibon de491b
                branch_commits.add(branch_commit.oid.hex)
Pierre-Yves Chibon fbb684
                diff_commits.append(branch_commit)
Pierre-Yves Chibon 38062e
            if main_commits.intersection(branch_commits):
Pierre-Yves Chibon dbf3e1
                break
Pierre-Yves Chibon 38062e
Pierre-Yves Chibon f556c2
        # If master is ahead of branch, we need to remove the commits
Pierre-Yves Chibon fbb684
        # that are after the first one found in master
Pierre-Yves Chibon fbb684
        i = 0
Pierre-Yves Chibon 875a08
        if diff_commits and main_commits:
Pierre-Yves Chibon 875a08
            for i in range(len(diff_commits)):
Pierre-Yves Chibon 875a08
                if diff_commits[i].oid.hex in main_commits:
Pierre-Yves Chibon 875a08
                    break
Pierre-Yves Chibon 875a08
            diff_commits = diff_commits[:i]
Pierre-Yves Chibon f556c2
Slavek Kabrda 94599b
        _log.debug("Diff commits: %s", diff_commits)
Pierre-Yves Chibon 875a08
        if diff_commits:
Pierre-Yves Chibon d9d77c
            first_commit = repo_obj[diff_commits[-1].oid.hex]
Pierre-Yves Chibon 875a08
            if len(first_commit.parents) > 0:
Pierre-Yves Chibon 875a08
                diff = repo_obj.diff(
Pierre-Yves Chibon 875a08
                    repo_obj.revparse_single(first_commit.parents[0].oid.hex),
Pierre-Yves Chibon 9c2953
                    repo_obj.revparse_single(diff_commits[0].oid.hex),
Pierre-Yves Chibon fa1dee
                )
Pierre-Yves Chibon 59f36c
            elif first_commit.oid.hex == diff_commits[0].oid.hex:
Pierre-Yves Chibon 83bb93
                _log.info(
Pierre-Yves Chibon 6bd26b
                    "pagure.lib.git.get_diff_info: First commit is also the "
Pierre-Yves Chibon 6bd26b
                    "last commit"
Pierre-Yves Chibon 9c2953
                )
Pierre-Yves Chibon 59f36c
                diff = diff_commits[0].tree.diff_to_tree(swap=True)
Pierre-Yves Chibon 59f36c
Pierre-Yves Chibon c96124
    elif orig_repo.is_empty and repo_obj and not repo_obj.is_empty:
Pierre-Yves Chibon 6bd26b
        _log.info("pagure.lib.git.get_diff_info: Pulling into an empty repo")
Pierre-Yves Chibon 9c2953
        if "master" in repo_obj.listall_branches():
Pierre-Yves Chibon 875a08
            repo_commit = repo_obj[repo_obj.head.target]
Pierre-Yves Chibon 875a08
        else:
Pierre-Yves Chibon 875a08
            branch = repo_obj.lookup_branch(branch_from)
Pierre-Yves Chibon 29ff0a
            repo_commit = branch.peel()
Pierre-Yves Chibon 875a08
Slavek Kabrda 0dd0cd
        for commit in repo_obj.walk(repo_commit.oid.hex, pygit2.GIT_SORT_NONE):
Pierre-Yves Chibon dbf3e1
            diff_commits.append(commit)
Pierre-Yves Chibon dbf3e1
Slavek Kabrda 94599b
        _log.debug("Diff commits: %s", diff_commits)
Pierre-Yves Chibon 875a08
        diff = repo_commit.tree.diff_to_tree(swap=True)
Pierre-Yves Chibon 875a08
    else:
Pierre-Yves Chibon 875a08
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon 9c2953
            "Fork is empty, there are no commits to create a pull "
Pierre-Yves Chibon 9c2953
            "request with"
Pierre-Yves Chibon 875a08
        )
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 83bb93
    _log.info(
Pierre-Yves Chibon 6bd26b
        "pagure.lib.git.get_diff_info: diff_commits length: %s",
Pierre-Yves Chibon 6bd26b
        len(diff_commits),
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon 6bd26b
    _log.info("pagure.lib.git.get_diff_info: original commit: %s", orig_commit)
Pierre-Yves Chibon 83bb93
Pierre-Yves Chibon 9c2953
    return (diff, diff_commits, orig_commit)
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 35fe76
def diff_pull_request(
Pierre-Yves Chibon b983f9
    session,
Pierre-Yves Chibon b983f9
    request,
Pierre-Yves Chibon b983f9
    repo_obj,
Pierre-Yves Chibon b983f9
    orig_repo,
Pierre-Yves Chibon b983f9
    with_diff=True,
Pierre-Yves Chibon b983f9
    notify=True,
Pierre-Yves Chibon b983f9
    username=None,
Pierre-Yves Chibon 433e5e
):
Pierre-Yves Chibon 875a08
    """ Returns the diff and the list of commits between the two git repos
Pierre-Yves Chibon 875a08
    mentionned in the given pull-request.
Pierre-Yves Chibon dbf3e1
Pierre-Yves Chibon 875a08
    :arg session: The sqlalchemy session to connect to the database
Pierre-Yves Chibon 875a08
    :arg request: The pagure.lib.model.PullRequest object of the pull-request
Pierre-Yves Chibon 875a08
        to look into
Pierre-Yves Chibon 875a08
    :arg repo_obj: The pygit2.Repository object of the first git repo
Pierre-Yves Chibon 875a08
    :arg orig_repo:  The pygit2.Repository object of the second git repo
Pierre-Yves Chibon 875a08
    :arg with_diff: A boolean on whether to return the diff with the list
Pierre-Yves Chibon 875a08
        of commits (or just the list of commits)
Pierre-Yves Chibon b983f9
    :arg username: The username of the user diffing the pull-request
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 875a08
    """
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon b983f9
    _log.debug("pagure.lib.git.diff_pull_request, started")
Pierre-Yves Chibon 875a08
    diff = None
Pierre-Yves Chibon 875a08
    diff_commits = []
Pierre-Yves Chibon 875a08
    diff, diff_commits, _ = get_diff_info(
Pierre-Yves Chibon 9c2953
        repo_obj,
Pierre-Yves Chibon 9c2953
        orig_repo,
Pierre-Yves Chibon 9c2953
        request.branch_from,
Pierre-Yves Chibon 9c2953
        request.branch,
Pierre-Yves Chibon 9c2953
        prid=request.id,
Pierre-Yves Chibon 9c2953
    )
Pierre-Yves Chibon b983f9
    _log.debug("pagure.lib.git.diff_pull_request, diff done")
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 9c2953
    if request.status == "Open" and diff_commits:
Pierre-Yves Chibon b983f9
        _log.debug("pagure.lib.git.diff_pull_request, PR open and with a diff")
Pierre-Yves Chibon c96124
        first_commit = diff_commits[-1]
Pierre-Yves Chibon 875a08
        # Check if we can still rely on the merge_status
Pierre-Yves Chibon 875a08
        commenttext = None
Pierre-Yves Chibon 9c2953
        if (
Pierre-Yves Chibon 9c2953
            request.commit_start != first_commit.oid.hex
Pierre-Yves Chibon 9c2953
            or request.commit_stop != diff_commits[0].oid.hex
Pierre-Yves Chibon 9c2953
        ):
Pierre-Yves Chibon 875a08
            request.merge_status = None
Pierre-Yves Chibon 875a08
            if request.commit_start:
Pierre-Yves Chibon f39c35
                pr_action = "updated"
Pierre-Yves Chibon 875a08
                new_commits_count = 0
Pierre-Yves Chibon 875a08
                commenttext = ""
Pierre-Yves Chibon 875a08
                for i in diff_commits:
Pierre-Yves Chibon 875a08
                    if i.oid.hex == request.commit_stop:
Pierre-Yves Chibon 875a08
                        break
Pierre-Yves Chibon 875a08
                    new_commits_count = new_commits_count + 1
Pierre-Yves Chibon 9c2953
                    commenttext = "%s * ``%s``\n" % (
Pierre-Yves Chibon 9c2953
                        commenttext,
Pierre-Yves Chibon 9c2953
                        i.message.strip().split("\n")[0],
Pierre-Yves Chibon 9c2953
                    )
Pierre-Yves Chibon 875a08
                if new_commits_count == 1:
Pierre-Yves Chibon 875a08
                    commenttext = "**%d new commit added**\n\n%s" % (
Pierre-Yves Chibon 9c2953
                        new_commits_count,
Pierre-Yves Chibon 9c2953
                        commenttext,
Pierre-Yves Chibon 9c2953
                    )
Pierre-Yves Chibon 875a08
                else:
Pierre-Yves Chibon 875a08
                    commenttext = "**%d new commits added**\n\n%s" % (
Pierre-Yves Chibon 9c2953
                        new_commits_count,
Pierre-Yves Chibon 9c2953
                        commenttext,
Pierre-Yves Chibon 9c2953
                    )
Pierre-Yves Chibon 9c2953
            if (
Pierre-Yves Chibon 9c2953
                request.commit_start
Pierre-Yves Chibon 9c2953
                and request.commit_start != first_commit.oid.hex
Pierre-Yves Chibon 9c2953
            ):
Pierre-Yves Chibon f39c35
                pr_action = "rebased"
Pierre-Yves Chibon 9c2953
                commenttext = "rebased onto %s" % first_commit.oid.hex
Pierre-Yves Chibon 875a08
        request.commit_start = first_commit.oid.hex
Pierre-Yves Chibon 875a08
        request.commit_stop = diff_commits[0].oid.hex
Pierre-Yves Chibon 875a08
        session.add(request)
Pierre-Yves Chibon 875a08
        session.commit()
Pierre-Yves Chibon b983f9
        _log.debug(
Pierre-Yves Chibon b983f9
            "pagure.lib.git.diff_pull_request, commenttext: %s", commenttext
Pierre-Yves Chibon b983f9
        )
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon eb5f49
        pagure.lib.tasks.sync_pull_ref.delay(
Pierre-Yves Chibon acd3eb
            request.project.name,
Pierre-Yves Chibon acd3eb
            request.project.namespace,
Pierre-Yves Chibon acd3eb
            request.project.user.username if request.project.is_fork else None,
Pierre-Yves Chibon 9c2953
            request.id,
Pierre-Yves Chibon acd3eb
        )
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon 875a08
        if commenttext:
Pierre-Yves Chibon eb5f49
            pagure.lib.tasks.link_pr_to_ticket.delay(request.uid)
Pierre-Yves Chibon 35fe76
            if notify:
Pierre-Yves Chibon f39c35
                if pr_action:
Pierre-Yves Chibon f39c35
                    pagure.lib.notify.log(
Pierre-Yves Chibon f39c35
                        request.project,
Pierre-Yves Chibon f39c35
                        topic="pull-request.%s" % pr_action,
Pierre-Yves Chibon f39c35
                        msg=dict(
Pierre-Yves Chibon f39c35
                            pullrequest=request.to_json(
Pierre-Yves Chibon 97d19d
                                with_comments=False, public=True
Pierre-Yves Chibon 97d19d
                            ),
Pierre-Yves Chibon 97d19d
                            agent="pagure",
Pierre-Yves Chibon f39c35
                        ),
Pierre-Yves Chibon f39c35
                    )
Pierre-Yves Chibon b983f9
                _log.debug(
Pierre-Yves Chibon b983f9
                    "pagure.lib.git.diff_pull_request: adding notification: %s"
Pierre-Yves Chibon b983f9
                    "as user: %s",
Pierre-Yves Chibon b983f9
                    commenttext,
Pierre-Yves Chibon b983f9
                    username or request.user.username,
Pierre-Yves Chibon b983f9
                )
Pierre-Yves Chibon 930073
                pagure.lib.query.add_pull_request_comment(
Pierre-Yves Chibon 35fe76
                    session,
Pierre-Yves Chibon 35fe76
                    request,
Pierre-Yves Chibon 35fe76
                    commit=None,
Pierre-Yves Chibon 35fe76
                    tree_id=None,
Pierre-Yves Chibon 35fe76
                    filename=None,
Pierre-Yves Chibon 35fe76
                    row=None,
Pierre-Yves Chibon 35fe76
                    comment="%s" % commenttext,
Pierre-Yves Chibon b983f9
                    user=username or request.user.username,
Pierre-Yves Chibon 35fe76
                    notify=False,
Pierre-Yves Chibon 35fe76
                    notification=True,
Pierre-Yves Chibon 35fe76
                )
Pierre-Yves Chibon 35fe76
                session.commit()
Pierre-Yves Chibon 9542ad
        else:
Pierre-Yves Chibon 9542ad
            pagure.lib.git.update_git(request, repo=request.project)
Pierre-Yves Chibon 875a08
Pierre-Yves Chibon 875a08
    if with_diff:
Pierre-Yves Chibon 875a08
        return (diff_commits, diff)
Pierre-Yves Chibon 875a08
    else:
Pierre-Yves Chibon bcb5c1
        return diff_commits
Pierre-Yves Chibon 04f59d
Pierre-Yves Chibon 04f59d
Pierre-Yves Chibon acd3eb
def update_pull_ref(request, repo):
Pierre-Yves Chibon acd3eb
    """ Create or update the refs/pull/ reference in the git repo.
Pierre-Yves Chibon acd3eb
    """
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon b130e5
    repopath = pagure.utils.get_repo_path(request.project)
Pierre-Yves Chibon 9c2953
    reponame = "%s_%s" % (request.user.user, request.uid)
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon 9c2953
    _log.info("  Adding remote: %s pointing to: %s", reponame, repopath)
Pierre-Yves Chibon acd3eb
    rc = RemoteCollection(repo)
Slavek Kabrda 197fa2
Slavek Kabrda 197fa2
    try:
Slavek Kabrda 197fa2
        # we do rc.delete(reponame) both here and in the finally block below:
Slavek Kabrda 197fa2
        # * here: it's useful for cases when worker was interrupted
Slavek Kabrda 197fa2
        #   on the previous execution of this function and didn't manage
Slavek Kabrda 197fa2
        #   to remove the ref
Slavek Kabrda 197fa2
        # * in the finally clause: to remove the ref so that it doesn't stay
Slavek Kabrda 197fa2
        #   in the fork forever (as noted above, it might still stay there
Slavek Kabrda 197fa2
        #   if the worker gets interrupted, but that's not a huge deal)
Slavek Kabrda 197fa2
        rc[reponame]
Slavek Kabrda 197fa2
        rc.delete(reponame)
Slavek Kabrda 197fa2
    except KeyError:
Slavek Kabrda 197fa2
        pass
Slavek Kabrda 197fa2
Pierre-Yves Chibon acd3eb
    remote = rc.create(reponame, repopath)
Pierre-Yves Chibon acd3eb
    try:
Pierre-Yves Chibon acd3eb
        _log.info(
Pierre-Yves Chibon 9c2953
            "  Pushing refs/heads/%s to refs/pull/%s/head",
Pierre-Yves Chibon 9c2953
            request.branch_from,
Pierre-Yves Chibon 9c2953
            request.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 9c2953
        refname = "+refs/heads/%s:refs/pull/%s/head" % (
Pierre-Yves Chibon 9c2953
            request.branch_from,
Pierre-Yves Chibon 9c2953
            request.id,
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon acd3eb
        PagureRepo.push(remote, refname)
Pierre-Yves Chibon acd3eb
    finally:
Pierre-Yves Chibon acd3eb
        rc.delete(reponame)
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon acd3eb
Pierre-Yves Chibon 06f5ad
def get_git_tags(project, with_commits=False):
Pierre-Yves Chibon 04f59d
    """ Returns the list of tags created in the git repositorie of the
Pierre-Yves Chibon 04f59d
    specified project.
Pierre-Yves Chibon 04f59d
    """
Pierre-Yves Chibon b130e5
    repopath = pagure.utils.get_repo_path(project)
Pierre-Yves Chibon 609965
    repo_obj = PagureRepo(repopath)
Pierre-Yves Chibon 4230af
Pierre-Yves Chibon 06f5ad
    if with_commits:
Pierre-Yves Chibon 06f5ad
        tags = {}
Pierre-Yves Chibon 06f5ad
        for tag in repo_obj.listall_references():
Pierre-Yves Chibon 9c2953
            if tag.startswith("refs/tags/"):
Pierre-Yves Chibon 06f5ad
                ref = repo_obj.lookup_reference(tag)
Pierre-Yves Chibon 06f5ad
                if ref:
Pierre-Yves Chibon 29ff0a
                    com = ref.peel()
Pierre-Yves Chibon 06f5ad
                    if com:
Pierre-Yves Chibon 9c2953
                        tags[tag.split("refs/tags/")[1]] = com.oid.hex
Pierre-Yves Chibon 06f5ad
    else:
Pierre-Yves Chibon 06f5ad
        tags = [
Pierre-Yves Chibon 9c2953
            tag.split("refs/tags/")[1]
Pierre-Yves Chibon 06f5ad
            for tag in repo_obj.listall_references()
Pierre-Yves Chibon 9c2953
            if tag.startswith("refs/tags/")
Pierre-Yves Chibon 06f5ad
        ]
Pierre-Yves Chibon 4230af
Pierre-Yves Chibon 04f59d
    return tags
Pierre-Yves Chibon 49f980
Pierre-Yves Chibon 49f980
Pierre-Yves Chibon 49f980
def get_git_tags_objects(project):
Pierre-Yves Chibon 49f980
    """ Returns the list of references of the tags created in the git
Pierre-Yves Chibon 49f980
    repositorie the specified project.
Clement Verna d76ad6
    The list is sorted using the time of the commit associated to the tag """
Pierre-Yves Chibon b130e5
    repopath = pagure.utils.get_repo_path(project)
Pierre-Yves Chibon 609965
    repo_obj = PagureRepo(repopath)
Ryan Lerch fe67e4
    tags = {}
Ryan Lerch fe67e4
    for tag in repo_obj.listall_references():
Pierre-Yves Chibon 9c2953
        if "refs/tags/" in tag and repo_obj.lookup_reference(tag):
Pierre-Yves Chibon 616641
            commit_time = None
Pierre-Yves Chibon 616641
            try:
Pierre-Yves Chibon 616641
                theobject = repo_obj[repo_obj.lookup_reference(tag).target]
Pierre-Yves Chibon 616641
            except ValueError:
Pierre-Yves Chibon 616641
                theobject = None
Ryan Lerch fe67e4
            objecttype = ""
Ryan Lerch fe67e4
            if isinstance(theobject, pygit2.Tag):
Pierre-Yves Chibon 29ff0a
                underlying_obj = theobject.peel(pygit2.Commit)
Pierre-Yves Chibon 0d3c39
                commit_time = underlying_obj.commit_time
Ryan Lerch fe67e4
                objecttype = "tag"
Ryan Lerch 9aecfd
            elif isinstance(theobject, pygit2.Commit):
Ryan Lerch fe67e4
                commit_time = theobject.commit_time
Ryan Lerch fe67e4
                objecttype = "commit"
Ryan Lerch fe67e4
Ryan Lerch fe67e4
            tags[commit_time] = {
Pierre-Yves Chibon 616641
                "object": theobject,
Pierre-Yves Chibon c70df4
                "tagname": tag.replace("refs/tags/", ""),
Pierre-Yves Chibon 0f5e8f
                "date": commit_time,
Pierre-Yves Chibon 0f5e8f
                "objecttype": objecttype,
Pierre-Yves Chibon 0f5e8f
                "head_msg": None,
Pierre-Yves Chibon 0f5e8f
                "body_msg": None,
Pierre-Yves Chibon 0f5e8f
            }
Pierre-Yves Chibon 9c2953
            if objecttype == "tag":
Pierre-Yves Chibon 0f5e8f
                head_msg, _, body_msg = tags[commit_time][
Pierre-Yves Chibon 9c2953
                    "object"
Pierre-Yves Chibon 9c2953
                ].message.partition("\n")
Pierre-Yves Chibon 9c2953
                if body_msg.strip().endswith("\n-----END PGP SIGNATURE-----"):
Pierre-Yves Chibon cc2fc7
                    body_msg = body_msg.rsplit(
Pierre-Yves Chibon 9c2953
                        "-----BEGIN PGP SIGNATURE-----", 1
Pierre-Yves Chibon 9c2953
                    )[0].strip()
Pierre-Yves Chibon 0f5e8f
                tags[commit_time]["head_msg"] = head_msg
Pierre-Yves Chibon 0f5e8f
                tags[commit_time]["body_msg"] = body_msg
Clement Verna c99c18
    sorted_tags = []
Clement Verna c99c18
Ryan Lerch fe67e4
    for tag in sorted(tags, reverse=True):
Ryan Lerch fe67e4
        sorted_tags.append(tags[tag])
Clement Verna c99c18
Clement Verna c99c18
    return sorted_tags
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 7772c6
def log_commits_to_db(session, project, commits, gitdir):
Pierre-Yves Chibon 7772c6
    """ Log the given commits to the DB. """
Pierre-Yves Chibon 7772c6
    repo_obj = PagureRepo(gitdir)
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 801150
    for commitid in commits:
Pierre-Yves Chibon 7772c6
        try:
Pierre-Yves Chibon 801150
            commit = repo_obj[commitid]
Pierre-Yves Chibon 7772c6
        except ValueError:
Pierre-Yves Chibon 7772c6
            continue
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 7772c6
        try:
Pierre-Yves Chibon 930073
            author_obj = pagure.lib.query.get_user(
Pierre-Yves Chibon 930073
                session, commit.author.email
Pierre-Yves Chibon 930073
            )
Pierre-Yves Chibon 7772c6
        except pagure.exceptions.PagureException:
Pierre-Yves Chibon 7772c6
            author_obj = None
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 7772c6
        date_created = arrow.get(commit.commit_time)
Pierre-Yves Chibon 7772c6
Pierre-Yves Chibon 7772c6
        log = model.PagureLog(
Pierre-Yves Chibon 7772c6
            user_id=author_obj.id if author_obj else None,
Pierre-Yves Chibon 7772c6
            user_email=commit.author.email if not author_obj else None,
Pierre-Yves Chibon 7772c6
            project_id=project.id,
Pierre-Yves Chibon 9c2953
            log_type="committed",
Pierre-Yves Chibon 3fa4ec
            ref_id=commit.oid.hex,
Pierre-Yves Chibon 7772c6
            date=date_created.date(),
Pierre-Yves Chibon 9c2953
            date_created=date_created.datetime,
Pierre-Yves Chibon 7772c6
        )
Pierre-Yves Chibon 7772c6
        session.add(log)
Vivek Anand 8ced04
Vivek Anand 8ced04
Vivek Anand 8ced04
def reinit_git(project, repofolder):
Pierre-Yves Chibon 9c2953
    """ Delete and recreate a git folder
Vivek Anand 8ced04
    :args project: SQLAlchemy object of the project
Vivek Anand 8ced04
    :args folder: The folder which contains the git repos
Vivek Anand 8ced04
    like TICKETS_FOLDER for tickets and REQUESTS_FOLDER for
Vivek Anand 8ced04
    pull requests
Pierre-Yves Chibon 9c2953
    """
Vivek Anand 8ced04
Vivek Anand 8ced04
    repo_path = os.path.join(repofolder, project.path)
Vivek Anand 8ced04
    if not os.path.exists(repo_path):
Vivek Anand 8ced04
        return
Vivek Anand 8ced04
Vivek Anand 8ced04
    # delete that repo
Vivek Anand 8ced04
    shutil.rmtree(repo_path)
Vivek Anand 8ced04
Vivek Anand 8ced04
    # create it again
Vivek Anand 8ced04
    pygit2.init_repository(
Pierre-Yves Chibon 9c2953
        repo_path, bare=True, mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP
Vivek Anand 8ced04
    )
Matt Prahl cd940d
Matt Prahl cd940d
Brian Stinson 4f81f7
def get_git_branches(project, with_commits=False):
Pierre-Yves Chibon 9c2953
    """ Return a list of branches for the project
Matt Prahl cd940d
    :arg project: The Project instance to get the branches for
Julen Landa Alustiza f499a7
    :arg with_commits: Whether we should return branch head commits or not
Pierre-Yves Chibon 9c2953
    """
Pierre-Yves Chibon b130e5
    repo_path = pagure.utils.get_repo_path(project)
Brian Stinson 4f81f7
    repo_obj = PagureRepo(repo_path)
Brian Stinson 4f81f7
Brian Stinson 4f81f7
    if with_commits:
Brian Stinson 4f81f7
        branches = {}
Brian Stinson 4f81f7
Brian Stinson 4f81f7
        for branch in repo_obj.listall_branches():
Brian Stinson 4f81f7
            resolved_branch = repo_obj.lookup_branch(branch).resolve()
Brian Stinson 4f81f7
            com = resolved_branch.peel()
Brian Stinson 4f81f7
            if com:
Brian Stinson 4f81f7
                branches[branch] = com.oid.hex
Brian Stinson 4f81f7
    else:
Brian Stinson 4f81f7
        branches = repo_obj.listall_branches()
Brian Stinson 4f81f7
Brian Stinson 4f81f7
    return branches
mprahl 7d811e
mprahl 7d811e
Patrick Uiterwijk 03a519
def new_git_branch(
Patrick Uiterwijk 03a519
    username, project, branch, from_branch=None, from_commit=None
Patrick Uiterwijk 03a519
):
Pierre-Yves Chibon 9c2953
    """ Create a new git branch on the project
mprahl 7d811e
    :arg project: The Project instance to get the branches for
mprahl 7d811e
    :arg from_branch: The branch to branch off of
Pierre-Yves Chibon 9c2953
    """
Patrick Uiterwijk 3f97f6
    with TemporaryClone(project, "main", "new_branch") as tempclone:
Patrick Uiterwijk 3f97f6
        repo_obj = tempclone.repo
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        if not from_branch and not from_commit:
Patrick Uiterwijk 3f97f6
            from_branch = "master"
Patrick Uiterwijk 3f97f6
        branches = repo_obj.listall_branches()
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        if from_branch:
Patrick Uiterwijk 3f97f6
            if from_branch not in branches:
Patrick Uiterwijk 3f97f6
                raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                    'The "{0}" branch does not exist'.format(from_branch)
Patrick Uiterwijk 3f97f6
                )
Pierre-Yves Chibon 29ff0a
            parent = get_branch_ref(repo_obj, from_branch).peel()
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            if from_commit not in repo_obj:
Patrick Uiterwijk 3f97f6
                raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                    'The commit "{0}" does not exist'.format(from_commit)
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
            parent = repo_obj[from_commit]
mprahl 7d811e
Patrick Uiterwijk 3f97f6
        if branch not in branches:
Patrick Uiterwijk 3f97f6
            repo_obj.create_branch(branch, parent)
Patrick Uiterwijk 3f97f6
        else:
mprahl 7d811e
            raise pagure.exceptions.PagureException(
Patrick Uiterwijk 3f97f6
                'The branch "{0}" already exists'.format(branch)
Pierre-Yves Chibon 9c2953
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 03a519
        tempclone.push(username, branch, branch)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
def delete_project_repos(project):
Patrick Uiterwijk 3f97f6
    """ Deletes the actual git repositories on disk or repoSpanner
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    Args:
Patrick Uiterwijk 3f97f6
        project (Project): Project to delete repos for
Patrick Uiterwijk 3f97f6
    """
Slavek Kabrda 0915d0
    for repotype in pagure.lib.query.get_repotypes():
Patrick Uiterwijk 3f97f6
        if project.is_on_repospanner:
Patrick Uiterwijk 3f97f6
            _, regioninfo = project.repospanner_repo_info(repotype)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            _log.debug("Deleting repotype %s", repotype)
Patrick Uiterwijk 3f97f6
            data = {
Patrick Uiterwijk 3f97f6
                "Reponame": project._repospanner_repo_name(
Patrick Uiterwijk 3f97f6
                    repotype, project.repospanner_region
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
            }
Patrick Uiterwijk 3f97f6
            resp = requests.post(
Patrick Uiterwijk 3f97f6
                "%s/admin/deleterepo" % regioninfo["url"],
Patrick Uiterwijk 3f97f6
                json=data,
Patrick Uiterwijk 3f97f6
                verify=regioninfo["ca"],
Patrick Uiterwijk 3f97f6
                cert=(
Patrick Uiterwijk 3f97f6
                    regioninfo["admin_cert"]["cert"],
Patrick Uiterwijk 3f97f6
                    regioninfo["admin_cert"]["key"],
Patrick Uiterwijk 3f97f6
                ),
Pierre-Yves Chibon 9c2953
            )
Patrick Uiterwijk 3f97f6
            resp.raise_for_status()
Patrick Uiterwijk 3f97f6
            resp = resp.json()
Patrick Uiterwijk 3f97f6
            _log.debug("Response json: %s", resp)
Patrick Uiterwijk 3f97f6
            if not resp["Success"]:
Patrick Uiterwijk 3f97f6
                raise Exception(
Patrick Uiterwijk 3f97f6
                    "Error in repoSpanner API call: %s" % resp["Error"]
Patrick Uiterwijk 3f97f6
                )
mprahl 7d811e
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            repopath = project.repopath(repotype)
Patrick Uiterwijk 3f97f6
            if repopath is None:
Patrick Uiterwijk 3f97f6
                continue
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            try:
Patrick Uiterwijk 3f97f6
                shutil.rmtree(repopath)
Patrick Uiterwijk 3f97f6
            except Exception:
Patrick Uiterwijk 3f97f6
                _log.exception(
Patrick Uiterwijk 3f97f6
                    "Failed to remove repotype %s for %s",
Patrick Uiterwijk 3f97f6
                    repotype,
Patrick Uiterwijk 3f97f6
                    project.fullname,
Patrick Uiterwijk 3f97f6
                )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk d29158
def set_up_project_hooks(project, region, hook=None):
Patrick Uiterwijk d29158
    """ Makes sure the git repositories for a project have their hooks setup.
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
    Args:
Patrick Uiterwijk d29158
        project (model.Project): Project to set up hooks for
Patrick Uiterwijk d29158
        region (string or None): repoSpanner region to set hooks up for
Patrick Uiterwijk d29158
        hook (string): The hook ID to set up in repoSpanner (tests only)
Patrick Uiterwijk d29158
    """
Patrick Uiterwijk d29158
    if region is None:
Patrick Uiterwijk d29158
        # This repo is not on repoSpanner, create hooks locally
Patrick Uiterwijk d29158
        pagure.hooks.BaseHook.set_up(project)
Patrick Uiterwijk d29158
    else:
Patrick Uiterwijk d29158
        regioninfo = pagure_config["REPOSPANNER_REGIONS"].get(region)
Patrick Uiterwijk d29158
        if not regioninfo:
Patrick Uiterwijk d29158
            raise ValueError(
Patrick Uiterwijk d29158
                "Invalid repoSpanner region %s looked up" % region
Patrick Uiterwijk d29158
            )
Patrick Uiterwijk d29158
        if not hook:
Patrick Uiterwijk d29158
            hook = regioninfo["hook"]
Patrick Uiterwijk d29158
        if not hook:
Patrick Uiterwijk d29158
            # No hooks to set up for this region
Patrick Uiterwijk d29158
            return
Patrick Uiterwijk d29158
Slavek Kabrda 0915d0
        for repotype in pagure.lib.query.get_repotypes():
Patrick Uiterwijk d29158
            data = {
Patrick Uiterwijk d29158
                "Reponame": project._repospanner_repo_name(repotype, region),
Patrick Uiterwijk d29158
                "UpdateRequest": {
Patrick Uiterwijk d29158
                    "hook-prereceive": hook,
Patrick Uiterwijk d29158
                    "hook-update": hook,
Patrick Uiterwijk d29158
                    "hook-postreceive": hook,
Patrick Uiterwijk d29158
                },
Patrick Uiterwijk d29158
            }
Patrick Uiterwijk d29158
            resp = requests.post(
Patrick Uiterwijk d29158
                "%s/admin/editrepo" % regioninfo["url"],
Patrick Uiterwijk d29158
                json=data,
Patrick Uiterwijk d29158
                verify=regioninfo["ca"],
Patrick Uiterwijk d29158
                cert=(
Patrick Uiterwijk d29158
                    regioninfo["admin_cert"]["cert"],
Patrick Uiterwijk d29158
                    regioninfo["admin_cert"]["key"],
Patrick Uiterwijk d29158
                ),
Patrick Uiterwijk d29158
            )
Patrick Uiterwijk d29158
            resp.raise_for_status()
Patrick Uiterwijk d29158
            resp = resp.json()
Patrick Uiterwijk d29158
            _log.debug("Response json: %s", resp)
Patrick Uiterwijk d29158
            if not resp["Success"]:
Patrick Uiterwijk d29158
                raise Exception(
Patrick Uiterwijk d29158
                    "Error in repoSpanner API call: %s" % resp["Error"]
Patrick Uiterwijk d29158
                )
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
Patrick Uiterwijk 3f97f6
def _create_project_repo(project, region, templ, ignore_existing, repotype):
Patrick Uiterwijk 3f97f6
    """ Creates a single specific git repository on disk or repoSpanner
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    Args:
Patrick Uiterwijk 3f97f6
        project (Project): Project to create repos for
Patrick Uiterwijk 3f97f6
        region (string or None): repoSpanner region to create the repos in
Patrick Uiterwijk 3f97f6
        templ (string): Template directory, only valid for non-repoSpanner
Patrick Uiterwijk 3f97f6
        ignore_existing (bool): Whether a repo already existing is fatal
Patrick Uiterwijk 3f97f6
        repotype (string): Repotype to create
Patrick Uiterwijk 3f97f6
    Returns: (string or None): Directory created
Patrick Uiterwijk 3f97f6
    """
Patrick Uiterwijk 3f97f6
    if region:
Patrick Uiterwijk 3f97f6
        # repoSpanner creation
Patrick Uiterwijk 3f97f6
        regioninfo = pagure_config["REPOSPANNER_REGIONS"][region]
Patrick Uiterwijk 3f97f6
        # First create the repository on the repoSpanner region
Patrick Uiterwijk 3f97f6
        _log.debug("Creating repotype %s", repotype)
Patrick Uiterwijk 3f97f6
        data = {
Patrick Uiterwijk 3f97f6
            "Reponame": project._repospanner_repo_name(repotype, region),
Patrick Uiterwijk 3f97f6
            "Public": False,
Patrick Uiterwijk 3f97f6
        }
Patrick Uiterwijk 3f97f6
        resp = requests.post(
Patrick Uiterwijk 3f97f6
            "%s/admin/createrepo" % regioninfo["url"],
Patrick Uiterwijk 3f97f6
            json=data,
Patrick Uiterwijk 3f97f6
            verify=regioninfo["ca"],
Patrick Uiterwijk 3f97f6
            cert=(
Patrick Uiterwijk 3f97f6
                regioninfo["admin_cert"]["cert"],
Patrick Uiterwijk 3f97f6
                regioninfo["admin_cert"]["key"],
Patrick Uiterwijk 3f97f6
            ),
Pierre-Yves Chibon 9c2953
        )
Patrick Uiterwijk 3f97f6
        resp.raise_for_status()
Patrick Uiterwijk 3f97f6
        resp = resp.json()
Patrick Uiterwijk 3f97f6
        _log.debug("Response json: %s", resp)
Patrick Uiterwijk 3f97f6
        if not resp["Success"]:
Patrick Uiterwijk 6876a8
            if "already exists" in resp["Error"]:
Patrick Uiterwijk 6876a8
                if ignore_existing:
Patrick Uiterwijk 6876a8
                    return None
Patrick Uiterwijk 6876a8
                else:
Patrick Uiterwijk 6876a8
                    raise pagure.exceptions.RepoExistsException(resp["Error"])
Patrick Uiterwijk 3f97f6
            raise Exception(
Patrick Uiterwijk 3f97f6
                "Error in repoSpanner API call: %s" % resp["Error"]
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        return None
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    else:
Patrick Uiterwijk 3f97f6
        # local repo
Patrick Uiterwijk 3f97f6
        repodir = project.repopath(repotype)
Patrick Uiterwijk 3f97f6
        if repodir is None:
Patrick Uiterwijk 3f97f6
            # This repo type is disabled
Patrick Uiterwijk 3f97f6
            return None
Patrick Uiterwijk ac10ea
        if os.path.exists(repodir):
Patrick Uiterwijk ac10ea
            if not ignore_existing:
Patrick Uiterwijk ac10ea
                raise pagure.exceptions.RepoExistsException(
Patrick Uiterwijk ac10ea
                    "The %s repo %s already exists" % (repotype, project.path)
Patrick Uiterwijk ac10ea
                )
Patrick Uiterwijk ac10ea
            else:
Patrick Uiterwijk ac10ea
                return None
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        if repotype == "main":
Patrick Uiterwijk 3f97f6
            pygit2.init_repository(repodir, bare=True, template_path=templ)
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
            if not project.private:
Patrick Uiterwijk 3f97f6
                # Make the repo exportable via apache
Patrick Uiterwijk 3f97f6
                http_clone_file = os.path.join(repodir, "git-daemon-export-ok")
Patrick Uiterwijk 3f97f6
                if not os.path.exists(http_clone_file):
Patrick Uiterwijk 3f97f6
                    with open(http_clone_file, "w"):
Patrick Uiterwijk 3f97f6
                        pass
Patrick Uiterwijk 3f97f6
        else:
Patrick Uiterwijk 3f97f6
            pygit2.init_repository(
Patrick Uiterwijk 3f97f6
                repodir,
Patrick Uiterwijk 3f97f6
                bare=True,
Patrick Uiterwijk 3f97f6
                mode=pygit2.C.GIT_REPOSITORY_INIT_SHARED_GROUP,
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
        return repodir
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
def create_project_repos(project, region, templ, ignore_existing):
Patrick Uiterwijk 3f97f6
    """ Creates the actual git repositories on disk or repoSpanner
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    Args:
Patrick Uiterwijk 3f97f6
        project (Project): Project to create repos for
Patrick Uiterwijk 3f97f6
        region (string or None): repoSpanner region to create the repos in
Patrick Uiterwijk 3f97f6
        templ (string): Template directory, only valid for non-repoSpanner
Patrick Uiterwijk 3f97f6
        ignore_existing (bool): Whether a repo already existing is fatal
Patrick Uiterwijk 3f97f6
    """
Patrick Uiterwijk 3f97f6
    if region and templ:
Patrick Uiterwijk 3f97f6
        raise Exception("repoSpanner is incompatible with template directory")
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    created_dirs = []
Patrick Uiterwijk 3f97f6
Patrick Uiterwijk 3f97f6
    try:
Slavek Kabrda 0915d0
        for repotype in pagure.lib.query.get_repotypes():
Patrick Uiterwijk 3f97f6
            created = _create_project_repo(
Patrick Uiterwijk 3f97f6
                project, region, templ, ignore_existing, repotype
Patrick Uiterwijk 3f97f6
            )
Patrick Uiterwijk 3f97f6
            if created:
Patrick Uiterwijk 3f97f6
                created_dirs.append(created)
Patrick Uiterwijk 3f97f6
    except Exception:
Patrick Uiterwijk 3f97f6
        for created in created_dirs:
Patrick Uiterwijk 3f97f6
            shutil.rmtree(created)
Patrick Uiterwijk 3f97f6
        raise
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
    set_up_project_hooks(project, region)
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
def get_stats_patch(patch):
Pierre-Yves Chibon f6a6e0
    """ Returns some statistics about a given patch.
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    These stats include:
Pierre-Yves Chibon f6a6e0
        status: if the file was added (A), deleted (D), modified (M) or
Pierre-Yves Chibon f6a6e0
            renamed (R)
Pierre-Yves Chibon f6a6e0
        old_path: the path to the old file
Pierre-Yves Chibon f6a6e0
        new_path: the path to the new file
Pierre-Yves Chibon f6a6e0
        lines_added: the number of lines added in this patch
Pierre-Yves Chibon f6a6e0
        lines_removed: the number of lines removed in this patch
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    All these information are returned in a dict.
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    Args:
Pierre-Yves Chibon f6a6e0
        patch (pygit2.Patch): the patch object to get stats on
Pierre-Yves Chibon f6a6e0
    Returns: a dict with the stats described above
Pierre-Yves Chibon f6a6e0
    Raises (pagure.exceptions.PagureException): if for some reason (likely
Pierre-Yves Chibon f6a6e0
        a change in pygit2's API) this function does not manage to gather
Pierre-Yves Chibon f6a6e0
        all the stats it should
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    """
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    output = {
Pierre-Yves Chibon f6a6e0
        "lines_added": patch.line_stats[1],
Pierre-Yves Chibon f6a6e0
        "lines_removed": patch.line_stats[2],
Pierre-Yves Chibon f6a6e0
        "new_path": None,
Pierre-Yves Chibon f6a6e0
        "old_path": None,
Pierre-Yves Chibon f6a6e0
        "status": None,
Pierre-Yves Chibon f6a6e0
        "new_id": None,
Pierre-Yves Chibon f6a6e0
        "old_id": None,
Pierre-Yves Chibon f6a6e0
    }
Pierre-Yves Chibon f6a6e0
    if hasattr(patch, "new_file_path"):
Pierre-Yves Chibon f6a6e0
        # Older pygit2
Pierre-Yves Chibon f6a6e0
        status = patch.status
Pierre-Yves Chibon f6a6e0
        if patch.new_file_path != patch.old_file_path:
Pierre-Yves Chibon f6a6e0
            status = "R"
Pierre-Yves Chibon f6a6e0
        output["status"] = status
Pierre-Yves Chibon f6a6e0
        output["new_path"] = patch.new_file_path
Pierre-Yves Chibon f6a6e0
        output["old_path"] = patch.old_file_path
Pierre-Yves Chibon f6a6e0
        output["new_id"] = str(patch.new_id)
Pierre-Yves Chibon f6a6e0
        output["old_id"] = str(patch.old_id)
Pierre-Yves Chibon f6a6e0
    elif hasattr(patch, "delta"):
Pierre-Yves Chibon 70eb4b
        status = None
Pierre-Yves Chibon f6a6e0
        # Newer pygit2
Slavek Kabrda dade63
        # we recognize non-executable file, executable file and symlink
Slavek Kabrda dade63
        expected_modes = [33188, 33261, 40960]
Slavek Kabrda dade63
        if (
Slavek Kabrda dade63
            patch.delta.new_file.mode == 0
Slavek Kabrda dade63
            and patch.delta.old_file.mode in expected_modes
Slavek Kabrda dade63
        ):
Pierre-Yves Chibon f6a6e0
            status = "D"
Pierre-Yves Chibon f6a6e0
        elif (
Slavek Kabrda dade63
            patch.delta.new_file.mode in expected_modes
Pierre-Yves Chibon f6a6e0
            and patch.delta.old_file.mode == 0
Pierre-Yves Chibon f6a6e0
        ):
Pierre-Yves Chibon f6a6e0
            status = "A"
Slavek Kabrda dade63
        elif (
Slavek Kabrda dade63
            patch.delta.new_file.mode in expected_modes
Slavek Kabrda dade63
            and patch.delta.old_file.mode in expected_modes
Slavek Kabrda dade63
        ):
Pierre-Yves Chibon f6a6e0
            status = "M"
Pierre-Yves Chibon f6a6e0
        if patch.delta.new_file.path != patch.delta.old_file.path:
Pierre-Yves Chibon f6a6e0
            status = "R"
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
        output["status"] = status
Pierre-Yves Chibon f6a6e0
        output["new_path"] = patch.delta.new_file.path
Pierre-Yves Chibon f6a6e0
        output["new_id"] = str(patch.delta.new_file.id)
Pierre-Yves Chibon f6a6e0
        output["old_path"] = patch.delta.old_file.path
Pierre-Yves Chibon f6a6e0
        output["old_id"] = str(patch.delta.old_file.id)
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    if None in output.values():  # pragma: no-cover
Pierre-Yves Chibon f6a6e0
        raise pagure.exceptions.PagureException(
Pierre-Yves Chibon f6a6e0
            "Unable to properly retrieve the stats for this patch"
Pierre-Yves Chibon f6a6e0
        )
Pierre-Yves Chibon f6a6e0
Pierre-Yves Chibon f6a6e0
    return output
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
def generate_archive(project, commit, tag, name, archive_fmt):
Pierre-Yves Chibon ded1fa
    """ Generate the desired archive of the specified project for the
Pierre-Yves Chibon ded1fa
    specified commit with the given name and archive format.
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
    Args:
Pierre-Yves Chibon ded1fa
        project (pagure.lib.model.Project): the project's repository from
Pierre-Yves Chibon ded1fa
            which to generate the archive
Pierre-Yves Chibon ded1fa
        commit (str): the commit hash to generate the archive of
Pierre-Yves Chibon ded1fa
        name (str): the name to give to the archive
Pierre-Yves Chibon ded1fa
        archive_fmt (str): the format of the archive to generate, can be
Pierre-Yves Chibon ded1fa
            either gzip or tag or tar.gz
Pierre-Yves Chibon ded1fa
    Returns: None
Pierre-Yves Chibon ded1fa
    Raises (pagure.exceptions.PagureException): if an un-supported archive
Pierre-Yves Chibon ded1fa
        format is specified
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
    """
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
    def _exclude_git(filename):
Pierre-Yves Chibon ded1fa
        return ".git" in filename
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
    with TemporaryClone(project, "main", "archive", parent=name) as tempclone:
Pierre-Yves Chibon ded1fa
        repo_obj = tempclone.repo
Pierre-Yves Chibon ded1fa
        commit_obj = repo_obj[commit]
Pierre-Yves Chibon ded1fa
        repo_obj.checkout_tree(commit_obj.tree)
Pierre-Yves Chibon ded1fa
        archive_folder = pagure_config.get("ARCHIVE_FOLDER")
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
        tag_path = ""
Pierre-Yves Chibon ded1fa
        if tag:
Pierre-Yves Chibon ded1fa
            tag_path = os.path.join("tags", tag)
Pierre-Yves Chibon ded1fa
        target_path = os.path.join(
Pierre-Yves Chibon ded1fa
            archive_folder, project.fullname, tag_path, commit
Pierre-Yves Chibon ded1fa
        )
Pierre-Yves Chibon ded1fa
        if not os.path.exists(target_path):
Pierre-Yves Chibon 7c59e9
            _log.info("Creating folder: %s", target_path)
Pierre-Yves Chibon ded1fa
            os.makedirs(target_path)
Pierre-Yves Chibon ded1fa
        fullpath = os.path.join(target_path, name)
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
        if archive_fmt == "tar":
Pierre-Yves Chibon ded1fa
            with tarfile.open(name=fullpath + ".tar", mode="w") as tar:
Pierre-Yves Chibon ded1fa
                tar.add(
Pierre-Yves Chibon ded1fa
                    name=tempclone.repopath, exclude=_exclude_git, arcname=name
Pierre-Yves Chibon ded1fa
                )
Pierre-Yves Chibon ded1fa
        elif archive_fmt == "tar.gz":
Pierre-Yves Chibon ded1fa
            with tarfile.open(name=fullpath + ".tar.gz", mode="w:gz") as tar:
Pierre-Yves Chibon ded1fa
                tar.add(
Pierre-Yves Chibon ded1fa
                    name=tempclone.repopath, exclude=_exclude_git, arcname=name
Pierre-Yves Chibon ded1fa
                )
Pierre-Yves Chibon ded1fa
        elif archive_fmt == "zip":
Pierre-Yves Chibon ded1fa
            # Code from /usr/lib64/python2.7/zipfile.py adjusted for our
Pierre-Yves Chibon ded1fa
            # needs
Pierre-Yves Chibon ded1fa
            def addToZip(zf, path, zippath):
Pierre-Yves Chibon ded1fa
                if _exclude_git(path):
Pierre-Yves Chibon ded1fa
                    return
Pierre-Yves Chibon ded1fa
                if os.path.isfile(path):
Pierre-Yves Chibon ded1fa
                    zf.write(path, zippath, zipfile.ZIP_DEFLATED)
Pierre-Yves Chibon ded1fa
                elif os.path.isdir(path):
Pierre-Yves Chibon ded1fa
                    if zippath:
Pierre-Yves Chibon ded1fa
                        zf.write(path, zippath)
Pierre-Yves Chibon ded1fa
                    for nm in os.listdir(path):
Pierre-Yves Chibon ded1fa
                        if _exclude_git(path):
Pierre-Yves Chibon ded1fa
                            continue
Pierre-Yves Chibon ded1fa
                        addToZip(
Pierre-Yves Chibon ded1fa
                            zf,
Pierre-Yves Chibon ded1fa
                            os.path.join(path, nm),
Pierre-Yves Chibon ded1fa
                            os.path.join(zippath, nm),
Pierre-Yves Chibon ded1fa
                        )
Pierre-Yves Chibon ded1fa
Pierre-Yves Chibon ded1fa
            with zipfile.ZipFile(fullpath + ".zip", "w") as zipstream:
Pierre-Yves Chibon ded1fa
                addToZip(zipstream, tempclone.repopath, name)
Pierre-Yves Chibon ded1fa
        else:
Pierre-Yves Chibon ded1fa
            raise pagure.exceptions.PagureException(
Pierre-Yves Chibon ded1fa
                "Un-support archive format requested: %s", archive_fmt
Pierre-Yves Chibon ded1fa
            )
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
def mirror_pull_project(session, project, debug=False):
Pierre-Yves Chibon 227f4c
    """ Mirror locally a project from a remote URL. """
Pierre-Yves Chibon 227f4c
    remote = project.mirrored_from
Pierre-Yves Chibon cea8e2
    if not remote:
Pierre-Yves Chibon cea8e2
        _log.info("No remote found, ignoring")
Pierre-Yves Chibon cea8e2
        return
Pierre-Yves Chibon 227f4c
    repopath = tempfile.mkdtemp(prefix="pagure-mirror_in-")
Pierre-Yves Chibon 227f4c
    lclrepopath = pagure.utils.get_repo_path(project)
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
    def _run_command(command, logs):
Pierre-Yves Chibon 227f4c
        _log.info("Running the command: %s" % command)
Pierre-Yves Chibon 227f4c
        if debug:
Pierre-Yves Chibon 227f4c
            print("Running the command: %s" % command)
Pierre-Yves Chibon 227f4c
            print("  Running in: %s" % repopath)
Pierre-Yves Chibon 227f4c
        (stdout, stderr) = pagure.lib.git.read_git_lines(
Pierre-Yves Chibon 227f4c
            command, abspath=repopath, error=True
Pierre-Yves Chibon 227f4c
        )
Pierre-Yves Chibon 227f4c
        log = "Output from %s:\n  stdout: %s\n  stderr: %s" % (
Pierre-Yves Chibon 227f4c
            command,
Pierre-Yves Chibon 227f4c
            stdout,
Pierre-Yves Chibon 227f4c
            stderr,
Pierre-Yves Chibon 227f4c
        )
Pierre-Yves Chibon 227f4c
        logs.append(log)
Pierre-Yves Chibon 227f4c
        if debug:
Pierre-Yves Chibon 227f4c
            print(log)
Pierre-Yves Chibon 227f4c
        return logs
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
    try:
Pierre-Yves Chibon 227f4c
        # Pull
Pierre-Yves Chibon 227f4c
        logs = []
Pierre-Yves Chibon 227f4c
        logs = _run_command(["clone", "--mirror", remote, "."], logs)
Pierre-Yves Chibon 227f4c
        logs = _run_command(["remote", "add", "local", lclrepopath], logs)
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
        # Push the changes
Pierre-Yves Chibon 227f4c
        _log.info("Pushing")
Pierre-Yves Chibon 227f4c
        if debug:
Pierre-Yves Chibon 227f4c
            print("Pushing to the local git repo")
Pierre-Yves Chibon 227f4c
        extra = {}
Pierre-Yves Chibon 227f4c
        if project.is_on_repospanner:
Pierre-Yves Chibon 227f4c
            regioninfo = pagure_config["REPOSPANNER_REGIONS"][
Pierre-Yves Chibon 227f4c
                project.repospanner_region
Pierre-Yves Chibon 227f4c
            ]
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
            extra.update(
Pierre-Yves Chibon 227f4c
                {
Pierre-Yves Chibon 227f4c
                    "username": "pagure",
Pierre-Yves Chibon 227f4c
                    "repotype": "main",
Pierre-Yves Chibon 227f4c
                    "project_name": project.name,
Pierre-Yves Chibon 227f4c
                    "project_user": project.user.username
Pierre-Yves Chibon 227f4c
                    if project.is_fork
Pierre-Yves Chibon 227f4c
                    else "",
Pierre-Yves Chibon 227f4c
                    "project_namespace": project.namespace or "",
Pierre-Yves Chibon 227f4c
                }
Pierre-Yves Chibon 227f4c
            )
Pierre-Yves Chibon 227f4c
            args = []
Pierre-Yves Chibon 227f4c
            for opt in extra:
Pierre-Yves Chibon 227f4c
                args.extend(["--extra", opt, extra[opt]])
Pierre-Yves Chibon 227f4c
            command = [
Pierre-Yves Chibon 227f4c
                "git",
Pierre-Yves Chibon 227f4c
                "-c",
Pierre-Yves Chibon 227f4c
                "protocol.ext.allow=always",
Pierre-Yves Chibon 227f4c
                "push",
Pierre-Yves Chibon 227f4c
                "ext::%s %s %s"
Pierre-Yves Chibon 227f4c
                % (
Pierre-Yves Chibon 227f4c
                    pagure_config["REPOBRIDGE_BINARY"],
Pierre-Yves Chibon 227f4c
                    " ".join(args),
Pierre-Yves Chibon 227f4c
                    project._repospanner_repo_name("main"),
Pierre-Yves Chibon 227f4c
                ),
Pierre-Yves Chibon 227f4c
                "--repo",
Pierre-Yves Chibon 227f4c
                repopath,
Pierre-Yves Chibon 227f4c
            ]
Pierre-Yves Chibon 227f4c
            environ = {
Pierre-Yves Chibon 227f4c
                "USER": "pagure",
Pierre-Yves Chibon 227f4c
                "REPOBRIDGE_CONFIG": ":environment:",
Pierre-Yves Chibon 227f4c
                "REPOBRIDGE_BASEURL": regioninfo["url"],
Pierre-Yves Chibon 227f4c
                "REPOBRIDGE_CA": regioninfo["ca"],
Pierre-Yves Chibon 227f4c
                "REPOBRIDGE_CERT": regioninfo["push_cert"]["cert"],
Pierre-Yves Chibon 227f4c
                "REPOBRIDGE_KEY": regioninfo["push_cert"]["key"],
Pierre-Yves Chibon 227f4c
            }
Pierre-Yves Chibon 227f4c
        else:
Pierre-Yves Chibon 227f4c
            command = ["git", "push", "local", "--mirror"]
Pierre-Yves Chibon 227f4c
            environ = {}
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
        _log.debug("Running a git push to %s", project.fullname)
Pierre-Yves Chibon 227f4c
        env = os.environ.copy()
Pierre-Yves Chibon 227f4c
        env["GL_USER"] = "pagure"
Pierre-Yves Chibon 227f4c
        env["GL_BYPASS_ACCESS_CHECKS"] = "1"
Pierre-Yves Chibon 227f4c
        if pagure_config.get("GITOLITE_HOME"):
Pierre-Yves Chibon 227f4c
            env["HOME"] = pagure_config["GITOLITE_HOME"]
Pierre-Yves Chibon 227f4c
        env.update(environ)
Pierre-Yves Chibon 227f4c
        env.update(extra)
Pierre-Yves Chibon 227f4c
        out = subprocess.check_output(
Pierre-Yves Chibon 227f4c
            command, cwd=repopath, stderr=subprocess.STDOUT, env=env
Pierre-Yves Chibon 227f4c
        )
Pierre-Yves Chibon 227f4c
        log = "Output from %s:" % command
Pierre-Yves Chibon 227f4c
        logs.append(log)
Pierre-Yves Chibon 227f4c
        logs.append(out)
Pierre-Yves Chibon 227f4c
        _log.debug("Output: %s" % out)
Pierre-Yves Chibon 227f4c
Pierre-Yves Chibon 227f4c
        project.mirrored_from_last_log = "\n".join(logs)
Pierre-Yves Chibon 227f4c
        session.add(project)
Pierre-Yves Chibon 227f4c
        session.commit()
Pierre-Yves Chibon 227f4c
        _log.info("\n".join(logs))
Pierre-Yves Chibon 227f4c
    except subprocess.CalledProcessError as err:
Pierre-Yves Chibon 227f4c
        _log.debug(
Pierre-Yves Chibon 227f4c
            "Rebase FAILED: {cmd} returned code {code} with the "
Pierre-Yves Chibon 227f4c
            "following output: {output}".format(
Pierre-Yves Chibon 227f4c
                cmd=err.cmd, code=err.returncode, output=err.output
Pierre-Yves Chibon 227f4c
            )
Pierre-Yves Chibon 227f4c
        )
Pierre-Yves Chibon 227f4c
        # This should never really happen, since we control the repos, but
Pierre-Yves Chibon 227f4c
        # this way, we can be sure to get the output logged
Pierre-Yves Chibon 227f4c
        remotes = []
Pierre-Yves Chibon 227f4c
        for line in err.output.decode("utf-8").split("\n"):
Pierre-Yves Chibon 227f4c
            _log.info("Remote line: %s", line)
Pierre-Yves Chibon 227f4c
            if line.startswith("remote: "):
Pierre-Yves Chibon 227f4c
                _log.debug("Remote: %s" % line)
Pierre-Yves Chibon 227f4c
                remotes.append(line[len("remote: ") :].strip())
Pierre-Yves Chibon 227f4c
        if remotes:
Pierre-Yves Chibon 227f4c
            _log.info("Remote rejected with: %s" % remotes)
Pierre-Yves Chibon 227f4c
            raise pagure.exceptions.PagurePushDenied(
Pierre-Yves Chibon 227f4c
                "Remote hook declined the push: %s" % "\n".join(remotes)
Pierre-Yves Chibon 227f4c
            )
Pierre-Yves Chibon 227f4c
        else:
Pierre-Yves Chibon 227f4c
            # Something else happened, pass the original
Pierre-Yves Chibon 227f4c
            _log.exception("Error pushing. Output: %s", err.output)
Pierre-Yves Chibon 227f4c
            raise
Pierre-Yves Chibon 227f4c
    finally:
Pierre-Yves Chibon 227f4c
        shutil.rmtree(repopath)