#!/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])') 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)) return (l) def usage(): print("usage: gitlab [--help] [--gitlab=GITLAB] [--fancy] what action [options]") print("") print("--gitlab=GITLAB: Specifies which python-gitlab.cfg configuration section should be used.") print(" If not defined, the default selection will be used.") print("--fancy : More verbose output.") print("--help : 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 `what`.") 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)) gitlab_id = None verbose = False args = [] d = {} for arg in sys.argv[1:]: if arg.startswith('--'): arg = arg[2:] if arg == 'help': usage() sys.exit(0) elif arg == 'fancy': verbose = True continue k, v = arg.split('=', 1) k = k.strip().replace('_', '-') v = v.strip() if k == 'gitlab': gitlab_id = v else: d[k] = v 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") if action not in ['get', 'list', 'update', 'create', 'delete', 'help']: die("Unknown action: %s. Use \"gitlab %s help\" to get details." % (action, what)) 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) try: gl = gitlab.Gitlab(gitlab_url, private_token=gitlab_token) gl.auth() except: die("Could not connect to GitLab (%s)" % gitlab_url) if action == "create": 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)) if verbose: o.pretty_print() else: o.short_print() sys.exit(0) elif action == "list": 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)) for o in l: if verbose: o.pretty_print() else: o.short_print() print("") sys.exit(0) elif action == "get": if not cls.canGet: die("%s objects can't be retrieved" % what) id = None if cls not in [gitlab.CurrentUser]: try: id = d.pop('id') except: die("Missing --id argument") try: o = cls(gl, id, **d) except Exception as e: die("Impossible to get object (%s)" % str(e)) if verbose: o.pretty_print() else: o.short_print() sys.exit(0) elif action == "delete": if not cls.canDelete: die("%s objects can't be deleted" % what) try: id = d.pop('id') except: die("Missing --id argument") try: o = cls(gl, id, **d) except Exception as e: die("Impossible to get object (%s)" % id, str(e)) try: o.delete() except Exception as e: die("Impossible to destroy object (%s)" % str(e)) sys.exit(0) elif action == "update": if not cls.canDelete: die("%s objects can't be updated" % what) try: id = d.pop('id') except: die("Missing --id argument") try: o = cls(gl, id, **d) except Exception as e: die("Impossible to get object (%s)" % str(e)) 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)) sys.exit(0)