Finished project migrations, also added exit handler

This commit is contained in:
Enrico Ludwig 2024-07-19 09:12:43 +02:00
parent 66db60e2ef
commit 603f39bc6f

View File

@ -25,9 +25,9 @@
# --no-update-existing-users Do not update existing users on Gitea (default: False)
# --no-update-existing-projects Do not update existing projects on Gitea (default: False)
#
# --include-wiki Include wiki repositories (default: False) - not implemented yet
# --include-issues Include issues repositories (default: False) - not implemented yet
# --include-merge-requests Include merge requests repositories (default: False) - not implemented yet
# --include-wiki Include wiki repositories (default: False)
# --include-issues Include issues repositories (default: False)
# --include-merge-requests Include merge requests repositories (default: False)
#
# --override-groups Override existing groups on Gitea (default: False) - not implemented yet
# --override-users Override existing users on Gitea (default: False) - not implemented yet
@ -98,11 +98,18 @@ LOG_FILE = "gitlab2gitea.log"
APPEND_LOG = False
QUIET = False
### DEBUG - REMOVE LATER
ONLY_ONE_PROJECT = False
### END DEBUG
# Internal variables - Do not change
APP_NAME = "GitLab2Gitea"
APP_VERSION = "1.0"
APP_AUTHOR = "Zion Networks"
EXIT_REQUESTED = False
GITEA_RESERVED_USERNAMES = ["ghost", "notifications"]
GITEA_RESERVED_ORGANAMES = [
"api",
@ -155,6 +162,7 @@ import argparse
import requests
import traceback
import json
import signal
# Set cwd to script directory
@ -776,6 +784,9 @@ def get_gitlab_projects() -> list:
projects = []
while next_page_link is not None:
if EXIT_REQUESTED:
return
_debug(f'REQUEST: GET {next_page_link.split("?")[0]}')
response = requests.get(
next_page_link,
@ -819,7 +830,7 @@ def migrate_gitlab_project_to_gitea(gitlab_project: dict):
response = requests.post(
f"{GITEA_URL}/api/{GITEA_API_VERSION}/repos/migrate",
json={
"auth_token": GITEA_TOKEN,
"auth_token": GITLAB_TOKEN,
"repo_name": gitlab_project["path"],
"repo_owner": (
gitlab_project["namespace"]["path"]
@ -828,7 +839,7 @@ def migrate_gitlab_project_to_gitea(gitlab_project: dict):
),
"description": gitlab_project["description"],
"clone_addr": gitlab_project["http_url_to_repo"],
"service": "gitlab",
"service": "git",
"issues": INCLUDE_ISSUES,
"pull_requests": INCLUDE_MERGE_REQUESTS,
"wiki": INCLUDE_WIKI,
@ -1193,17 +1204,26 @@ def get_gitea_user(username: str) -> dict:
def remap_keys(d, mapping):
def nested_get(d, keys):
for key in keys.split("/"):
if EXIT_REQUESTED:
return
d = d[key]
return d
def nested_set(d, keys, value):
keys = keys.split("/")
for key in keys[:-1]:
if EXIT_REQUESTED:
return
d = d.setdefault(key, {})
d[keys[-1]] = value
remapped = {}
for k, v in d.items():
if EXIT_REQUESTED:
return
if k in mapping:
nested_set(remapped, mapping[k], v)
elif isinstance(v, dict):
@ -1223,6 +1243,9 @@ def remap_keys(d, mapping):
def is_ignored(path_to_check, ignore_dict):
def nested_get(d, keys):
for key in keys:
if EXIT_REQUESTED:
return
if key in d:
d = d[key]
else:
@ -1250,6 +1273,9 @@ def cmp_dicts(
all_keys = all_keys.intersection(to_check.keys())
for key in all_keys:
if EXIT_REQUESTED:
return
current_path = path + [key]
if ignore and is_ignored(current_path, ignore):
continue
@ -1303,10 +1329,16 @@ def cmp_gitlab_gitea_groups(gitlab_groups: list, gitea_groups: list) -> dict:
missing_matches = 0
for gitlab_group in gitlab_groups:
if EXIT_REQUESTED:
return
name = gitlab_group["path"]
exists = False
for gitea_group in gitea_groups:
if EXIT_REQUESTED:
return
if name == gitea_group["name"]:
exists = True
break
@ -1320,10 +1352,16 @@ def cmp_gitlab_gitea_groups(gitlab_groups: list, gitea_groups: list) -> dict:
compare_result[name] = 0 if exists else 1
for gitea_group in gitea_groups:
if EXIT_REQUESTED:
return
name = gitea_group["name"]
exists = False
for gitlab_group in gitlab_groups:
if EXIT_REQUESTED:
return
if name == gitlab_group["path"]:
exists = True
break
@ -1341,10 +1379,16 @@ def cmp_gitlab_gitea_users(gitlab_users: list, gitea_users: list) -> dict:
missing_matches = 0
for gitlab_user in gitlab_users:
if EXIT_REQUESTED:
return
name = gitlab_user["username"]
exists = False
for gitea_user in gitea_users:
if EXIT_REQUESTED:
return
if name == gitea_user["login"]:
exists = True
break
@ -1358,10 +1402,16 @@ def cmp_gitlab_gitea_users(gitlab_users: list, gitea_users: list) -> dict:
compare_result[name] = 0 if exists else 1
for gitea_user in gitea_users:
if EXIT_REQUESTED:
return
name = gitea_user["login"]
exists = False
for gitlab_user in gitlab_users:
if EXIT_REQUESTED:
return
if name == gitlab_user["username"]:
exists = True
break
@ -1379,11 +1429,17 @@ def cmp_gitlab_gitea_projects(gitlab_projects: list, gitea_projects: list) -> di
missing_matches = 0
for gitlab_project in gitlab_projects:
if EXIT_REQUESTED:
return
exists = False
name = gitlab_project["path"]
owner = gitlab_project["namespace"]["path"]
for gitea_project in gitea_projects["data"]:
if EXIT_REQUESTED:
return
# fmt: off
if (name == gitea_project["name"] and owner == gitea_project["owner"]["username"]):
exists = True
@ -1398,11 +1454,17 @@ def cmp_gitlab_gitea_projects(gitlab_projects: list, gitea_projects: list) -> di
compare_result[name] = 0 if exists else 1
for gitea_project in gitea_projects["data"]:
if EXIT_REQUESTED:
return
exists = False
name = gitea_project["name"]
owner = gitea_project["owner"]["username"]
for gitlab_project in gitlab_projects:
if EXIT_REQUESTED:
return
# fmt: off
if name == gitlab_project["path"] and owner == gitlab_project["namespace"]["path"]:
exists = True
@ -1419,10 +1481,16 @@ def cmp_gitlab_gitea_projects(gitlab_projects: list, gitea_projects: list) -> di
def create_missing_groups(gitlab_groups: list, gitea_groups: list):
for gitlab_group in gitlab_groups:
if EXIT_REQUESTED:
return
name = gitlab_group["path"]
exists = False
for gitea_group in gitea_groups:
if EXIT_REQUESTED:
return
if name == gitea_group["name"]:
exists = True
break
@ -1444,10 +1512,16 @@ def create_missing_groups(gitlab_groups: list, gitea_groups: list):
def create_missing_users(gitlab_users: list, gitea_users: list):
for gitlab_user in gitlab_users:
if EXIT_REQUESTED:
return
name = gitlab_user["username"]
exists = False
for gitea_user in gitea_users["data"]:
if EXIT_REQUESTED:
return
if name == gitea_user["login"]:
exists = True
break
@ -1479,11 +1553,17 @@ def create_missing_users(gitlab_users: list, gitea_users: list):
def create_missing_projects(gitlab_projects: list, gitea_projects: list):
for gitlab_project in gitlab_projects:
if EXIT_REQUESTED:
return
exists = False
name = gitlab_project["path"]
group = gitlab_project["namespace"]["path"]
for gitea_project in gitea_projects["data"]:
if EXIT_REQUESTED:
return
if (
name == gitea_project["name"]
and group == gitea_project["owner"]["username"]
@ -1508,24 +1588,30 @@ def create_missing_projects(gitlab_projects: list, gitea_projects: list):
_info(f'Migrating Gitlab project "{name}" to Gitea...')
migrate_gitlab_project_to_gitea(gitlab_project)
if DEBUG:
if ONLY_ONE_PROJECT:
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
break
except Exception as e:
_exception(f'Failed to create Gitea project "{name}": {e}', e)
if DEBUG:
if ONLY_ONE_PROJECT:
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
break
def update_existing_groups(gitlab_groups: list, gitea_groups: list):
for gitlab_group in gitlab_groups:
if EXIT_REQUESTED:
return
name = gitlab_group["path"]
exists = False
for gitea_group in gitea_groups:
if EXIT_REQUESTED:
return
if name == gitea_group["name"]:
exists = True
break
@ -1555,10 +1641,16 @@ def update_existing_groups(gitlab_groups: list, gitea_groups: list):
def update_existing_users(gitlab_users: list, gitea_users: list):
for gitlab_user in gitlab_users:
if EXIT_REQUESTED:
return
name = gitlab_user["username"]
exists = False
for gitea_user in gitea_users["data"]:
if EXIT_REQUESTED:
return
if name == gitea_user["login"]:
exists = True
break
@ -1591,11 +1683,17 @@ def update_existing_projects(gitlab_projects: list, gitea_projects: list):
# update existing projects
for gitlab_project in gitlab_projects:
if EXIT_REQUESTED:
return
exists = False
name = gitlab_project["path"]
group = gitlab_project["namespace"]["path"]
for gitea_project in gitea_projects["data"]:
if EXIT_REQUESTED:
return
if (
name == gitea_project["name"]
and group == gitea_project["owner"]["username"]
@ -1622,14 +1720,14 @@ def update_existing_projects(gitlab_projects: list, gitea_projects: list):
else:
update_gitea_project(gitlab_project)
if DEBUG:
if ONLY_ONE_PROJECT:
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
break
except Exception as e:
_exception(f'Failed to update Gitea project "{name}": {e}', e)
if DEBUG:
if ONLY_ONE_PROJECT:
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
break
else:
@ -1693,6 +1791,10 @@ def migrate_groups():
if not DRY_RUN:
try:
create_missing_groups(gitlab_groups, gitea_groups)
if EXIT_REQUESTED:
return
except Exception as e:
_exception(f"Failed to create missing groups: {e}", e)
else:
@ -1706,6 +1808,10 @@ def migrate_groups():
try:
if not DRY_RUN:
update_existing_groups(gitlab_groups, gitea_groups)
if EXIT_REQUESTED:
return
else:
_warn(
"Dry-run mode enabled, skipping update of existing groups on Gitea..."
@ -1739,6 +1845,10 @@ def migrate_users():
if not DRY_RUN:
create_missing_users(gitlab_users, gitea_users)
if EXIT_REQUESTED:
return
else:
_warn(
"Dry-run mode enabled, skipping creation of missing users on Gitea..."
@ -1750,6 +1860,10 @@ def migrate_users():
try:
if not DRY_RUN:
update_existing_users(gitlab_users, gitea_users)
if EXIT_REQUESTED:
return
else:
_warn(
"Dry-run mode enabled, skipping update of existing users on Gitea..."
@ -1764,15 +1878,28 @@ def migrate_projects():
_warn("EXISTING PROJECTS WILL BE OVERRIDDEN!")
gitlab_projects = get_gitlab_projects()
if EXIT_REQUESTED:
return
gitea_projects = get_gitea_projects()
if EXIT_REQUESTED:
return
# dump the projects to json files
with open("gitlab_projects.json", "w") as f:
json.dump(gitlab_projects, f, indent=4)
if EXIT_REQUESTED:
return
with open("gitea_projects.json", "w") as f:
json.dump(gitea_projects, f, indent=4)
if EXIT_REQUESTED:
return
_info(f"Projects on GitLab: {len(gitlab_projects)}")
_trace(f"Projects on GitLab: {gitlab_projects}")
_info(f"Projects on Gitea: {len(gitea_projects)}")
@ -1782,6 +1909,9 @@ def migrate_projects():
project_result, missing_matches = cmp_gitlab_gitea_projects(
gitlab_projects, gitea_projects
)
if EXIT_REQUESTED:
return
except Exception as e:
_exception(f"Failed to compare GitLab and Gitea projects: {e}", e)
return
@ -1794,6 +1924,9 @@ def migrate_projects():
if not DRY_RUN:
create_missing_projects(gitlab_projects, gitea_projects)
if EXIT_REQUESTED:
return
else:
_warn(
"Dry-run mode enabled, skipping creation of missing projects on Gitea..."
@ -1805,6 +1938,9 @@ def migrate_projects():
try:
if not DRY_RUN:
update_existing_projects(gitlab_projects, gitea_projects)
if EXIT_REQUESTED:
return
else:
_warn(
"Dry-run mode enabled, skipping update of existing projects on Gitea..."
@ -1816,18 +1952,27 @@ def migrate_projects():
def run_migration():
if ONLY_GROUPS:
_warn("Skipping users!")
_warn("Skipping projects!")
_info("Migrating GitLab groups...")
migrate_groups()
_info("Group migration completed!")
return
elif ONLY_USERS:
_warn("Skipping groups!")
_warn("Skipping projects!")
_info("Migrating GitLab users...")
migrate_users()
_info("User migration completed!")
return
elif ONLY_PROJECTS:
_warn("Skipping groups!")
_warn("Skipping users!")
_info("Migrating GitLab projects...")
migrate_projects()
_info("Project migration completed!")
@ -1838,16 +1983,24 @@ def run_migration():
migrate_groups()
_info("Group migration completed!")
if EXIT_REQUESTED:
return
_info("Migrating GitLab users...")
migrate_users()
_info("User migration completed!")
if EXIT_REQUESTED:
return
_info("Migrating GitLab projects...")
migrate_projects()
_info("Project migration completed!")
def main():
signal.signal(signal.SIGINT, signal_handler)
header_info = f"{APP_NAME} v{APP_VERSION} - by {APP_AUTHOR}"
_info(f"{header_info}")
@ -1896,5 +2049,19 @@ def main():
_exception(f"An error occurred: {e}", e)
def signal_handler(sig, frame):
global EXIT_REQUESTED
if EXIT_REQUESTED:
_error("EXITING IMMEDIATELY!")
try:
sys.exit(1)
except SystemExit:
os._exit(1)
else:
_warn("EXIT REQUESTED! Please wait...")
EXIT_REQUESTED = True
if __name__ == "__main__":
main()