diff --git a/pagure/hooks/default.py b/pagure/hooks/default.py new file mode 100644 index 0000000..97f92bb --- /dev/null +++ b/pagure/hooks/default.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2016 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +import sqlalchemy as sa +import wtforms +try: + from flask_wtf import FlaskForm +except ImportError: + from flask_wtf import Form as FlaskForm +from sqlalchemy.orm import relation +from sqlalchemy.orm import backref + +import pagure.lib +from pagure.hooks import BaseHook, RequiredIf +from pagure.lib.model import BASE, Project +from pagure import SESSION, APP, get_repo_path + + +class DefaultTable(BASE): + """ Stores information about the CI linked to on a project. + + Table -- hook_default + """ + + __tablename__ = 'hook_default' + + id = sa.Column(sa.Integer, primary_key=True) + project_id = sa.Column( + sa.Integer, + sa.ForeignKey( + 'projects.id', onupdate='CASCADE', ondelete='CASCADE'), + nullable=False, + unique=True, + index=True) + active = sa.Column(sa.Boolean, nullable=False, default=False) + + project = relation( + 'Project', remote_side=[Project.id], + backref=backref( + 'default_hook', cascade="delete, delete-orphan", + single_parent=True, uselist=False) + ) + + +class DefaultForm(FlaskForm): + ''' Form to configure the default hook. ''' + active = wtforms.BooleanField( + 'Active', + [wtforms.validators.Optional()] + ) + + def __init__(self, *args, **kwargs): + """ Calls the default constructor with the normal argument but + uses the list of collection provided to fill the choices of the + drop-down list. + """ + super(DefaultForm, self).__init__(*args, **kwargs) + + +class Default(BaseHook): + ''' Mail hooks. ''' + + name = 'default' + description = 'Default hooks that should be enabled for each and '\ + 'every project.' + + form = DefaultForm + db_object = DefaultTable + backref = 'default_hook' + form_fields = ['active'] + + @classmethod + def install(cls, project, dbobj): + ''' Method called to install the hook for a project. + + :arg project: a ``pagure.model.Project`` object to which the hook + should be installed + + ''' + repopaths = [get_repo_path(project)] + + cls.base_install(repopaths, dbobj, 'default', 'default_hook.py') + + @classmethod + def remove(cls, project): + ''' Method called to remove the hook of a project. + + :arg project: a ``pagure.model.Project`` object to which the hook + should be installed + + ''' + repopaths = [get_repo_path(project)] + + cls.base_remove(repopaths, 'default') diff --git a/pagure/hooks/files/default_hook.py b/pagure/hooks/files/default_hook.py new file mode 100644 index 0000000..7526443 --- /dev/null +++ b/pagure/hooks/files/default_hook.py @@ -0,0 +1,84 @@ +#! /usr/bin/env python2 + + +"""Pagure specific hook to be added to all projects in pagure by default. +""" + +import os +import sys + +import pygit2 + +from sqlalchemy.exc import SQLAlchemyError + +if 'PAGURE_CONFIG' not in os.environ \ + and os.path.exists('/etc/pagure/pagure.cfg'): + os.environ['PAGURE_CONFIG'] = '/etc/pagure/pagure.cfg' + + +import pagure +import pagure.exceptions +import pagure.lib.link + + +abspath = os.path.abspath(os.environ['GIT_DIR']) + + +def run_as_post_receive_hook(): + + for line in sys.stdin: + if pagure.APP.config.get('HOOK_DEBUG', False): + print line + (oldrev, newrev, refname) = line.strip().split(' ', 2) + + if pagure.APP.config.get('HOOK_DEBUG', False): + print ' -- Old rev' + print oldrev + print ' -- New rev' + print newrev + print ' -- Ref name' + print refname + + # Retrieve the default branch + repo_obj = pygit2.Repository(abspath) + default_branch = None + if not repo_obj.is_empty and not repo_obj.head_is_unborn: + default_branch = repo_obj.head.shorthand + + # Skip all branch but the default one + refname = refname.replace('refs/heads/', '') + if refname != default_branch: + continue + + if set(newrev) == set(['0']): + print "Deleting a reference/branch, so we won't run the "\ + "pagure hook" + return + + repo = pagure.lib.git.get_repo_name(abspath) + username = pagure.lib.git.get_username(abspath) + namespace = pagure.lib.git.get_repo_namespace(abspath) + if pagure.APP.config.get('HOOK_DEBUG', False): + print 'repo:', repo + print 'user:', username + print 'namespace:', namespace + + project = pagure.lib.get_project( + pagure.SESSION, repo, user=username, namespace=namespace) + try: + # Reset the merge_status of all opened PR to refresh their cache + pagure.lib.reset_status_pull_request(pagure.SESSION, project) + pagure.SESSION.commit() + except SQLAlchemyError as err: # pragma: no cover + pagure.SESSION.rollback() + pagure.APP.logger.exception(err) + print 'An error occured while running the default hook, please '\ + 'report it to an admin.' + + +def main(args): + run_as_post_receive_hook() + + +if __name__ == '__main__': + main(sys.argv[1:])