summaryrefslogtreecommitdiff
path: root/gitlab/cli.py
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab/cli.py')
-rwxr-xr-xgitlab/cli.py357
1 files changed, 357 insertions, 0 deletions
diff --git a/gitlab/cli.py b/gitlab/cli.py
new file mode 100755
index 0000000..1805cde
--- /dev/null
+++ b/gitlab/cli.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2013-2014 Gauvain Pocentek <gauvain@pocentek.net>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+import argparse
+import inspect
+import os
+import re
+import sys
+try:
+ import ConfigParser as configparser
+except ImportError:
+ import configparser
+
+import gitlab
+
+camel_re = re.compile('(.)([A-Z])')
+LIST = 'list'
+GET = 'get'
+CREATE = 'create'
+UPDATE = 'update'
+DELETE = 'delete'
+PROTECT = 'protect'
+UNPROTECT = 'unprotect'
+SEARCH = 'search'
+OWNED = 'owned'
+ALL = 'all'
+ACTIONS = [LIST, GET, CREATE, UPDATE, DELETE]
+EXTRA_ACTION = [PROTECT, UNPROTECT, SEARCH, OWNED, ALL]
+
+extra_actions = {
+ gitlab.ProjectBranch: {PROTECT: {'requiredAttrs': ['id', 'project-id']},
+ UNPROTECT: {'requiredAttrs': ['id', 'project-id']}},
+ gitlab.Project: {SEARCH: {'requiredAttrs': ['query']},
+ OWNED: {'requiredAttrs': []},
+ ALL: {'requiredAttrs': []}},
+}
+
+
+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 populate_sub_parser_by_class(cls, sub_parser):
+ for action_name in ACTIONS:
+ attr = 'can' + action_name.capitalize()
+ y = getattr(cls, attr) or getattr(gitlab.GitlabObject, attr)
+ if not y:
+ continue
+ sub_parser_action = sub_parser.add_parser(action_name)
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=True)
+ for x in cls.requiredUrlAttrs]
+
+ if action_name == LIST:
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=True)
+ for x in cls.requiredListAttrs]
+ sub_parser_action.add_argument("--page", required=False)
+ sub_parser_action.add_argument("--per-page", required=False)
+
+ elif action_name in [GET, DELETE]:
+ if cls not in [gitlab.CurrentUser]:
+ id_attr = cls.idAttr.replace('_', '-')
+ sub_parser_action.add_argument("--%s" % id_attr,
+ required=True)
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=True)
+ for x in cls.requiredGetAttrs]
+
+ elif action_name == CREATE:
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=True)
+ for x in cls.requiredCreateAttrs]
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=False)
+ for x in cls.optionalCreateAttrs]
+
+ elif action_name == UPDATE:
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=True)
+ for x in cls.requiredCreateAttrs]
+ [sub_parser_action.add_argument("--%s" % x.replace('_', '-'),
+ required=False)
+ for x in cls.optionalCreateAttrs]
+
+ if cls in extra_actions:
+ for action_name in sorted(extra_actions[cls]):
+ sub_parser_action = sub_parser.add_parser(action_name)
+ d = extra_actions[cls][action_name]
+ [sub_parser_action.add_argument("--%s" % arg, required=True)
+ for arg in d['requiredAttrs']]
+
+
+def do_auth(gitlab_url, gitlab_token, ssl_verify, timeout):
+ try:
+ gl = gitlab.Gitlab(gitlab_url, private_token=gitlab_token,
+ ssl_verify=ssl_verify, timeout=timeout)
+ gl.auth()
+ return gl
+ except Exception as e:
+ die("Could not connect to GitLab %s (%s)" % (gitlab_url, str(e)))
+
+
+def get_id(cls, args):
+ try:
+ id = args.pop(cls.idAttr)
+ except Exception:
+ die("Missing --%s argument" % cls.idAttr.replace('_', '-'))
+
+ return id
+
+
+def do_create(cls, gl, what, args):
+ if not cls.canCreate:
+ die("%s objects can't be created" % what)
+
+ try:
+ o = cls(gl, args)
+ o.save()
+ except Exception as e:
+ die("Impossible to create object (%s)" % str(e))
+
+ return o
+
+
+def do_list(cls, gl, what, args):
+ if not cls.canList:
+ die("%s objects can't be listed" % what)
+
+ try:
+ l = cls.list(gl, **args)
+ except Exception as e:
+ die("Impossible to list objects (%s)" % str(e))
+
+ return l
+
+
+def do_get(cls, gl, what, args):
+ if not cls.canGet:
+ die("%s objects can't be retrieved" % what)
+
+ id = None
+ if cls not in [gitlab.CurrentUser]:
+ id = get_id(cls, args)
+
+ try:
+ o = cls(gl, id, **args)
+ except Exception as e:
+ die("Impossible to get object (%s)" % str(e))
+
+ return o
+
+
+def do_delete(cls, gl, what, args):
+ if not cls.canDelete:
+ die("%s objects can't be deleted" % what)
+
+ o = do_get(cls, args)
+ try:
+ o.delete()
+ except Exception as e:
+ die("Impossible to destroy object (%s)" % str(e))
+
+
+def do_update(cls, gl, what, args):
+ if not cls.canUpdate:
+ die("%s objects can't be updated" % what)
+
+ o = do_get(cls, args)
+ try:
+ for k, v in args.items():
+ o.__dict__[k] = v
+ o.save()
+ except Exception as e:
+ die("Impossible to update object (%s)" % str(e))
+
+ return o
+
+
+def do_project_search(gl, what, args):
+ try:
+ return gl.search_projects(args['query'])
+ except Exception as e:
+ die("Impossible to search projects (%s)" % str(e))
+
+
+def do_project_all(gl, what, args):
+ try:
+ return gl.all_projects()
+ except Exception as e:
+ die("Impossible to list all projects (%s)" % str(e))
+
+
+def do_project_owned(gl, what, args):
+ try:
+ return gl.owned_projects()
+ except Exception as e:
+ die("Impossible to list owned projects (%s)" % str(e))
+
+
+def main():
+ ssl_verify = True
+ timeout = 60
+
+ parser = argparse.ArgumentParser(
+ description="GitLab API Command Line Interface")
+ parser.add_argument("-v", "--verbose", "--fancy",
+ help="increase output verbosity",
+ action="store_true")
+ parser.add_argument("--gitlab",
+ help=("Specifies which python-gitlab.cfg "
+ "configuration section should be used. "
+ "If not defined, the default selection "
+ "will be used."),
+ required=False)
+
+ subparsers = parser.add_subparsers(dest='what')
+
+ # populate argparse for all Gitlab Object
+ classes = []
+ for cls in gitlab.__dict__.values():
+ try:
+ if gitlab.GitlabObject in inspect.getmro(cls):
+ classes.append(cls)
+ except AttributeError:
+ pass
+ classes.sort()
+
+ for cls in classes:
+ arg_name = clsToWhat(cls)
+ object_group = subparsers.add_parser(arg_name)
+
+ object_subparsers = object_group.add_subparsers(dest='action')
+ populate_sub_parser_by_class(cls, object_subparsers)
+
+ arg = parser.parse_args()
+ args = arg.__dict__
+
+ # read the config
+ config = configparser.ConfigParser()
+ config.read(['/etc/python-gitlab.cfg',
+ os.path.expanduser('~/.python-gitlab.cfg')])
+ gitlab_id = arg.gitlab
+ # conflicts with "gitlab" attribute from GitlabObject class
+ args.pop("gitlab")
+ verbose = arg.verbose
+ action = arg.action
+ what = arg.what
+
+ if gitlab_id is None:
+ try:
+ gitlab_id = config.get('global', 'default')
+ except Exception:
+ 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 Exception:
+ die("Impossible to get gitlab informations from configuration "
+ "(%s)" % gitlab_id)
+
+ try:
+ ssl_verify = config.getboolean('global', 'ssl_verify')
+ except Exception:
+ pass
+ try:
+ ssl_verify = config.getboolean(gitlab_id, 'ssl_verify')
+ except Exception:
+ pass
+
+ try:
+ timeout = config.getint('global', 'timeout')
+ except Exception:
+ pass
+ try:
+ timeout = config.getint(gitlab_id, 'timeout')
+ except Exception:
+ pass
+
+ cls = None
+ try:
+ cls = gitlab.__dict__[whatToCls(what)]
+ except Exception:
+ die("Unknown object: %s" % what)
+
+ gl = do_auth(gitlab_url, gitlab_token, ssl_verify, timeout)
+
+ if action == CREATE or action == GET:
+ o = globals()['do_%s' % action.lower()](cls, gl, what, args)
+ o.display(verbose)
+
+ elif action == LIST:
+ for o in do_list(cls, gl, what, args):
+ o.display(verbose)
+ print("")
+
+ elif action == DELETE or action == UPDATE:
+ o = globals()['do_%s' % action.lower()](cls, gl, what, args)
+
+ elif action == PROTECT or action == UNPROTECT:
+ if cls != gitlab.ProjectBranch:
+ die("%s objects can't be protected" % what)
+
+ o = do_get(cls, gl, what, args)
+ getattr(o, action)()
+
+ elif action == SEARCH:
+ if cls != gitlab.Project:
+ die("%s objects don't support this request" % what)
+
+ for o in do_project_search(gl, what, args):
+ o.display(verbose)
+
+ elif action == OWNED:
+ if cls != gitlab.Project:
+ die("%s objects don't support this request" % what)
+
+ for o in do_project_owned(gl, what, args):
+ o.display(verbose)
+
+ elif action == ALL:
+ if cls != gitlab.Project:
+ die("%s objects don't support this request" % what)
+
+ for o in do_project_all(gl, what, args):
+ o.display(verbose)
+
+ sys.exit(0)