Skip to content

Commit

Permalink
feat: organization functions to perform get projects
Browse files Browse the repository at this point in the history
  • Loading branch information
lpmatos committed Jul 28, 2020
1 parent 11eeca0 commit 405affb
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 82 deletions.
2 changes: 1 addition & 1 deletion gitlabrc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-

VERSION = (1, 0, 18)
VERSION = (1, 0, 33)

__version__ = ".".join(map(str, VERSION))
1 change: 0 additions & 1 deletion gitlabrc/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@

if __name__ == "__main__":
main()

10 changes: 5 additions & 5 deletions gitlabrc/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self, argv: Optional[Type[Dict]] = None) -> NoReturn:

def _create_parser_object(self) -> ArgumentParser:
return ArgumentParser(
description="GitlabRC is a CLI that help you to clone all projects inside a specific namespace in Gitlab",
description="GitlabRC is a CLI that help you clone projects inside a specific group (namespace) in Gitlab",
prog="gitlabrc",
epilog=CLI,
formatter_class=RawTextHelpFormatter)
Expand All @@ -39,12 +39,12 @@ def _adding_arguments(self) -> NoReturn:
dest = "namespace",
default = "",
metavar = "<namespace>",
help = "namespace in GitLab to clone all projects")
help = "namespace that represent a GitLab group used to clone/fetch all projects")
self._parser.add_argument("-p", "--path",
dest = "path",
default = self._config.get_env("PWD"),
metavar = "<path>",
help = "destination path to cloned projects")
help = "destination path into your system to clone/fetch all projects")
self._parser.add_argument("-m", "--method",
type = CloneMethod.parse,
dest = "method",
Expand All @@ -56,7 +56,7 @@ def _adding_arguments(self) -> NoReturn:
action ="store_true",
dest = "noroot",
default = False,
help = "don't create root namepace folder in path")
help = "don't create root namespace folder in path")
self._parser.add_argument("--dry-run",
action = "store_true",
dest = "dryrun",
Expand All @@ -66,7 +66,7 @@ def _adding_arguments(self) -> NoReturn:
action = "store_true",
dest = "tree",
default = False,
help = "list all repositories using anytree")
help = "list all repositories in a tree representation without clone/fetch")
self._parser.add_argument("--version",
action = "store_true",
help = "show version")
163 changes: 88 additions & 75 deletions gitlabrc/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,105 +3,118 @@
import os
import sys
import re
from git import Repo
import shutil
import time
import optparse
from art import *
import subprocess
from collections import defaultdict
from typing import NoReturn
from tqdm import tqdm

from .arguments import Arguments
from .method import CloneMethod
from .process import Process
from . import __version__ as VERSION
from .base import GitLabBase
from .method import CloneMethod
from .arguments import Arguments
from . import __version__ as VERSION
from .tree import Tree

# ==============================================================================
# FUNCTIONS
# ==============================================================================

def pname():
return f"[gitlabrc - {str(os.getpid())}]"

def make_tree(lst):
tree = lambda: defaultdict(tree)
d = tree()
for x in lst:
curr = d
for item in x:
curr = curr[item]
return d

def make_strs(d, indent=0):
strs = []
for k, v in d.items():
strs.append(" " * indent + str(k))
strs.extend(make_strs(v, indent+1))
return strs

def print_tree(d):
print('\n'.join(make_strs(d)))

def main():
Art=text2art("GitLabRC")
print(Art)
args = Arguments(argv=None if sys.argv[1:] else ["--help"]).args
if args.version:
print(f"Version: {VERSION}")
sys.exit(0)
perform(args)

def perform(options):
url, token, namespace = options.url, options.token, options.namespace
gl = GitLabBase(url, token).client

if not os.path.isdir(options.path):
sys.stderr.write("\nError: destination path does not exist " + options.path + "\n\n")
exit(1)
# ==============================================================================

git_path = shutil.which("git")
if git_path == "None":
def check_git():
if shutil.which("git") == "None":
sys.stderr.write("Error: git executable not installed or not in $PATH" + "\n")
exit(2)

# ==============================================================================

def get_subgroups(gl, group, root=False, info=False):
if root:
return gl.groups.list(all=True, owned=True, query_parameters={"id": group})
elif info:
return gl.groups.get(group.id, lazy=True)
else:
print(pname() + " using " + git_path)
return group.subgroups.list(all=True)

t = time.time()
# ==============================================================================

group = gl.groups.get(namespace, lazy=True, include_subgroups=True)
def get_projects(gl, group, root=False):
if root:
return gl.groups.get(group, lazy=True, include_subgroups=True).projects.list(all=True)
else:
return group.projects.list(all=True)

