Skip to content

Commit f90d642

Browse files
author
Alexander Droste
committed
initial commit
0 parents  commit f90d642

File tree

3 files changed

+99
-0
lines changed

3 files changed

+99
-0
lines changed

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Alexander Droste
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# gitlab-clone-group
2+
3+
Python script to clone all GitLab repos of a group and their subgroups while keeping the tree structure.
4+
5+
Tested with GitLab API v4.
6+
7+
## Usage
8+
9+
1. Download the gitlab-clone-group.py
10+
2. Generate a private access token with *read_api* and *read_repository* rights
11+
3. Get your group ID (displayed in light gray on below your group name)
12+
4. Run the script
13+
14+
### Example:
15+
```
16+
python3 gitlab-clone-group.py --token glabc-D-e-llaaabbbbcccccdd 12345678 .
17+
```
18+
*Clones the group 12345678 (and subgroups) into the current working directory, keeping the tree structure.*
19+
20+
### Help:
21+
```
22+
usage: gitlab-clone-group.py [-h] [--token TOKEN] [--gitlab-domain GITLAB_DOMAIN] group_id directory
23+
24+
positional arguments:
25+
group_id id of group to clone (including subgroups)
26+
directory directory to clone repos into
27+
28+
options:
29+
-h, --help show this help message and exit
30+
--token TOKEN Gitlab private access token with read_api and read_repository rights
31+
--gitlab-domain GITLAB_DOMAIN
32+
Domain of Gitlab instance to use, defaults to: gitlab.com
33+
```

gitlab-clone-group.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env python3
2+
import os
3+
import re
4+
import requests
5+
import posixpath
6+
import argparse
7+
from git import Repo
8+
9+
parser = argparse.ArgumentParser('gitlab-clone-group.py')
10+
parser.add_argument('group_id', help='id of group to clone (including subgroups)')
11+
parser.add_argument('directory', help='directory to clone repos into')
12+
parser.add_argument('--token', help='Gitlab private access token with read_api and read_repository rights')
13+
parser.add_argument('--gitlab-domain', help='Domain of Gitlab instance to use, defaults to: gitlab.com', default='gitlab.com')
14+
15+
args = parser.parse_args()
16+
17+
api_url = 'https://' + posixpath.join(args.gitlab_domain, 'api/v4/groups/', args.group_id, 'projects') + '?per_page=9999&page=1&include_subgroups=true'
18+
19+
headers = {'PRIVATE-TOKEN': args.token}
20+
res = requests.get(api_url, headers=headers)
21+
projects = res.json()
22+
23+
base_ns = os.path.commonprefix([p['namespace']['full_path'] for p in projects])
24+
print('Found %d projects in: %s' % (len(projects), base_ns))
25+
26+
abs_dir = os.path.abspath(args.directory)
27+
os.makedirs(abs_dir,exist_ok=True)
28+
29+
def get_rel_path(path):
30+
subpath = path[len(base_ns):]
31+
if (subpath.startswith('/')):
32+
subpath = subpath[1:]
33+
return posixpath.join(args.directory, subpath)
34+
35+
for p in projects:
36+
clone_dir = get_rel_path(p['namespace']['full_path'])
37+
project_path = get_rel_path(p['path_with_namespace'])
38+
print('Cloning project: %s' % project_path)
39+
if os.path.exists(project_path):
40+
print("\tProject folder already exists, skipping")
41+
else:
42+
print("\tGit url: %s" % p['ssh_url_to_repo'])
43+
os.makedirs(clone_dir, exist_ok=True)
44+
Repo.clone_from(p['ssh_url_to_repo'], project_path)
45+

0 commit comments

Comments
 (0)