Blame pagure/hooks/__init__.py

Pierre-Yves Chibon 33b534
# -*- coding: utf-8 -*-
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3add0
"""
Pierre-Yves Chibon 5729b8
 (c) 2014-2017 - Copyright Red Hat Inc
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3add0
 Authors:
Pierre-Yves Chibon a3add0
   Pierre-Yves Chibon <pingou@pingoured.fr></pingou@pingoured.fr>
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3add0
"""
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon 67d1cc
from __future__ import unicode_literals, absolute_import
Aurélien Bompard dcf6f6
Patrick Uiterwijk 4012dc
import subprocess
Patrick Uiterwijk 4012dc
import sys
Patrick Uiterwijk 4a14a9
import traceback
Pierre-Yves Chibon 3976d7
import os
Aurélien Bompard 831553
Aurélien Bompard 831553
import six
Pierre-Yves Chibon 9ce34e
import wtforms
Pierre-Yves Chibon 3976d7
Pierre-Yves Chibon b130e5
from pagure.config import config as pagure_config
Clement Verna 45cdc3
from pagure.exceptions import FileNotFoundException
Pierre-Yves Chibon 930073
import pagure.lib.query
Patrick Uiterwijk 4012dc
import pagure.lib.git
Patrick Uiterwijk b2cb9c
from pagure.lib.git_auth import get_git_auth_helper
Patrick Uiterwijk 4012dc
from pagure.lib.plugins import get_enabled_plugins
Pierre-Yves Chibon 3976d7
Pierre-Yves Chibon a3add0
Lenka Segura e2e146
class RequiredIf(wtforms.validators.DataRequired):
Pierre-Yves Chibon b1fc61
    """ Wtforms validator setting a field as required if another field
Pierre-Yves Chibon b1fc61
    has a value.
Pierre-Yves Chibon b1fc61
    """
Pierre-Yves Chibon 9ce34e
Pierre-Yves Chibon b1fc61
    def __init__(self, fields, *args, **kwargs):
Aurélien Bompard 831553
        if isinstance(fields, six.string_types):
Pierre-Yves Chibon b1fc61
            fields = [fields]
Pierre-Yves Chibon b1fc61
        self.fields = fields
Pierre-Yves Chibon b1fc61
        super(RequiredIf, self).__init__(*args, **kwargs)
Pierre-Yves Chibon 9ce34e
Pierre-Yves Chibon b1fc61
    def __call__(self, form, field):
Pierre-Yves Chibon b1fc61
        for fieldname in self.fields:
Pierre-Yves Chibon b1fc61
            nfield = form._fields.get(fieldname)
Pierre-Yves Chibon b1fc61
            if nfield is None:
Pierre-Yves Chibon 9c2953
                raise Exception('no field named "%s" in form' % fieldname)
Pierre-Yves Chibon b1fc61
            if bool(nfield.data):
Pierre-Yves Chibon 9c2953
                if (
Pierre-Yves Chibon 9c2953
                    not field.data
Pierre-Yves Chibon 9c2953
                    or isinstance(field.data, six.string_types)
Pierre-Yves Chibon 9c2953
                    and not field.data.strip()
Pierre-Yves Chibon 9c2953
                ):
Pierre-Yves Chibon 440fae
                    if self.message is None:
Pierre-Yves Chibon 9c2953
                        message = field.gettext("This field is required.")
Pierre-Yves Chibon 440fae
                    else:
Pierre-Yves Chibon 440fae
                        message = self.message
Pierre-Yves Chibon 440fae
Pierre-Yves Chibon 440fae
                    field.errors[:] = []
Pierre-Yves Chibon 440fae
                    raise wtforms.validators.StopValidation(message)
Pierre-Yves Chibon 9ce34e
Pierre-Yves Chibon 9ce34e
Patrick Uiterwijk 4012dc
class BaseRunner(object):
Patrick Uiterwijk 4012dc
    dbobj = None
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @classmethod
Patrick Uiterwijk 4012dc
    def runhook(
Patrick Uiterwijk 4012dc
        cls, session, username, hooktype, project, repotype, repodir, changes
Patrick Uiterwijk 4012dc
    ):
