Created
February 22, 2022 09:33
-
-
Save internaut/a62142050d00667476f9d07a9fa6980c to your computer and use it in GitHub Desktop.
Transfer a user's GitLab projects to a new group.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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 <[email protected]> | |
""" | |
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