mirror_hook.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2016-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import sqlalchemy as sa
  9. import wtforms
  10. try:
  11. from flask_wtf import FlaskForm
  12. except ImportError:
  13. from flask_wtf import Form as FlaskForm
  14. from sqlalchemy.orm import relation
  15. from sqlalchemy.orm import backref
  16. import pagure.config
  17. import pagure.lib.tasks_mirror
  18. from pagure.hooks import BaseHook, BaseRunner, RequiredIf
  19. from pagure.lib.model import BASE, Project
  20. from pagure.utils import get_repo_path, ssh_urlpattern
  21. _config = pagure.config.reload_config()
  22. class MirrorTable(BASE):
  23. """Stores information about the mirroring hook deployed on a project.
  24. Table -- mirror_pagure
  25. """
  26. __tablename__ = "hook_mirror"
  27. id = sa.Column(sa.Integer, primary_key=True)
  28. project_id = sa.Column(
  29. sa.Integer,
  30. sa.ForeignKey("projects.id", onupdate="CASCADE", ondelete="CASCADE"),
  31. nullable=False,
  32. unique=True,
  33. index=True,
  34. )
  35. active = sa.Column(sa.Boolean, nullable=False, default=False)
  36. public_key = sa.Column(sa.Text, nullable=True)
  37. target = sa.Column(sa.Text, nullable=True)
  38. last_log = sa.Column(sa.Text, nullable=True)
  39. project = relation(
  40. "Project",
  41. remote_side=[Project.id],
  42. backref=backref(
  43. "mirror_hook",
  44. cascade="delete, delete-orphan",
  45. single_parent=True,
  46. uselist=False,
  47. ),
  48. )
  49. class MirrorRunner(BaseRunner):
  50. """ Runner for the mirror hook. """
  51. @staticmethod
  52. def post_receive(session, username, project, repotype, repodir, changes):
  53. """Run the default post-receive hook.
  54. For args, see BaseRunner.runhook.
  55. """
  56. print("Running the default hook")
  57. if repotype != "main":
  58. if _config.get("HOOK_DEBUG", False):
  59. print("Default hook only runs on the main project repository")
  60. return
  61. pagure.lib.tasks_mirror.mirror_project.delay(
  62. username=project.user.user if project.is_fork else None,
  63. namespace=project.namespace,
  64. name=project.name,
  65. )
  66. class CustomRegexp(wtforms.validators.Regexp):
  67. def __init__(self, *args, **kwargs):
  68. self.optional = kwargs.get("optional") or False
  69. if self.optional:
  70. kwargs.pop("optional")
  71. super(CustomRegexp, self).__init__(*args, **kwargs)
  72. def __call__(self, form, field):
  73. if self.optional:
  74. if field.data:
  75. return super(CustomRegexp, self).__call__(form, field)
  76. else:
  77. return super(CustomRegexp, self).__call__(form, field)
  78. class MirrorForm(FlaskForm):
  79. """ Form to configure the mirror hook. """
  80. active = wtforms.BooleanField("Active", [wtforms.validators.Optional()])
  81. target = wtforms.StringField(
  82. "Git repo to mirror to",
  83. [RequiredIf("active"), CustomRegexp(ssh_urlpattern, optional=True)],
  84. )
  85. public_key = wtforms.TextAreaField(
  86. "Public SSH key", [wtforms.validators.Optional()]
  87. )
  88. last_log = wtforms.TextAreaField(
  89. "Log of the last sync:", [wtforms.validators.Optional()]
  90. )
  91. DESCRIPTION = """
  92. Pagure specific hook to mirror a repo hosted on pagure to another location.
  93. The first field below should contain the URL to be set in the git configuration
  94. as the URL of the git repository to mirror to.
  95. It's format is going to be something like:
  96. <user>@<host>:<path>
  97. The public SSH key is being generated by pagure and will be available in this
  98. page shortly after the activation of this hook. Just refresh the page until
  99. it shows up.
  100. Finally the log of the last sync at the bottom is meant.
  101. """
  102. class MirrorHook(BaseHook):
  103. """ Mirror hook. """
  104. name = "Mirroring"
  105. description = DESCRIPTION
  106. form = MirrorForm
  107. db_object = MirrorTable
  108. backref = "mirror_hook"
  109. form_fields = ["active", "target", "public_key", "last_log"]
  110. form_fields_readonly = ["public_key", "last_log"]
  111. runner = MirrorRunner
  112. @classmethod
  113. def install(cls, project, dbobj):
  114. """Method called to install the hook for a project.
  115. :arg project: a ``pagure.model.Project`` object to which the hook
  116. should be installed
  117. """
  118. pagure.lib.tasks_mirror.setup_mirroring.delay(
  119. username=project.user.user if project.is_fork else None,
  120. namespace=project.namespace,
  121. name=project.name,
  122. )
  123. repopaths = [get_repo_path(project)]
  124. cls.base_install(repopaths, dbobj, "mirror", "mirror.py")
  125. @classmethod
  126. def remove(cls, project):
  127. """Method called to remove the hook of a project.
  128. :arg project: a ``pagure.model.Project`` object to which the hook
  129. should be installed
  130. """
  131. pagure.lib.tasks_mirror.teardown_mirroring.delay(
  132. username=project.user.user if project.is_fork else None,
  133. namespace=project.namespace,
  134. name=project.name,
  135. )
  136. repopaths = [get_repo_path(project)]
  137. cls.base_remove(repopaths, "mirror")