Patrick Uiterwijk 4012dc
        """ Run a specific hook on a project.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        By default, this calls out to the pre_receive, update or post_receive
Patrick Uiterwijk 4012dc
        functions as appropriate.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        Args:
Patrick Uiterwijk 4012dc
            session (Session): Database session
Patrick Uiterwijk 4012dc
            username (string): The user performing a push
Patrick Uiterwijk 4012dc
            project (model.Project): The project this call is made for
Slavek Kabrda 0915d0
            repotype (string): Value of lib.query.get_repotypes() indicating
Slavek Kabrda 0915d0
                for which repo the current call is
Patrick Uiterwijk 4012dc
            repodir (string): Directory where a clone of the specified repo is
Patrick Uiterwijk 4012dc
                located. Do note that this might or might not be a writable
Patrick Uiterwijk 4012dc
                clone.
Patrick Uiterwijk 4012dc
            changes (dict): A dict with keys being the ref to update, values
Patrick Uiterwijk 4012dc
                being a tuple of (from, to).
Pierre-Yves Chibon dd0de4
                For example: {'refs/heads/master': (hash_from, hash_to), ...}
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        if hooktype == "pre-receive":
Patrick Uiterwijk 4012dc
            cls.pre_receive(
Pierre-Yves Chibon d3d4c6
                session=session,
Pierre-Yves Chibon d3d4c6
                username=username,
Pierre-Yves Chibon d3d4c6
                project=project,
Pierre-Yves Chibon d3d4c6
                repotype=repotype,
Pierre-Yves Chibon d3d4c6
                repodir=repodir,
Pierre-Yves Chibon d3d4c6
                changes=changes,
Patrick Uiterwijk 4012dc
            )
Patrick Uiterwijk 4012dc
        elif hooktype == "update":
Pierre-Yves Chibon d3d4c6
            cls.update(
Pierre-Yves Chibon d3d4c6
                session=session,
Pierre-Yves Chibon d3d4c6
                username=username,
Pierre-Yves Chibon d3d4c6
                project=project,
Pierre-Yves Chibon d3d4c6
                repotype=repotype,
Pierre-Yves Chibon d3d4c6
                repodir=repodir,
Pierre-Yves Chibon d3d4c6
                changes=changes,
Pierre-Yves Chibon d3d4c6
            )
Pierre-Yves Chibon d3d4c6
Patrick Uiterwijk 4012dc
        elif hooktype == "post-receive":
Patrick Uiterwijk 4012dc
            cls.post_receive(
Pierre-Yves Chibon d3d4c6
                session=session,
Pierre-Yves Chibon d3d4c6
                username=username,
Pierre-Yves Chibon d3d4c6
                project=project,
Pierre-Yves Chibon d3d4c6
                repotype=repotype,
Pierre-Yves Chibon d3d4c6
                repodir=repodir,
Pierre-Yves Chibon d3d4c6
                changes=changes,
Patrick Uiterwijk 4012dc
            )
Patrick Uiterwijk 4012dc
        else:
Patrick Uiterwijk 4012dc
            raise ValueError('Invalid hook type "%s"' % hooktype)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @staticmethod
Patrick Uiterwijk 4012dc
    def pre_receive(session, username, project, repotype, repodir, changes):
Patrick Uiterwijk 4012dc
        """ Run the pre-receive tasks of a hook.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        For args, see BaseRunner.runhook.
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        pass
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @staticmethod
Patrick Uiterwijk 4012dc
    def update(session, username, project, repotype, repodir, changes):
Patrick Uiterwijk 4012dc
        """ Run the update tasks of a hook.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        For args, see BaseRunner.runhook.
Patrick Uiterwijk 4012dc
        Note that the "changes" list has exactly one element.
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        pass
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @staticmethod
Patrick Uiterwijk 4012dc
    def post_receive(session, username, project, repotype, repodir, changes):
Patrick Uiterwijk 4012dc
        """ Run the post-receive tasks of a hook.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        For args, see BaseRunner.runhook.
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        pass
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon a3add0
class BaseHook(object):
Pierre-Yves Chibon 9c2953
    """ Base class for pagure's hooks. """
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3add0
    name = None
Pierre-Yves Chibon a3add0
    form = None
farhaanbukhsh a7eada
    description = None
Slavek Kabrda 370e67
    backref = None
Patrick Uiterwijk 4012dc
    db_object = None
Patrick Uiterwijk 4012dc
    # hook_type is not used in hooks that use a Runner class, as those can
