Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

<!--next-version-placeholder-->





## [1.2.5] - 02/07/2024
### Added
- Added ability to clone a user's personal projects
### Changed
### Deprecated
### Removed
### Fixed
### Security


## [1.2.4] - 02/07/2024
Expand Down
2 changes: 1 addition & 1 deletion gitlabber/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.2.4'
__version__ = '1.2.5'
9 changes: 7 additions & 2 deletions gitlabber/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ def main():

tree = GitlabTree(args.url, args.token, args.method, args.naming, args.archived.api_value, includes,
excludes, args.file, args.concurrency, args.recursive, args.verbose,
args.use_fetch, args.hide_token, group_search=args.group_search)
log.debug("Reading projects tree from gitlab at [%s]", args.url)
args.include_shared, args.use_fetch, args.hide_token, args.user_projects, group_search=args.group_search)
tree.load_tree()

if tree.is_empty():
Expand Down Expand Up @@ -211,6 +210,12 @@ def parse_args(argv=None):
'--group-search',
metavar=('term'),
help='only include groups matching the search term, filtering done at the API level (useful for large projects, see: https://docs.gitlab.com/ee/api/groups.html#search-for-group works with partial names of path or name)')
parser.add_argument(
'-U',
'--user-projects',
action='store_true',
default=False,
help='fetch only user personal projects (skips the group tree altogether, group related parameters are ignored). Clones personal projects to \'{gitlab-username}-personal-projects\'')
parser.add_argument(
'--version',
action='store_true',
Expand Down
20 changes: 17 additions & 3 deletions gitlabber/gitlab_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@


class GitlabTree:

def __init__(self, url, token, method, naming=None, archived=None, includes=[], excludes=[], in_file=None, concurrency=1, recursive=False, disable_progress=False,
include_shared=True, use_fetch=False, hide_token=False, group_search=None):
include_shared=True, use_fetch=False, hide_token=False, user_projects=False, group_search=None):
self.includes = includes
self.excludes = excludes
self.url = url
Expand All @@ -38,6 +37,7 @@ def __init__(self, url, token, method, naming=None, archived=None, includes=[],
self.include_shared = include_shared
self.use_fetch = use_fetch
self.hide_token = hide_token
self.user_projects = user_projects
self.group_search = group_search

@staticmethod
Expand Down Expand Up @@ -160,12 +160,26 @@ def load_file_tree(self):
dct = yaml.safe_load(stream)
self.root = DictImporter().import_(dct)

def load_user_tree(self):
log.debug(f"Starting user project search with archived: {self.archived}")
self.gitlab.auth()
user = self.gitlab.users.get(self.gitlab.user.id)
username = user.username
projects = user.projects.list(as_list=False, archived=self.archived, get_all=True)
self.progress.init_progress(len(projects))
root = self.make_node("group", f"{username}-prsonal-projects", self.root, url=f"{self.url}/users/{username}/projects")
self.add_projects(root, projects)


def load_tree(self):
if self.in_file:
log.debug("Loading tree from file [%s]", self.in_file)
self.load_file_tree()
elif self.user_projects:
log.debug("Loading user personal projects from gitlab server [%s]", self.url)
self.load_user_tree()
else:
log.debug("Loading projects tree gitlab server [%s]", self.url)
log.debug("Loading projects tree from gitlab server [%s]", self.url)
self.load_gitlab_tree()

log.debug("Fetched root node with [%d] projects" % len(
Expand Down
8 changes: 4 additions & 4 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_args_version():
def test_args_logging(mock_tree, mock_log, mock_os, mock_sys, mock_logging):
args_mock = mock.Mock()
args_mock.return_value = Node(
type="test", name="test", version=None, verbose=True, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.PATH, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=None, dest=".", include_shared=True, use_fetch=None, hide_token=None, group_search=None)
type="test", name="test", version=None, verbose=True, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.PATH, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=None, dest=".", include_shared=True, use_fetch=None, hide_token=None, user_projects=None, group_search=None)
cli.parse_args = args_mock

mock_streamhandler = mock.Mock()
Expand All @@ -57,7 +57,7 @@ def test_args_include(mock_tree):
exc_groups = "/exc**,/exc**"
args_mock = mock.Mock()
args_mock.return_value = Node(
type="test", name="test", version=None, debug=None, include=inc_groups, exclude=exc_groups, url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=None, dest=".", use_fetch=None, hide_token=None, group_search=None)
type="test", name="test", version=None, debug=None, include=inc_groups, exclude=exc_groups, url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=None, dest=".", use_fetch=None, hide_token=None, user_projects=None, group_search=None)
cli.parse_args = args_mock

split_mock = mock.Mock()
Expand All @@ -73,7 +73,7 @@ def test_args_include(mock_tree):
def test_args_include(mock_tree):
args_mock = mock.Mock()
args_mock.return_value = Node(
type="test", name="test", version=None, verbose=None, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=True, dest=".", print_format=PrintFormat.YAML, include_shared=True, use_fetch=None, hide_token=None, group_search=None)
type="test", name="test", version=None, verbose=None, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=True, dest=".", print_format=PrintFormat.YAML, include_shared=True, use_fetch=None, hide_token=None, user_projects=None, group_search=None)
cli.parse_args = args_mock

print_tree_mock = mock.Mock()
Expand Down Expand Up @@ -116,7 +116,7 @@ def test_missing_url(mock_tree):
def test_empty_tree(mock_tree):
args_mock = mock.Mock()
args_mock.return_value = Node(
type="test", name="test", version=None, verbose=None, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=True, dest=".", include_shared=True, use_fetch=None, hide_token=None, group_search=None)
type="test", name="test", version=None, verbose=None, include="", exclude="", url="test_url", token="test_token", method=CloneMethod.SSH, naming=FolderNaming.NAME, archived=ArchivedResults.INCLUDE, file=None, concurrency=1, recursive=False, disble_progress=True, print=True, dest=".", include_shared=True, use_fetch=None, hide_token=None, user_projects=None, group_search=None)
cli.parse_args = args_mock

with pytest.raises(SystemExit):
Expand Down
11 changes: 10 additions & 1 deletion tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,20 @@ def test_clone_subgroup_naming_path():
@pytest.mark.slow_integration_test
def test_large_groups():
os.environ['GITLAB_URL'] = 'https://gitlab.com/'
output = io_util.execute(['-p', '--print-format', 'json', '-n', 'path', '--group-search', 'large-group-test', '--verbose'], 60)
output = io_util.execute(['-p', '--print-format', 'json', '-n', 'path', '--group-search', 'large-group-test'], 60)
obj = json.loads(output)
assert obj['children'][0]['name'] == 'large-group-test'
assert obj['children'][0]['children'][0]['name'] == 'many-subgroups'
assert len(obj['children'][0]['children'][0]['children']) == 21
assert obj['children'][0]['children'][1]['name'] == 'gitlab-many-projects'
assert len(obj['children'][0]['children'][1]['children']) == 21


@pytest.mark.slow_integration_test
def test_user_personal_projects():
os.environ['GITLAB_URL'] = 'https://gitlab.com/'
output = io_util.execute(['-p', '--print-format', 'json', '-n', 'path', '--user-projects'], 60)
obj = json.loads(output)
assert obj['children'][0]['name'] == 'erezmazor-prsonal-projects'
assert obj['children'][0]['children'][0]['name'] == 'gitlabber-personal-project'