#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2013 Gauvain Pocentek
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
import os
import sys
import re
try:
from ConfigParser import ConfigParser
except:
from configparser import ConfigParser
from inspect import getmro, getmembers, isclass
import gitlab
camel_re = re.compile('(.)([A-Z])')
extra_actions = {
gitlab.ProjectBranch: {
'protect': { 'requiredAttrs': ['id', 'project-id'] },
'unprotect': { 'requiredAttrs': ['id', 'project-id'] }
},
}
def die(msg):
sys.stderr.write(msg + "\n")
sys.exit(1)
def whatToCls(what):
return "".join([s.capitalize() for s in what.split("-")])
def clsToWhat(cls):
return camel_re.sub(r'\1-\2', cls.__name__).lower()
def actionHelpList(cls):
l = []
for action in 'list', 'get', 'create', 'update', 'delete':
attr = 'can' + action.capitalize()
try:
y = cls.__dict__[attr]
except:
y = gitlab.GitlabObject.__dict__[attr]
if not y:
continue
detail = ''
if action == 'list':
detail = " ".join(["--%s=ARG" % x.replace('_', '-') for x in cls.requiredListAttrs])
if detail:
detail += " "
detail += "--page=ARG --per-page=ARG"
elif action in ['get', 'delete']:
if cls not in [gitlab.CurrentUser]:
detail = "--id=ARG "
detail += " ".join(["--%s=ARG" % x.replace('_', '-') for x in cls.requiredGetAttrs])
elif action == 'create':
detail = " ".join(["--%s=ARG" % x.replace('_', '-') for x in cls.requiredCreateAttrs])
if detail:
detail += " "
detail += " ".join(["[--%s=ARG]" % x.replace('_', '-') for x in cls.optionalCreateAttrs])
elif action == 'update':
detail = " ".join(["[--%s=ARG]" % x.replace('_', '-') for x in cls.requiredCreateAttrs])
if detail:
detail += " "
detail += " ".join(["[--%s=ARG]" % x.replace('_', '-') for x in cls.optionalCreateAttrs])
l.append("%s %s" % (action, detail))
if extra_actions.has_key(cls):
for action in sorted(extra_actions[cls]):
d = extra_actions[cls][action]
detail = " ".join(["--%s=ARG" % arg for arg in d['requiredAttrs']])
l.append("%s %s" % (action, detail))
return (l)
def usage():
print("usage: gitlab [--help|-h] [--fancy|--verbose|-v] [--gitlab=GITLAB] WHAT ACTION [options]")
print("")
print("--gitlab=GITLAB")
print(" Specifies which python-gitlab.cfg configuration section should be used.")
print(" If not defined, the default selection will be used.")
print("")
print("--fancy, --verbose, -v")
print(" More verbose output.")
print("")
print("--help, -h")
print(" Displays this message.")
print("")
print("Available `options` depend on which WHAT/ACTION couple is used.")
print("If `ACTION` is \"help\", available actions and options will be listed for `ACTION`.")
print("")
print("Available `WHAT` values are:")
classes = []
for name, o in getmembers(gitlab):
if not isclass(o):
continue
if gitlab.GitlabObject in getmro(o) and o != gitlab.GitlabObject:
classes.append(o)
def s(a, b):
if a.__name__ < b.__name__:
return -1
elif a.__name__ > b.__name__:
return 1
classes.sort(cmp=s)
for cls in classes:
print(" %s" % clsToWhat(cls))
def do_auth():
try:
gl = gitlab.Gitlab(gitlab_url, private_token=gitlab_token)
gl.auth()
except:
die("Could not connect to GitLab (%s)" % gitlab_url)
return gl
def get_id():
try:
id = d.pop('id')
except:
die("Missing --id argument")
return id
def do_create(cls, d):
if not cls.canCreate:
die("%s objects can't be created" % what)
try:
o = cls(gl, d)
o.save()
except Exception as e:
die("Impossible to create object (%s)" % str(e))
return o
def do_list(cls, d):
if not cls.canList:
die("%s objects can't be listed" % what)
try:
l = cls.list(gl, **d)
except Exception as e:
die("Impossible to list objects (%s)" % str(e))
return l
def do_get(cls, d):
if not cls.canGet:
die("%s objects can't be retrieved" % what)
id = None
if cls not in [gitlab.CurrentUser]:
id = get_id()
try:
o = cls(gl, id, **d)
except Exception as e:
die("Impossible to get object (%s)" % str(e))
return o
def do_delete(cls, d):
if not cls.canDelete:
die("%s objects can't be deleted" % what)
o = do_get(cls, d)
try:
o.delete()
except Exception as e:
die("Impossible to destroy object (%s)" % str(e))
def do_update(cls, d):
if not cls.canUpdate:
die("%s objects can't be updated" % what)
o = do_get(cls, d)
try:
for k, v in d.items():
o.__dict__[k] = v
o.save()
except Exception as e:
die("Impossible to update object (%s)" % str(e))
return o
gitlab_id = None
verbose = False
args = []
d = {}
keep_looping = False
for idx, arg in enumerate(sys.argv[1:], 1):
if keep_looping:
keep_looping = False
continue
if arg.startswith('--'):
arg = arg[2:]
if arg == 'help':
usage()
sys.exit(0)
elif arg in ['verbose', 'fancy']:
verbose = True
continue
try:
k, v = arg.split('=', 1)
v.strip()
except:
k = arg
try:
v = sys.argv[idx + 1]
except:
die("--%s argument requires a value" % arg)
keep_looping = True
k = k.strip().replace('-', '_')
if k == 'gitlab':
gitlab_id = v
else:
d[k] = v
elif arg.startswith('-'):
arg = arg[1:]
if arg == 'h':
usage()
sys.exit(0)
elif arg == 'v':
verbose = True
else:
die("Unknown argument: -%s" % arg)
else:
args.append(arg)
# read the config
config = ConfigParser()
config.read(['/etc/python-gitlab.cfg',
os.path.expanduser('~/.python-gitlab.cfg')])
if gitlab_id is None:
try:
gitlab_id = config.get('global', 'default')
except:
die("Impossible to get the gitlab id (not specified in config file)")
try:
gitlab_url = config.get(gitlab_id, 'url')
gitlab_token = config.get(gitlab_id, 'private_token')
except:
die("Impossible to get gitlab informations from configuration (%s)" % gitlab_id)
try:
what = args.pop(0)
action = args.pop(0)
except:
die("Missing arguments. Use `gitlab -h` for help.")
try:
cls = gitlab.__dict__[whatToCls(what)]
except:
die("Unknown object: %s" % what)
if gitlab.GitlabObject not in getmro(cls):
die("Unknown object: %s" % what)
if action == "help":
print("%s options:" % what)
for item in actionHelpList(cls):
print(" %s %s" % (what, item))
sys.exit(0)
gl = do_auth()
if action == "create":
o = do_create(cls, d)
o.display(verbose)
elif action == "list":
for o in do_list(cls, d):
o.display(verbose)
print("")
elif action == "get":
o = do_get(cls, d)
o.display(verbose)
elif action == "delete":
o = do_delete(cls, d)
elif action == "update":
o = do_update(cls, d)
elif action == "protect":
if cls != gitlab.ProjectBranch:
die("%s objects can't be protected" % what)
o = do_get(cls, d)
o.protect()
elif action == "unprotect":
if cls != gitlab.ProjectBranch:
die("%s objects can't be protected" % what)
o = do_get(cls, d)
o.unprotect()
else:
die("Unknown action: %s. Use \"gitlab %s help\" to get details." % (action, what))
sys.exit(0)