Patrick Uiterwijk 4012dc
    # implement run actions on whatever is useful to them.
Pierre-Yves Chibon 9c2953
    hook_type = "post-receive"
Patrick Uiterwijk 4012dc
    runner = None
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3f816
    @classmethod
Pierre-Yves Chibon 3976d7
    def set_up(cls, project):
Pierre-Yves Chibon 9c2953
        """ Install the generic post-receive hook that allow us to call
Pierre-Yves Chibon 3976d7
        multiple post-receive hooks as set per plugin.
Pierre-Yves Chibon 9c2953
        """
Patrick Uiterwijk 4012dc
        if project.is_on_repospanner:
Patrick Uiterwijk 4012dc
            # If the project is on repoSpanner, there's nothing to set up,
Patrick Uiterwijk 4012dc
            # as the hook script will be arranged by repo creation.
Patrick Uiterwijk 4012dc
            return
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon 3976d7
        hook_files = os.path.join(
Pierre-Yves Chibon 9c2953
            os.path.dirname(os.path.realpath(__file__)), "files"
Pierre-Yves Chibon 9c2953
        )
Pierre-Yves Chibon 3976d7
Slavek Kabrda 0915d0
        for repotype in pagure.lib.query.get_repotypes():
Patrick Uiterwijk d29158
            repopath = project.repopath(repotype)
Patrick Uiterwijk 4012dc
            if repopath is None:
Patrick Uiterwijk 4012dc
                continue
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon b106f9
            # Make sure the hooks folder exists
Pierre-Yves Chibon 9c2953
            hookfolder = os.path.join(repopath, "hooks")
Pierre-Yves Chibon b106f9
            if not os.path.exists(hookfolder):
Pierre-Yves Chibon b106f9
                os.makedirs(hookfolder)
Pierre-Yves Chibon b106f9
Patrick Uiterwijk 4012dc
            for hooktype in ("pre-receive", "update", "post-receive"):
Patrick Uiterwijk 4012dc
                # Install the main hook file
Patrick Uiterwijk 4012dc
                target = os.path.join(hookfolder, hooktype)
Patrick Uiterwijk 4012dc
                if not os.path.exists(target):
Patrick Uiterwijk 4012dc
                    os.symlink(os.path.join(hook_files, "hookrunner"), target)
Pierre-Yves Chibon 3976d7
Pierre-Yves Chibon 3976d7
    @classmethod
Clement Verna 88d19e
    def base_install(cls, repopaths, dbobj, hook_name, filein):
Pierre-Yves Chibon 9c2953
        """ Method called to install the hook for a project.
Pierre-Yves Chibon daa5de
Pierre-Yves Chibon fe5017
        :arg project: a ``pagure.model.Project`` object to which the hook
Pierre-Yves Chibon daa5de
            should be installed
Pierre-Yves Chibon a0391b
        :arg dbobj: the DB object the hook uses to store the settings
Pierre-Yves Chibon a0391b
            information.
Pierre-Yves Chibon daa5de
Pierre-Yves Chibon 9c2953
        """
Patrick Uiterwijk 4012dc
        if cls.runner:
Patrick Uiterwijk 4012dc
            # In the case of a new-style hook (with a Runner), there is no
Patrick Uiterwijk 4012dc
            # need to copy any files into place
Patrick Uiterwijk 4012dc
            return
Patrick Uiterwijk 4012dc
Clement Verna fd7659
        for repopath in repopaths:
Clement Verna fd7659
            if not os.path.exists(repopath):
Pierre-Yves Chibon 9c2953
                raise FileNotFoundException("Repo %s not found" % repopath)
Clement Verna a76adc
Clement Verna fd7659
            hook_files = os.path.join(
Pierre-Yves Chibon 9c2953
                os.path.dirname(os.path.realpath(__file__)), "files"
Pierre-Yves Chibon 9c2953
            )
Clement Verna a76adc
Clement Verna fd7659
            # Make sure the hooks folder exists
Pierre-Yves Chibon 9c2953
            hookfolder = os.path.join(repopath, "hooks")
Clement Verna fd7659
            if not os.path.exists(hookfolder):
Clement Verna fd7659
                os.makedirs(hookfolder)
Clement Verna a76adc
Clement Verna fd7659
            # Install the hook itself