projects = []
# ==============================================================================

# Get all projects inside the namespace
for project in group.projects.list(all=True):
projects.append(project)
print(pname() + " found " + project.path_with_namespace)
def get_all_projects(gl, namespace):
projects = list()
root_projects = get_projects(gl, namespace, root=True)
rooot_subgroups = get_subgroups(gl, namespace, root=True)

# Get all projects inside the subgroups
for group in gl.groups.list(all=True, owned=True, query_parameters={"id": namespace}):
for project in group.projects.list(all=True):
if root_projects:
for project in root_projects:
projects.append(project)
if not options.tree:
print(pname() + " found " + project.path_with_namespace)

subgroups = group.subgroups.list(all=True)
while True:
for subgroup in subgroups:
real_group = gl.groups.get(subgroup.id, lazy=True)
for project in real_group.projects.list(all=True):
projects.append(project)
if not options.tree:
print(pname() + " found " + project.path_with_namespace)
subgroups = real_group.subgroups.list(all=True)
if len(subgroups) == 0: next
if len(subgroups) == 0: break
print(pname() + " found " + project.path_with_namespace)

if rooot_subgroups:
for group in rooot_subgroups:
group_projects = get_projects(gl, group)
if group_projects:
for group_project in group_projects:
projects.append(group_project)
print(pname() + " found " + project.path_with_namespace)
group_subgroups = get_subgroups(gl, group)
if group_subgroups:
while True:
for group in group_subgroups:
relative_subgroup = get_subgroups(gl, group, info=True)
for project in get_projects(gl, relative_subgroup):
projects.append(project)
print(pname() + " found " + project.path_with_namespace)
group_subgroups = get_subgroups(gl, relative_subgroup)
if len(group_subgroups) == 0: next
if len(group_subgroups) == 0: break
return projects

# ==============================================================================

def main():
Art=text2art("GitLabRC")
print(Art)
check_git()
args = Arguments(argv=None if sys.argv[1:] else ["--help"]).args
if args.version:
print(f"Version: {VERSION}")
sys.exit(0)
else:
print(f"Version: {VERSION}\n")
run(args)

def run(options):
url, token, namespace, path = options.url, options.token, options.namespace, options.path
gl, t = GitLabBase(url, token).client, time.time()

if path:
if not os.path.isdir(path):
sys.stderr.write("\nError: destination path does not exist " + options.path + "\n\n")
exit(1)

projects = get_all_projects(gl, namespace)

if options.tree:
lista = [project.path_with_namespace for project in projects]
lista = [[value + " " for value in elemento.split("/")] for elemento in lista]
d = make_tree(lista)
print_tree(d)
tree = Tree()
projects_parse = [project.path_with_namespace for project in projects]
projects_parse_content = [[value + " " for value in elemento.split("/")] for elemento in projects_parse]
d = tree.make_tree(projects_parse_content)
tree.print_tree(d)
exit(0)

if not options.dryrun:
for project in projects:
for index, project in enumerate(projects, start=1):
print(pname() + " clone/fetch project " + project.path_with_namespace)
folders = [f.strip().lower() for f in project.path_with_namespace.split("/")]
if options.noroot:
Expand All @@ -118,7 +131,7 @@ def perform(options):

if not os.path.isdir(clone_path):
print(f"{pname()} cloning {project_url}")
Process().run_command(f"git clone {project_url} {clone_path}")
Repo.clone_from(project_url, clone_path, branch="master")
else:
print(f"{pname()} fetching {project_url}")
Process().run_command(f"git -C {clone_path} fetch --all")
Expand Down
26 changes: 26 additions & 0 deletions gitlabrc/tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from collections import defaultdict
from typing import Type, List, DefaultDict, Text, NoReturn, Optional

class Tree:

def make_tree(self, information: Type[List[Text]]) -> DefaultDict:
tree = lambda: defaultdict(tree)
dictionary = tree()
for x in information:
curr = dictionary
for item in x:
curr = curr[item]
return dictionary

def make_strs(self,
dictionary: DefaultDict,
indent: Optional[Type[int]] = 0
) -> List:
strs = []
for k, v in dictionary.items():
strs.append(" " * indent + str(k))
strs.extend(self.make_strs(v, indent+1))
return strs

def print_tree(self, dictionary: DefaultDict) -> NoReturn:
print("\n".join(self.make_strs(dictionary)))
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
art==4.7
tqdm==4.43.0
GitPython==3.1.7
python-gitlab==2.4.0
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
# What packages are required for this module to be executed?
REQUIRED = [
"art",
"tqdm",
"GitPython",
"python-gitlab"
]

Expand Down

0 comments on commit 405affb

Please sign in to comment.