|
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()
|