Pierre-Yves Chibon 9c2953
            hook_file = os.path.join(
Pierre-Yves Chibon 9c2953
                repopath, "hooks", cls.hook_type + "." + hook_name
Pierre-Yves Chibon 9c2953
            )
Clement Verna a76adc
Clement Verna fd7659
            if not os.path.exists(hook_file):
Pierre-Yves Chibon 9c2953
                os.symlink(os.path.join(hook_files, filein), hook_file)
Pierre-Yves Chibon a3add0
Pierre-Yves Chibon a3f816
    @classmethod
Clement Verna 88d19e
    def base_remove(cls, repopaths, hook_name):
Pierre-Yves Chibon 9c2953
        """ Method called to remove the hook of a project.
Pierre-Yves Chibon daa5de
Pierre-Yves Chibon fe5017
        :arg project: a ``pagure.model.Project`` object to which the hook
Pierre-Yves Chibon daa5de
            should be installed
Pierre-Yves Chibon daa5de
Pierre-Yves Chibon 9c2953
        """
Clement Verna fd7659
        for repopath in repopaths:
Clement Verna 4123d5
            if not os.path.exists(repopath):
Pierre-Yves Chibon 9c2953
                raise FileNotFoundException("Repo %s not found" % repopath)
Clement Verna 4123d5
Pierre-Yves Chibon 9c2953
            hook_path = os.path.join(
Pierre-Yves Chibon 9c2953
                repopath, "hooks", cls.hook_type + "." + hook_name
Pierre-Yves Chibon 9c2953
            )
Clement Verna fd7659
            if os.path.exists(hook_path):
Clement Verna fd7659
                os.unlink(hook_path)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @classmethod
Patrick Uiterwijk 4012dc
    def install(cls, *args):
Patrick Uiterwijk 4012dc
        """ In sub-classess, this can be used for installation of the hook.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        However, this is not required anymore for hooks with a Runner.
Patrick Uiterwijk 4012dc
        This class is here as backwards compatibility.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        All args are ignored.
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        if not cls.runner:
Patrick Uiterwijk 4012dc
            raise ValueError("BaseHook.install called for runner-less hook")
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    @classmethod
Patrick Uiterwijk 4012dc
    def remove(cls, *args):
Patrick Uiterwijk 4012dc
        """ In sub-classess, this can be used for removal of the hook.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        However, this is not required anymore for hooks with a Runner.
Patrick Uiterwijk 4012dc
        This class is here as backwards compatibility.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
        All args are ignored.
Patrick Uiterwijk 4012dc
        """
Patrick Uiterwijk 4012dc
        if not cls.runner:
Patrick Uiterwijk 4012dc
            raise ValueError("BaseHook.remove called for runner-less hook")
Patrick Uiterwijk 4012dc
Slavek Kabrda 370e67
    @classmethod
Slavek Kabrda 370e67
    def is_enabled_for(cls, project):
Slavek Kabrda 370e67
        """ Determine if this hook should be run for given project.
Slavek Kabrda 370e67
Slavek Kabrda 370e67
        On some Pagure instances, some hooks should be run on all projects
Slavek Kabrda 370e67
        that fulfill certain criteria. It is therefore not necessary to keep
Slavek Kabrda 370e67
        database objects for them.
Slavek Kabrda 370e67
Slavek Kabrda 370e67
        If a hook's backref is set to None, this method is run to determine
Slavek Kabrda 370e67
        whether the hook should be run or not. These hooks also won't show
Slavek Kabrda 370e67
        up on settings page, since they can't be turned off.
Slavek Kabrda 370e67
Slavek Kabrda 370e67
        :arg project: The project to inspect
Slavek Kabrda 370e67
        :type project: pagure.lib.model.Project
Slavek Kabrda 370e67
        :return: True if this hook should be run on the given project,
Slavek Kabrda 370e67
            False otherwise
