def api_view_plugins_project(repo, username=None, namespace=None): """ List project's plugins ---------------------- List installed plugins on a project. :: GET /api/0/<repo>/settings/plugins GET /api/0/<namespace>/<repo>/settings/plugins :: GET /api/0/fork/<username>/<repo>/settings/plugins GET /api/0/fork/<username>/<namespace>/<repo>/settings/plugins Sample response ^^^^^^^^^^^^^^^ :: { 'plugins': [ { 'Mail': { 'mail_to': '*****@*****.**' } } ], 'total_plugins': 1 } """ repo = _get_repo(repo, username, namespace) plugins = { plugin[0]: plugin[1] for plugin in plugins_lib.get_enabled_plugins(repo) } output = {} output["plugins"] = [] for (plugin, dbobj) in plugins.items(): if dbobj: form = plugin.form(obj=dbobj) fields = _filter_fields(plugin) output["plugins"].append( {plugin.name: {field: form[field].data for field in fields}}) output["total_plugins"] = len(output["plugins"]) jsonout = flask.jsonify(output) return jsonout
def run_project_hooks( session, username, project, hooktype, repotype, repodir, changes, is_internal, pull_request, ): """ Function to run the hooks on a project This will first call all the plugins with a Runner on the project, and afterwards, for a non-repoSpanner repo, run all hooks/<hooktype>.* scripts in the repo. Args: session: Database session username (string): The user performing a push project (model.Project): The project this call is made for repotype (string): Value of lib.query.get_repotypes() indicating for which repo the currnet call is repodir (string): Directory where a clone of the specified repo is located. Do note that this might or might not be a writable clone. hooktype (string): The type of hook to run: pre-receive, update or post-receive changes (dict): A dict with keys being the ref to update, values being a tuple of (from, to). is_internal (bool): Whether this push originated from Pagure internally pull_request (model.PullRequest or None): The pull request whose merge is initiating this hook run. """ debug = pagure_config.get("HOOK_DEBUG", False) # First we run dynamic ACLs authbackend = get_git_auth_helper() if (is_internal and username == "pagure" and repotype in ("tickets", "requests")): if debug: print("This is an internal push, dynamic ACL is pre-approved") elif not authbackend.is_dynamic: if debug: print("Auth backend %s is static-only" % authbackend) elif hooktype == "post-receive": if debug: print("Skipping auth backend during post-receive") else: if debug: print("Checking push request against auth backend %s" % authbackend) todeny = [] for refname in changes: change = changes[refname] authresult = authbackend.check_acl( session, project, username, refname, is_update=hooktype == "update", revfrom=change[0], revto=change[1], is_internal=is_internal, pull_request=pull_request, repotype=repotype, repodir=repodir, ) if debug: print("Auth result for ref %s: %s" % (refname, "Accepted" if authresult else "Denied")) if not authresult: print("Denied push for ref '%s' for user '%s'" % (refname, username)) todeny.append(refname) for toremove in todeny: del changes[toremove] if not changes: print("All changes have been rejected") sys.exit(1) # Now we run the hooks for plugins haderrors = False for plugin, _ in get_enabled_plugins(project): if not plugin.runner: if debug: print("Hook plugin %s should be ported to Runner" % plugin.name) else: if debug: print("Running plugin %s" % plugin.name) try: plugin.runner.runhook( session=session, username=username, hooktype=hooktype, project=project, repotype=repotype, repodir=repodir, changes=changes, ) except Exception as e: if hooktype != "pre-receive" or debug: traceback.print_exc() else: print(str(e)) haderrors = True if project.is_on_repospanner: # We are done. We are not doing any legacy hooks for repoSpanner return hookdir = os.path.join(repodir, "hooks") if not os.path.exists(hookdir): return stdin = "" args = [] if hooktype == "update": refname = six.next(six.iterkeys(changes)) (revfrom, revto) = changes[refname] args = [refname, revfrom, revto] else: stdin = ("\n".join([ "%s %s %s" % (changes[refname] + (refname, )) for refname in changes ]) + "\n") stdin = stdin.encode("utf-8") if debug: print("Running legacy hooks (if any) with args: %s, stdin: %s" % (args, stdin)) for hook in os.listdir(hookdir): # This is for legacy hooks, which create symlinks in the form of # "post-receive.$pluginname" if hook.startswith(hooktype + "."): hookfile = os.path.join(hookdir, hook) # By-pass all the old hooks that pagure may have created before # moving to the runner architecture if hook in pagure.lib.query.ORIGINAL_PAGURE_HOOK: continue if hook.endswith(".sample"): # Ignore the samples that Git inserts continue # Execute print("Running legacy hook %s. " "Please ask your admin to port this to the new plugin " "format, as the current system will cease functioning " "in a future Pagure release" % hook) # Using subprocess.Popen rather than check_call so that stdin # can be passed without having to use a temporary file. proc = subprocess.Popen([hookfile] + args, cwd=repodir, stdin=subprocess.PIPE) proc.communicate(stdin) ecode = proc.wait() if ecode != 0: print("Hook %s errored out" % hook) haderrors = True if haderrors: session.close() raise SystemExit(1)
def api_remove_plugin(repo, plugin, username=None, namespace=None): """ Remove plugin -------------- Remove a plugin from repository. :: POST /api/0/<repo>/settings/<plugin>/remove POST /api/0/<namespace>/<repo>/settings/<plugin>/remove :: POST /api/0/fork/<username>/<repo>/settings/<plugin>/remove POST /api/0/fork/<username>/<namespace>/<repo>/settings/<plugin> /remove Sample response ^^^^^^^^^^^^^^^ :: { "plugin": { "mail_to": "*****@*****.**" }, "message": "Hook 'Mail' deactivated" } """ output = {} repo = _get_repo(repo, username, namespace) _check_token(repo, project_token=False) plugin = _check_plugin(repo, plugin) dbobj = plugin.db_object() enabled_plugins = { plugin[0]: plugin[1] for plugin in plugins_lib.get_enabled_plugins(repo) } # If the plugin is not installed raise error if plugin not in enabled_plugins.keys(): raise pagure.exceptions.APIError( 400, error_code=APIERROR.EPLUGINNOTINSTALLED) if enabled_plugins[plugin]: dbobj = enabled_plugins[plugin] form = plugin.form(obj=dbobj) form.active.data = False try: plugin.remove(repo) except pagure.exceptions.FileNotFoundException as err: flask.g.session.rollback() _log.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) try: flask.g.session.commit() output["message"] = "Hook '%s' deactivated" % plugin.name output["plugin"] = { field: form[field].data for field in _filter_fields(plugin) } except SQLAlchemyError as err: # pragma: no cover flask.g.session.rollback() _log.exception(err) raise pagure.exceptions.APIError(400, error_code=APIERROR.EDBERROR) jsonout = flask.jsonify(output) return jsonout
def run_project_hooks( session, username, project, hooktype, repotype, repodir, changes, is_internal, pull_request, ): """ Function to run the hooks on a project This will first call all the plugins with a Runner on the project, and afterwards, for a non-repoSpanner repo, run all hooks/<hooktype>.* scripts in the repo. Args: session: Database session username (string): The user performing a push project (model.Project): The project this call is made for repotype (string): Value of lib.query.get_repotypes() indicating for which repo the currnet call is repodir (string): Directory where a clone of the specified repo is located. Do note that this might or might not be a writable clone. hooktype (string): The type of hook to run: pre-receive, update or post-receive changes (dict): A dict with keys being the ref to update, values being a tuple of (from, to). is_internal (bool): Whether this push originated from Pagure internally pull_request (model.PullRequest or None): The pull request whose merge is initiating this hook run. """ debug = pagure_config.get("HOOK_DEBUG", False) # First we run dynamic ACLs authbackend = get_git_auth_helper() if ( is_internal and username == "pagure" and repotype in ("tickets", "requests") ): if debug: print("This is an internal push, dynamic ACL is pre-approved") elif not authbackend.is_dynamic: if debug: print("Auth backend %s is static-only" % authbackend) elif hooktype == "post-receive": if debug: print("Skipping auth backend during post-receive") else: if debug: print( "Checking push request against auth backend %s" % authbackend ) todeny = [] for refname in changes: change = changes[refname] authresult = authbackend.check_acl( session, project, username, refname, is_update=hooktype == "update", revfrom=change[0], revto=change[1], is_internal=is_internal, pull_request=pull_request, repotype=repotype, repodir=repodir, ) if debug: print( "Auth result for ref %s: %s" % (refname, "Accepted" if authresult else "Denied") ) if not authresult: print( "Denied push for ref '%s' for user '%s'" % (refname, username) ) todeny.append(refname) for toremove in todeny: del changes[toremove] if not changes: print("All changes have been rejected") sys.exit(1) # Now we run the hooks for plugins haderrors = False for plugin, _ in get_enabled_plugins(project): if not plugin.runner: if debug: print( "Hook plugin %s should be ported to Runner" % plugin.name ) else: if debug: print("Running plugin %s" % plugin.name) try: plugin.runner.runhook( session=session, username=username, hooktype=hooktype, project=project, repotype=repotype, repodir=repodir, changes=changes, ) except Exception as e: if hooktype != "pre-receive" or debug: traceback.print_exc() else: print(str(e)) haderrors = True if project.is_on_repospanner: # We are done. We are not doing any legacy hooks for repoSpanner return hookdir = os.path.join(repodir, "hooks") if not os.path.exists(hookdir): return stdin = "" args = [] if hooktype == "update": refname = six.next(six.iterkeys(changes)) (revfrom, revto) = changes[refname] args = [refname, revfrom, revto] else: stdin = ( "\n".join( [ "%s %s %s" % (changes[refname] + (refname,)) for refname in changes ] ) + "\n" ) stdin = stdin.encode("utf-8") if debug: print( "Running legacy hooks (if any) with args: %s, stdin: %s" % (args, stdin) ) for hook in os.listdir(hookdir): # This is for legacy hooks, which create symlinks in the form of # "post-receive.$pluginname" if hook.startswith(hooktype + "."): hookfile = os.path.join(hookdir, hook) # By-pass all the old hooks that pagure may have created before # moving to the runner architecture if hook in pagure.lib.query.ORIGINAL_PAGURE_HOOK: continue if hook.endswith(".sample"): # Ignore the samples that Git inserts continue # Execute print( "Running legacy hook %s. " "Please ask your admin to port this to the new plugin " "format, as the current system will cease functioning " "in a future Pagure release" % hook ) # Using subprocess.Popen rather than check_call so that stdin # can be passed without having to use a temporary file. proc = subprocess.Popen( [hookfile] + args, cwd=repodir, stdin=subprocess.PIPE ) proc.communicate(stdin) ecode = proc.wait() if ecode != 0: print("Hook %s errored out" % hook) haderrors = True if haderrors: session.close() raise SystemExit(1)