From b3b0444b127877f446f5f1baad3339d0bbf4e5c5 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Chibon Date: Apr 10 2019 10:20:10 +0000 Subject: Add the pagure_distgit 3rd party extension for pagure This 3rd party extension has a dedicated database model expending pagure's database model to store information about monitoring status of a project. It also has two new endpoints, one to retrieve the current monitoring status of a project, the second to update it. These two endpoints are then leveraged in the srcfpo theme of pagure to display the current monitoring status of the package and update it. Signed-off-by: Pierre-Yves Chibon --- diff --git a/MANIFEST.in b/MANIFEST.in index c1871c2..aef86f3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,5 +4,9 @@ include dist_git_auth.py include dist_git_auth_tests.py include pagure_poc.py recursive-include template * +recursive-include pagure_distgit * recursive-include scripts * recursive-include static * + +recursive-exclude . *.pyc +recursive-exclude pagure_distgit *.pyc diff --git a/createdb.py b/createdb.py new file mode 100644 index 0000000..39c93df --- /dev/null +++ b/createdb.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +from __future__ import print_function, unicode_literals + +import argparse +import os + +from sqlalchemy import create_engine + + +parser = argparse.ArgumentParser( + description="Create/Update the Pagure database" +) +parser.add_argument( + "--config", + "-c", + dest="config", + help="Configuration file to use for pagure.", +) + +args = parser.parse_args() + +if args.config: + config = args.config + if not config.startswith("/"): + here = os.path.join(os.path.dirname(os.path.abspath(__file__))) + config = os.path.join(here, config) + print(config) + os.environ["PAGURE_CONFIG"] = config + +import pagure.config +from pagure_distgit.model import BASE, PagureAnitya + +db_url = pagure.config.config.get("DB_URL") +if db_url.startswith("postgres"): + engine = create_engine(db_url, echo=True, client_encoding="utf8") +else: + engine = create_engine(db_url, echo=True) + +BASE.metadata.create_all(engine, tables=[PagureAnitya.__table__]) diff --git a/pagure_distgit/__init__.py b/pagure_distgit/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pagure_distgit/__init__.py diff --git a/pagure_distgit/forms.py b/pagure_distgit/forms.py new file mode 100644 index 0000000..2f892a6 --- /dev/null +++ b/pagure_distgit/forms.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2019 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +from __future__ import unicode_literals, print_function + +import wtforms + +import pagure.forms + + +class AnityaForm(pagure.forms.PagureForm): + """ Form to configure taiga for a project. """ + + anitya_status = wtforms.SelectField( + "Monitoring status of the package in anitya", + [wtforms.validators.DataRequired()], + choices=[ + ("monitoring", "monitoring"), + ("no-monitoring", "no-monitoring"), + ("monitoring-with-scratch", "monitoring-with-scratch"), + ], + ) diff --git a/pagure_distgit/model.py b/pagure_distgit/model.py new file mode 100644 index 0000000..4ffecf0 --- /dev/null +++ b/pagure_distgit/model.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2019 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +from __future__ import unicode_literals, print_function + +import logging +import sqlalchemy as sa + +from sqlalchemy.orm import backref +from sqlalchemy.orm import relation + +from pagure.lib.model_base import BASE +from pagure.lib.model import Project + + +_log = logging.getLogger(__name__) + + +# DB Model + + +class PagureAnitya(BASE): + """ Stores information about the monitoring of a project in anitya. + + Table -- pagure_anitya + """ + + __tablename__ = "pagure_anitya" + + 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, + ) + + anitya_status = sa.Column( + sa.String(255), nullable=False, unique=False, default="no-monitoring" + ) + + project = relation( + "Project", + remote_side=[Project.id], + backref=backref( + "anitya", + cascade="delete, delete-orphan", + single_parent=True, + uselist=False, + ), + ) diff --git a/pagure_distgit/pagure_distgit.py b/pagure_distgit/pagure_distgit.py new file mode 100644 index 0000000..1b62d20 --- /dev/null +++ b/pagure_distgit/pagure_distgit.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +""" + (c) 2019 - Copyright Red Hat Inc + + Authors: + Pierre-Yves Chibon + +""" + +from __future__ import unicode_literals, print_function +import logging + +import flask +from sqlalchemy.exc import SQLAlchemyError + +import pagure.utils +from pagure.api import api_method, APIERROR, api_login_required +from pagure.api.utils import _get_repo, _check_token + +import pagure_distgit.forms +from pagure_distgit import model + + +_log = logging.getLogger(__name__) + +DISTGIT_NS = flask.Blueprint( + "distgit_ns", __name__, url_prefix="/_dg", template_folder="templates" +) + + +@DISTGIT_NS.route("/anitya//", methods=["GET"]) +def anitya_get_endpoint(namespace, repo): + """ Returns the current status of the monitoring in anitya of this package. + """ + repo = flask.g.repo + output = {"monitoring": "no-monitoring"} + if repo.anitya: + output = {"monitoring": repo.anitya.anitya_status} + return flask.jsonify(output) + + +@DISTGIT_NS.route("/anitya//", methods=["PATCH"]) +@api_login_required(acls=["modify_project"]) +@api_method +def anitya_patch_endpoint(namespace, repo): + """ Updates the current status of the monitoring in anitya of this package. + """ + + repo = _get_repo(repo, namespace=namespace) + _check_token(repo, project_token=False) + + is_site_admin = pagure.utils.is_admin() + admins = [u.username for u in repo.get_project_users("admin")] + # Only allow the main admin, the admins of the project, and Pagure site + # admins to modify projects' monitoring, even if the user has the right + # ACLs on their token + if ( + flask.g.fas_user.username not in admins + and flask.g.fas_user.username != repo.user.username + and not is_site_admin + ): + raise pagure.exceptions.APIError( + 401, error_code=APIERROR.EMODIFYPROJECTNOTALLOWED + ) + + form = pagure_distgit.forms.AnityaForm(csrf_enabled=False) + if form.validate_on_submit(): + try: + if repo.anitya: + repo.anitya.anitya_status = form.anitya_status.data + else: + repo = model.PagureAnitya( + project_id=repo.id, anitya_status=form.anitya_status.data + ) + flask.g.session.add(repo) + flask.g.session.commit() + except SQLAlchemyError as err: # pragma: no cover + flask.g.session.rollback() + _log.exception(err) + raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) + else: + raise pagure.exceptions.APIError( + 400, error_code=APIERROR.EINVALIDREQ, errors=form.errors + ) + + return anitya_get_endpoint(namespace, repo.name) diff --git a/setup.py b/setup.py index ab5a8e1..a05b788 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ setup( install_requires=install_requires, tests_require=tests_require, test_suite='nose.collector', - packages=[], + packages=['pagure_distgit'], py_modules=['dist_git_auth'], include_package_data=True, zip_safe=False,