Slavek Kabrda 370e67
Slavek Kabrda 370e67
        """
Slavek Kabrda 370e67
        return False
Slavek Kabrda 370e67
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
def run_project_hooks(
Patrick Uiterwijk b2cb9c
    session,
Patrick Uiterwijk b2cb9c
    username,
Patrick Uiterwijk b2cb9c
    project,
Patrick Uiterwijk b2cb9c
    hooktype,
Patrick Uiterwijk b2cb9c
    repotype,
Patrick Uiterwijk b2cb9c
    repodir,
Patrick Uiterwijk b2cb9c
    changes,
Patrick Uiterwijk b2cb9c
    is_internal,
Patrick Uiterwijk b2cb9c
    pull_request,
Patrick Uiterwijk 4012dc
):
Patrick Uiterwijk 4012dc
    """ Function to run the hooks on a project
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    This will first call all the plugins with a Runner on the project,
Patrick Uiterwijk 4012dc
    and afterwards, for a non-repoSpanner repo, run all hooks/<hooktype>.*</hooktype>
Patrick Uiterwijk 4012dc
    scripts in the repo.
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    Args:
Patrick Uiterwijk 4012dc
        session: Database session
Patrick Uiterwijk 4012dc
        username (string): The user performing a push
Patrick Uiterwijk 4012dc
        project (model.Project): The project this call is made for
Slavek Kabrda 0915d0
        repotype (string): Value of lib.query.get_repotypes() indicating
Slavek Kabrda 0915d0
            for which repo the currnet call is
Patrick Uiterwijk 4012dc
        repodir (string): Directory where a clone of the specified repo is
Patrick Uiterwijk 4012dc
            located. Do note that this might or might not be a writable
Patrick Uiterwijk 4012dc
            clone.
Patrick Uiterwijk 4012dc
        hooktype (string): The type of hook to run: pre-receive, update
Patrick Uiterwijk 4012dc
            or post-receive
Patrick Uiterwijk 4012dc
        changes (dict): A dict with keys being the ref to update, values being
Patrick Uiterwijk 4012dc
            a tuple of (from, to).
Patrick Uiterwijk b2cb9c
        is_internal (bool): Whether this push originated from Pagure internally
Patrick Uiterwijk b2cb9c
        pull_request (model.PullRequest or None): The pull request whose merge
Patrick Uiterwijk b2cb9c
            is initiating this hook run.
