#!/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)