#33 Add helper to ease project config settings
Merged 4 years ago by zuul. Opened 4 years ago by fbo.
fbo/fedora-project-config settings-helper  into  master

@@ -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 

+ ```

@@ -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)