Patrick Uiterwijk 4012dc
    """
Patrick Uiterwijk 4012dc
    debug = pagure_config.get("HOOK_DEBUG", False)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk b2cb9c
    # First we run dynamic ACLs
Patrick Uiterwijk b2cb9c
    authbackend = get_git_auth_helper()
Patrick Uiterwijk b2cb9c
Patrick Uiterwijk b2cb9c
    if (
Patrick Uiterwijk b2cb9c
        is_internal
Patrick Uiterwijk b2cb9c
        and username == "pagure"
Patrick Uiterwijk b2cb9c
        and repotype in ("tickets", "requests")
Patrick Uiterwijk b2cb9c
    ):
Patrick Uiterwijk b2cb9c
        if debug:
Patrick Uiterwijk b2cb9c
            print("This is an internal push, dynamic ACL is pre-approved")
Patrick Uiterwijk b2cb9c
    elif not authbackend.is_dynamic:
Patrick Uiterwijk b2cb9c
        if debug:
Patrick Uiterwijk b2cb9c
            print("Auth backend %s is static-only" % authbackend)
Patrick Uiterwijk f0edc7
    elif hooktype == "post-receive":
Patrick Uiterwijk f0edc7
        if debug:
Patrick Uiterwijk f0edc7
            print("Skipping auth backend during post-receive")
Patrick Uiterwijk b2cb9c
    else:
Patrick Uiterwijk b2cb9c
        if debug:
Patrick Uiterwijk b2cb9c
            print(
Patrick Uiterwijk b2cb9c
                "Checking push request against auth backend %s" % authbackend
Patrick Uiterwijk b2cb9c
            )
Patrick Uiterwijk b2cb9c
        todeny = []
Patrick Uiterwijk b2cb9c
        for refname in changes:
Patrick Uiterwijk b2cb9c
            change = changes[refname]
Patrick Uiterwijk b2cb9c
            authresult = authbackend.check_acl(
Patrick Uiterwijk b2cb9c
                session,
Patrick Uiterwijk b2cb9c
                project,
Patrick Uiterwijk b2cb9c
                username,
Patrick Uiterwijk b2cb9c
                refname,
Patrick Uiterwijk b2cb9c
                is_update=hooktype == "update",
Patrick Uiterwijk b2cb9c
                revfrom=change[0],
Patrick Uiterwijk b2cb9c
                revto=change[1],
Patrick Uiterwijk b2cb9c
                is_internal=is_internal,
Patrick Uiterwijk b2cb9c
                pull_request=pull_request,
Patrick Uiterwijk b2cb9c
                repotype=repotype,
Patrick Uiterwijk aec745
                repodir=repodir,
Patrick Uiterwijk b2cb9c
            )
Patrick Uiterwijk b2cb9c
            if debug:
Patrick Uiterwijk b2cb9c
                print(
Patrick Uiterwijk b2cb9c
                    "Auth result for ref %s: %s"
Patrick Uiterwijk b2cb9c
                    % (refname, "Accepted" if authresult else "Denied")
Patrick Uiterwijk b2cb9c
                )
Patrick Uiterwijk b2cb9c
            if not authresult:
Patrick Uiterwijk b2cb9c
                print(
Patrick Uiterwijk b2cb9c
                    "Denied push for ref '%s' for user '%s'"
Patrick Uiterwijk b2cb9c
                    % (refname, username)
Patrick Uiterwijk b2cb9c
                )
Patrick Uiterwijk b2cb9c
                todeny.append(refname)
Patrick Uiterwijk b2cb9c
        for toremove in todeny:
Patrick Uiterwijk b2cb9c
            del changes[toremove]
Patrick Uiterwijk b2cb9c
        if not changes:
Patrick Uiterwijk b2cb9c
            print("All changes have been rejected")
Patrick Uiterwijk b2cb9c
            sys.exit(1)
Patrick Uiterwijk b2cb9c
Patrick Uiterwijk b2cb9c
    # Now we run the hooks for plugins
Patrick Uiterwijk 4a14a9
    haderrors = False
Slavek Kabrda adbb5f
    for plugin, _ in get_enabled_plugins(project):
Patrick Uiterwijk 4012dc
        if not plugin.runner:
Patrick Uiterwijk 4012dc
            if debug:
Patrick Uiterwijk 4012dc
                print(
Patrick Uiterwijk 4012dc
                    "Hook plugin %s should be ported to Runner" % plugin.name
Patrick Uiterwijk 4012dc
                )
Patrick Uiterwijk 4012dc
        else:
Patrick Uiterwijk 4012dc
            if debug:
Patrick Uiterwijk 4012dc
                print("Running plugin %s" % plugin.name)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4a14a9
            try:
Patrick Uiterwijk 4a14a9
                plugin.runner.runhook(
Patrick Uiterwijk 4a14a9
                    session=session,
Patrick Uiterwijk 4a14a9
                    username=username,
Patrick Uiterwijk 4a14a9
                    hooktype=hooktype,
Patrick Uiterwijk 4a14a9
                    project=project,
Patrick Uiterwijk 4a14a9
                    repotype=repotype,
Patrick Uiterwijk 4a14a9
                    repodir=repodir,
Patrick Uiterwijk 4a14a9
                    changes=changes,
Patrick Uiterwijk 4a14a9
                )
Pierre-Yves Chibon 7716e2
            except Exception as e:
Pierre-Yves Chibon 7716e2
                if hooktype != "pre-receive" or debug:
Pierre-Yves Chibon 7716e2
                    traceback.print_exc()
Pierre-Yves Chibon 7716e2
                else:
Pierre-Yves Chibon 7716e2
                    print(str(e))
Patrick Uiterwijk 4a14a9
                haderrors = True
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    if project.is_on_repospanner:
Patrick Uiterwijk 4012dc
        # We are done. We are not doing any legacy hooks for repoSpanner
Patrick Uiterwijk 4012dc
        return
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    hookdir = os.path.join(repodir, "hooks")
Patrick Uiterwijk 4012dc
    if not os.path.exists(hookdir):
Patrick Uiterwijk 4012dc
        return
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    stdin = ""
Patrick Uiterwijk 4012dc
    args = []
Patrick Uiterwijk 4012dc
    if hooktype == "update":
Patrick Uiterwijk 4012dc
        refname = six.next(six.iterkeys(changes))
Patrick Uiterwijk 4012dc
        (revfrom, revto) = changes[refname]
Patrick Uiterwijk 4012dc
        args = [refname, revfrom, revto]
Patrick Uiterwijk 4012dc
    else:
Patrick Uiterwijk 4012dc
        stdin = (
Patrick Uiterwijk 4012dc
            "\n".join(
Patrick Uiterwijk 4012dc
                [
Patrick Uiterwijk 4012dc
                    "%s %s %s" % (changes[refname] + (refname,))
Patrick Uiterwijk 4012dc
                    for refname in changes
Patrick Uiterwijk 4012dc
                ]
Patrick Uiterwijk 4012dc
            )
Patrick Uiterwijk 4012dc
            + "\n"
Patrick Uiterwijk 4012dc
        )
Patrick Uiterwijk 4012dc
    stdin = stdin.encode("utf-8")
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    if debug:
Pierre-Yves Chibon 0055c0
        print(
Pierre-Yves Chibon 0055c0
            "Running legacy hooks (if any) with args: %s, stdin: %s"
Pierre-Yves Chibon 0055c0
            % (args, stdin)
Pierre-Yves Chibon 0055c0
        )
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    for hook in os.listdir(hookdir):
Patrick Uiterwijk 4012dc
        # This is for legacy hooks, which create symlinks in the form of
Patrick Uiterwijk 4012dc
        # "post-receive.$pluginname"
Patrick Uiterwijk 4012dc
        if hook.startswith(hooktype + "."):
Patrick Uiterwijk 4012dc
            hookfile = os.path.join(hookdir, hook)
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon 4937c4
            # By-pass all the old hooks that pagure may have created before
Pierre-Yves Chibon 4937c4
            # moving to the runner architecture
Pierre-Yves Chibon 4937c4
            if hook in pagure.lib.query.ORIGINAL_PAGURE_HOOK:
Patrick Uiterwijk 4012dc
                continue
Pierre-Yves Chibon e0fd66
Pierre-Yves Chibon e0fd66
            if hook.endswith(".sample"):
Pierre-Yves Chibon e0fd66
                # Ignore the samples that Git inserts
Pierre-Yves Chibon e0fd66
                continue
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
            # Execute
Patrick Uiterwijk 4012dc
            print(
Patrick Uiterwijk 4012dc
                "Running legacy hook %s. "
Patrick Uiterwijk 4012dc
                "Please ask your admin to port this to the new plugin "
Patrick Uiterwijk 4012dc
                "format, as the current system will cease functioning "
Patrick Uiterwijk 4012dc
                "in a future Pagure release" % hook
Patrick Uiterwijk 4012dc
            )
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
            # Using subprocess.Popen rather than check_call so that stdin
Patrick Uiterwijk 4012dc
            # can be passed without having to use a temporary file.
Patrick Uiterwijk 4012dc
            proc = subprocess.Popen(
Patrick Uiterwijk 4012dc
                [hookfile] + args, cwd=repodir, stdin=subprocess.PIPE
Patrick Uiterwijk 4012dc
            )
Patrick Uiterwijk 4012dc
            proc.communicate(stdin)
Patrick Uiterwijk 4012dc
            ecode = proc.wait()
Patrick Uiterwijk 4012dc
            if ecode != 0:
Patrick Uiterwijk 4012dc
                print("Hook %s errored out" % hook)
Patrick Uiterwijk 4a14a9
                haderrors = True
Patrick Uiterwijk 4a14a9
Patrick Uiterwijk 4a14a9
    if haderrors:
Pierre-Yves Chibon 5ea163
        session.close()
Patrick Uiterwijk 4a14a9
        raise SystemExit(1)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
Patrick Uiterwijk d29158
def extract_changes(from_stdin):
Patrick Uiterwijk d29158
    """ Extracts a changes dict from either stdin or argv
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    Args:
Patrick Uiterwijk d29158
        from_stdin (bool): Whether to use stdin. If false, uses argv
