Skip to content

Instantly share code, notes, and snippets.

@internaut
Created February 22, 2022 09:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save internaut/a62142050d00667476f9d07a9fa6980c to your computer and use it in GitHub Desktop.
Save internaut/a62142050d00667476f9d07a9fa6980c to your computer and use it in GitHub Desktop.
Transfer a user's GitLab projects to a new group.
"""
Transfer all GitLab projects from the user authenticated with a supplied private access token (PAT) to a new
namespace (i.e. a group with a group ID).
To generate a PAT, log in to your GitLab account and go to "User settings > Access tokens".
To find out the ID of a group to which you want to transfer the projects, go to the group's page. The group ID is shown
under the title of the group.
Requirements: Python 3 with requests package installed (tested with Python 3.8 and requests 2.27.1).
Run script as:
python transfer.py <host address> <personal access token of user> <target namespace ID> [noninteractive]
The "host address" argument is the domain of the host where the GitLab instance is running, e.g. "gitlab.example.com".
If the "noninteractive" option is given, the script runs without asking for confirmation.
Date: February 2022
Author: Markus Konrad <markus.konrad@wzb.eu>
"""
import sys
import requests
SCHEME = 'https://'
API = 'api/v4'
if len(sys.argv) < 4:
print('required arguments: host address, personal access token of user, target namespace ID')
exit(1)
else:
host = sys.argv[1]
pat = sys.argv[2]
target_namespace = int(sys.argv[3])
interactive = len(sys.argv) <= 4 or sys.argv[4] != 'noninteractive'
def call_api(endpoint, method='get', page=None, **kwargs):
if page:
kwargs['page'] = page
urlparams = '&'.join([f'{k}={v}' for k, v in kwargs.items()])
url = f'{SCHEME}{host}/{API}/{endpoint}?{urlparams}'
resp = requests.request(method, url, headers={'PRIVATE-TOKEN': pat})
if resp.ok:
return resp.json()
else:
raise IOError(f'error requesting URL {url} with method "{method}": {resp.status_code}')
print('fetching user information')
userinfo = call_api('user')
print(f'collecting projects for user {userinfo["username"]}')
page = 1
gotdata = True
projects = {}
while gotdata:
print(f'> page {page}')
page_projects = call_api(f'users/{userinfo["id"]}/projects', page=page)
projects.update({p['id']: p['name'] for p in page_projects})
page += 1
gotdata = bool(page_projects)
if not projects:
print('no projects to transfer')
exit(0)
projects_list = '\n'.join(sorted(projects.values()))
print(f'collected {len(projects)} projects:')
print(projects_list)
if interactive:
prompt_response = input(f'\n\ntransfer these projects to namespace {target_namespace}? [y/n] >>> ')
if prompt_response.strip() != 'y':
print('skipped script')
exit(2)
print('\n\ntransferring projects')
for p_id, p_name in projects.items():
print(f'> {p_name} (ID = {p_id}) – ', end='')
transfer_resp = call_api(f'projects/{p_id}/transfer', method='put', namespace=target_namespace)
if transfer_resp.get('namespace', {}).get('id', -1) == target_namespace:
print('ok')
else:
print('failed')
print('\n\ndone')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment