From b69f67a6b61aaee88e55ffc636f72f6dbb9df91e Mon Sep 17 00:00:00 2001
From: Fabien Boucher <fboucher@redhat.com>
Date: Nov 21 2019 15:34:52 +0000
Subject: Add helper to ease project config settings


---

diff --git a/tools/project-settings-helper/README.md b/tools/project-settings-helper/README.md
new file mode 100644
index 0000000..31ee57c
--- /dev/null
+++ b/tools/project-settings-helper/README.md
@@ -0,0 +1,17 @@
+# Project Settings Helper
+
+This tool is a helper to configure a Project hosted on a Pagure
+instance to integrate with Zuul.
+
+Indeed some specific settings are needed. See:
+https://fedoraproject.org/wiki/Zuul-based-ci#Configure_the_repository_for_Zuul
+
+## Usage:
+
+```
+./helper.py --host src.fedoraproject.org --projects test-zuul,readerlib-installer --apikey XYZ
+./helper.py --host src.fedoraproject.org --from-yaml projects.yaml --apikey XYZ
+cat projects.yaml 
+- test-zuul
+- readerlib-installer 
+```
diff --git a/tools/project-settings-helper/helper.py b/tools/project-settings-helper/helper.py
new file mode 100755
index 0000000..b615898
--- /dev/null
+++ b/tools/project-settings-helper/helper.py
@@ -0,0 +1,165 @@
+#!/bin/env python3
+
+import os
+import sys
+import logging
+import argparse
+import requests
+import yaml
+
+
+BASE_WH_URL = "https://softwarefactory-project.io/zuul/api/connection/%s/payload"
+ZUUL_WH_SRC_F_O = BASE_WH_URL % "src.fedoraproject.io"
+ZUUL_WH_PAGURE_IO = BASE_WH_URL % "pagure.io"
+
+
+class PagureAPIClientException(Exception):
+    pass
+
+
+class PagureAPIClient():
+    log = logging.getLogger("zuul.PagureAPIClient")
+
+    def __init__(
+            self, baseurl, api_token):
+        self.session = requests.Session()
+        self.base_url = '%s/api/0/' % baseurl
+        self.api_token = api_token
+        self.headers = {'Authorization': 'token %s' % self.api_token}
+
+    def _manage_error(self, data, code, url, verb):
+        if code < 400:
+            return
+        else:
+            raise PagureAPIClientException(
+                "Unable to %s on %s (code: %s) due to: %s" % (
+                    verb, url, code, data
+                ))
+
+    def get(self, url):
+        self.log.debug("Getting resource %s ..." % url)
+        ret = self.session.get(url, headers=self.headers)
+        self.log.debug("GET returned (code: %s): %s" % (
+            ret.status_code, ret.text))
+        return ret.json(), ret.status_code, ret.url, 'GET'
+
+    def post(self, url, params=None):
+        self.log.debug(
+            "Posting on resource %s, params (%s) ..." % (url, params))
+        ret = self.session.post(url, data=params, headers=self.headers)
+        self.log.debug("POST returned (code: %s): %s" % (
+            ret.status_code, ret.text))
+        return ret.json(), ret.status_code, ret.url, 'POST'
+
+    def get_config(self, project):
+        path = '%s/options' % project
+        resp = self.get(self.base_url + path)
+        self._manage_error(*resp)
+        return resp[0]
+
+    def set_config(self, project, params):
+        path = '%s/options/update' % project
+        resp = self.post(self.base_url + path, params)
+        self._manage_error(*resp)
+        return resp[0]
+
+    def set_acl(self, project, params):
+        path = '%s/git/modifyacls' % project
+        resp = self.post(self.base_url + path, params)
+        self._manage_error(*resp)
+        return resp[0]
+
+    def set_tags(self, project, params):
+        # This endpoint does not exists yet
+        # https://pagure.io/pagure/issue/4663
+        pass
+        # path = '%s/tags/update' % project
+        # resp = self.post(self.base_url + path, params)
+        # self._manage_error(*resp)
+        # return resp[0]
+
+
+def set_zuul_settings(client, project, gating):
+    logging.debug("Get settings config on %s" % project)
+    config = client.get_config(project)
+    config['Web-hooks'] = ZUUL_WH_SRC_F_O
+    config['pull_requests'] = True
+    config['notify_on_pull-request_flag'] = True
+    if gating:
+        config['open_metadata_access_to_all'] = False
+        # Cannot set 0 by the API so let's force it to -1
+        # Then https://review.opendev.org/#/c/691254/ is needed asap.
+        config['Minimum_score_to_merge_pull-request'] = -1
+        client.set_tags(project, ['gateit'])
+    client.set_config(project, config)
+    logging.debug("Applying new setting set on %s" % project)
+    acls = {
+        'user_type': 'user',
+        'name': 'zuul',
+        'acl': 'admin'
+    }
+    client.set_acl(project, acls)
+    logging.debug("Adding zuul user as admin on %s" % project)
+
+
+def process(client, projects, gating):
+    count = 1
+    for project in projects:
+        logging.info('[%s/%s] Applying setting for %s' % (
+            count, len(projects), project))
+        try:
+            set_zuul_settings(client, project, gating)
+            count += 1
+        except Exception:
+            logging.exception("Unable to set settings for %s" % project)
+
+
+def load_projects_from_yaml(path):
+    path = os.path.expanduser(path)
+    if not os.path.isfile(path):
+        logging.error("Unable to access %s" % path)
+        sys.exit(1)
+    with open(path) as fd:
+        projects = yaml.load(fd, Loader=yaml.FullLoader)
+    return projects
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--host', help='Pagure instance',
+        default='pagure.io')
+    parser.add_argument(
+        '--projects',
+        help='Projects to set settings on (comma separated)')
+    parser.add_argument(
+        '--from-yaml',
+        help='Projects to set settings on (from a yaml file)')
+    parser.add_argument(
+        '--apikey', help='API key with modify project ACL',
+        required=True)
+    parser.add_argument(
+        '--gating', help='Set settings for gating',
+        action="store_true")
+    parser.add_argument(
+        '--loglevel', help='Log level',
+        default="INFO")
+    args = parser.parse_args()
+
+    logging.basicConfig(
+        level=getattr(logging, args.loglevel.upper()))
+
+    client = PagureAPIClient(
+            'https://%s' % args.host,
+            args.apikey)
+    if not(args.projects or args.from_yaml):
+        logging.info("Missing argument --projects or --from-yaml")
+        parser.print_help()
+        sys.exit(1)
+
+    if args.projects:
+        projects = args.projects.split(',')
+    if args.from_yaml:
+        projects = load_projects_from_yaml(
+            args.from_yaml)
+    process(client, projects, args.gating)