Patrick Uiterwijk 4012dc
    """
Patrick Uiterwijk 4012dc
    changes = {}
Patrick Uiterwijk d29158
    if from_stdin:
Patrick Uiterwijk 4012dc
        for line in sys.stdin:
Pierre-Yves Chibon f58de2
            (oldrev, newrev, refname) = str(line).strip().split(str(" "), 2)
Pierre-Yves Chibon f58de2
            if six.PY2:
Pierre-Yves Chibon f58de2
                refname = refname.decode("utf-8")
Patrick Uiterwijk 4012dc
            changes[refname] = (oldrev, newrev)
Patrick Uiterwijk d29158
    else:
Patrick Uiterwijk 4012dc
        (refname, oldrev, newrev) = sys.argv[1:]
Pierre-Yves Chibon f58de2
        if six.PY2:
Pierre-Yves Chibon f58de2
            refname = refname.decode("utf-8")
Patrick Uiterwijk 4012dc
        changes[refname] = (oldrev, newrev)
Patrick Uiterwijk d29158
    return changes
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
def run_hook_file(hooktype):
Patrick Uiterwijk d29158
    """ Runs a specific hook by grabbing the changes and running functions.
Patrick Uiterwijk d29158
Patrick Uiterwijk d29158
    Args:
Patrick Uiterwijk d29158
        hooktype (string): The name of the hook to run: pre-receive, update
