Gitlab via the API: prepare a repo with automatic deploy keys and protected branches/tags

 

You have a whole mess of gitlab repo. You want to make them ready to use (protect master so only accepts merge, protect v* tags to prevent overwrite version, add a deploy key). And you are lazy. Have I the tool for you.

First, make sure you have python-gitlab installed:

pip3 install --upgrade python-gitlab

Now, go get a token from the web UI. For simplicity put it in this file in your home dir.

$ cat .python-gitlab.cfg 
[global]
default = git
ssl_verify = true
timeout = 5

[git]
url = https://git.MYDOMAIN.com
private_token = __MY_TOKEN__

Now run this magic script, takes arguments of the form group/project. It will:

  • Create an SSH ED25519 pub/priv key
  • Create a deploy key called ‘RELEASE KEY’ with the pubkey
  • Create a variable called SSH_DEPLOY_KEY (protected) with the privkey
  • protect master to only allow merge from maintainer, no push
  • protect the tags ‘v*’ to not allow overwrite

You’re welcome. If you don’t know what this means, you probably stopped reading earlier.

$ cat prep-repo.py 
#!/usr/bin/env python3
"""
Run a set of 'prep' steps against a repo:
    - Create a deploy key `DEPLOY KEY` with public
    - Create environment variable `SSH_DEPLOY_KEY` with private
    - set master to `protected` (merge maintainers, no push)
    - set v* tags (version tag) to protected
"""
import gitlab
import argparse
import configparser
import os
import sys
import tempfile

config = configparser.ConfigParser()
config.read(os.path.expanduser('~/.python-gitlab.cfg'))
globals = dict(config['global'])
defaults = dict(config[globals['default']])
url = defaults['url']
private_token = defaults['private_token']

parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', help='API url', default=url)
parser.add_argument(
    '-t', '--token', help='personal token', default=private_token)
parser.add_argument('args', nargs=argparse.REMAINDER)

args = parser.parse_args()

gl = gitlab.Gitlab(args.url, args.token)
gl.auth()


def find_group(gl, group):
    """ Given a group, find in the API """
    groups = gl.groups.list()
    for g in groups:
        if g.name == group:
            return g
    return None


def find_subgroup(gl, group, subgroup):
    """ Given a group and subgroup, return the subgroup """
    subgroups = group.subgroups.list()
    for sg in subgroups:
        if sg.name == subgroup:
            return sg
    return None


def find_project(gl, group, project_with_subgroup):
    """ if project is of form x/y, then assume x is a subgroup """
    pws = project_with_subgroup.split('/')
    if len(pws) == 2:
        subgroup = find_subgroup(gl, group, pws[0])
        project = pws[1]
        group = gl.groups.get(subgroup.id, lazy=True)
    else:
        project = pws[0]
    projects = group.projects.list()
    for p in projects:
        if p.name == project:
            return p
    return None


for group_project in args.args:
    group = group_project.split('/')[0]
    project = group_project.split('/', 1)[1]

    group = find_group(gl, group)
    if not group:
        print("Error: group <%s> does not exist" % group)
    project = find_project(gl, group, project)
    if not project:
        print("Error: %s does not exist" % project)
        sys.exit(1)
    project = gl.projects.get(project.get_id())

    with tempfile.TemporaryDirectory() as key_dir:
        key_file = os.path.join(key_dir, 'myrepo-key')
        os.system("/usr/bin/ssh-keygen -q -f %s -P '' -t ed25519" % key_file)

        keys = project.keys.list()
        for key in keys:
            if key.title == 'RELEASE KEY':
                print("Deploy keys already exists, overwrite")
                key.delete()
        key = project.keys.create({
            'title': 'RELEASE KEY',
            'key': open("%s.pub" % key_file).read()
        })

        variable = project.variables.get('SSH_DEPLOY_KEY')
        if variable:
            print("Deploy key variable already exists, overwrite")
            variable.delete()
        project.variables.create({
            'key': 'SSH_DEPLOY_KEY',
            'value': open(key_file).read()
        })

        #        import pdb;pdb.set_trace()
        try:
            branch = project.protectedbranches.get('master')
            if branch:
                print("Protected branch 'master' exists, overwrite")
                branch.delete()
        except gitlab.exceptions.GitlabGetError:
            pass
        project.protectedbranches.create({
            'name': 'master',
            'merge_access_level': gitlab.MAINTAINER_ACCESS,
            'push_access_level': 0
        })

        tag = project.protectedtags.get('v*')
        if tag:
            print("Protected tag 'v*' already exists, overwrite")
            tag.delete()
        project.protectedtags.create({
            'name':
            'v*',
            'create_access_level':
            gitlab.MAINTAINER_ACCESS
        })

Posted

in

by

Tags:

Comments

2 Responses to “Gitlab via the API: prepare a repo with automatic deploy keys and protected branches/tags”

  1. faizal

    can you let me know how to protect a tag and allow it only for a group instead of maintainer.

    1. db

      i don’t really understand what you are asking?
      the `protect` means that it can’t be read.

Leave a Reply

Your email address will not be published. Required fields are marked *