Patrick Uiterwijk d29158
            or post-receive
Patrick Uiterwijk d29158
    """
Patrick Uiterwijk d29158
    if hooktype not in ("pre-receive", "update", "post-receive"):
Patrick Uiterwijk 4012dc
        raise ValueError("Hook type %s not valid" % hooktype)
Patrick Uiterwijk d29158
    changes = extract_changes(from_stdin=hooktype != "update")
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon cf98be
    session = pagure.lib.model_base.create_session(pagure_config["DB_URL"])
Patrick Uiterwijk b2cb9c
    if not session:
Patrick Uiterwijk b2cb9c
        raise Exception("Unable to initialize db session")
Patrick Uiterwijk b2cb9c
Patrick Uiterwijk 4012dc
    pushuser = os.environ.get("GL_USER")
Patrick Uiterwijk b2cb9c
    is_internal = os.environ.get("internal", False) == "yes"
Patrick Uiterwijk b2cb9c
    pull_request = None
Patrick Uiterwijk b2cb9c
    if "pull_request_uid" in os.environ:
Pierre-Yves Chibon 930073
        pull_request = pagure.lib.query.get_request_by_uid(
Patrick Uiterwijk b2cb9c
            session, os.environ["pull_request_uid"]
Patrick Uiterwijk b2cb9c
        )
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    if pagure_config.get("HOOK_DEBUG", False):
Patrick Uiterwijk 4012dc
        print("Changes: %s" % changes)
Patrick Uiterwijk 4012dc
Patrick Uiterwijk 4012dc
    gitdir = os.path.abspath(os.environ["GIT_DIR"])
Patrick Uiterwijk 4012dc
    (
Patrick Uiterwijk 4012dc
        repotype,
Patrick Uiterwijk 4012dc
        username,
Patrick Uiterwijk 4012dc
        namespace,
Patrick Uiterwijk 4012dc
        repo,
Patrick Uiterwijk 4012dc
    ) = pagure.lib.git.get_repo_info_from_path(gitdir)
Patrick Uiterwijk 4012dc
Pierre-Yves Chibon 930073
    project = pagure.lib.query._get_project(
Patrick Uiterwijk 4012dc
        session, repo, user=username, namespace=namespace
Patrick Uiterwijk 4012dc
    )
Pierre-Yves Chibon d3d4c6
    if not project:
Pierre-Yves Chibon d3d4c6
        raise Exception(
Pierre-Yves Chibon d3d4c6
            "Not able to find the project corresponding to: %%s - s - "
Pierre-Yves Chibon d3d4c6
            "%s - %s" % (repotype, username, namespace, repo)
Pierre-Yves Chibon d3d4c6
        )
Patrick Uiterwijk 4012dc
Slavek Kabrda 536c18
    if pagure_config.get("HOOK_DEBUG", False):
Slavek Kabrda 536c18
        print("Running %s hooks for %s" % (hooktype, project.fullname))
Patrick Uiterwijk 4012dc
    run_project_hooks(
Patrick Uiterwijk b2cb9c
        session,
Patrick Uiterwijk b2cb9c
        pushuser,
Patrick Uiterwijk b2cb9c
        project,
Patrick Uiterwijk b2cb9c
        hooktype,
Patrick Uiterwijk b2cb9c
        repotype,
Patrick Uiterwijk b2cb9c
        gitdir,
Patrick Uiterwijk b2cb9c
        changes,
Patrick Uiterwijk b2cb9c
        is_internal,
Patrick Uiterwijk b2cb9c
        pull_request,
Patrick Uiterwijk 4012dc
    )
Pierre-Yves Chibon d3d4c6
    session.close()