Did a lot of work for project migration, fixed some stuff with user migration, optimized comparisons
This commit is contained in:
parent
14c9139718
commit
66db60e2ef
@ -25,9 +25,13 @@
|
||||
# --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
|
||||
#
|
||||
# --override-groups Override existing groups on Gitea (default: False) - not implemented yet
|
||||
# --override-users Override existing users on Gitea (default: False) - not implemented yet
|
||||
# --override-projects Override existing projects on Gitea (default: False) - not implemented yet
|
||||
# --override-projects Override existing projects on Gitea (default: False)
|
||||
#
|
||||
# --skip-empty-groups Skip empty groups (default: False) - not implemented yet
|
||||
# --skip-empty-projects Skip empty projects (default: False) - not implemented yet
|
||||
@ -73,9 +77,13 @@ NO_UPDATE_EXISTING_GROUPS = False
|
||||
NO_UPDATE_EXISTING_USERS = False
|
||||
NO_UPDATE_EXISTING_PROJECTS = False
|
||||
|
||||
OVERWRITE_EXISTING_GROUPS = False
|
||||
OVERWRITE_EXISTING_USERS = False
|
||||
OVERWRITE_EXISTING_PROJECTS = False
|
||||
INCLUDE_WIKI = False
|
||||
INCLUDE_ISSUES = False
|
||||
INCLUDE_MERGE_REQUESTS = False
|
||||
|
||||
OVERRIDE_EXISTING_GROUPS = False
|
||||
OVERRIDE_EXISTING_USERS = False
|
||||
OVERRIDE_EXISTING_PROJECTS = False
|
||||
|
||||
ONLY_GROUPS = False
|
||||
ONLY_USERS = False
|
||||
@ -92,7 +100,52 @@ QUIET = False
|
||||
|
||||
# Internal variables - Do not change
|
||||
|
||||
APP_NAME = "GitLab2Gitea"
|
||||
APP_VERSION = "1.0"
|
||||
APP_AUTHOR = "Zion Networks"
|
||||
GITEA_RESERVED_USERNAMES = ["ghost", "notifications"]
|
||||
GITEA_RESERVED_ORGANAMES = [
|
||||
"api",
|
||||
"assets",
|
||||
"attachments",
|
||||
"avatar",
|
||||
"commit",
|
||||
"commits",
|
||||
"debug",
|
||||
"error",
|
||||
"explore",
|
||||
"faq",
|
||||
"issues",
|
||||
"mail",
|
||||
"milestone",
|
||||
"new",
|
||||
"notifications",
|
||||
"org",
|
||||
"organizations",
|
||||
"plugins",
|
||||
"pull",
|
||||
"pulls",
|
||||
"repo",
|
||||
"repositories",
|
||||
"script",
|
||||
"user",
|
||||
"users",
|
||||
]
|
||||
GITEA_RESERVED_REPONAMES = [
|
||||
"api",
|
||||
"assets",
|
||||
"issues",
|
||||
"labels",
|
||||
"milestones",
|
||||
"notifications",
|
||||
"projects",
|
||||
"pr",
|
||||
"pulls",
|
||||
"repo",
|
||||
"repos",
|
||||
"settings",
|
||||
"wiki",
|
||||
]
|
||||
|
||||
# Imports
|
||||
|
||||
@ -101,6 +154,7 @@ import sys
|
||||
import argparse
|
||||
import requests
|
||||
import traceback
|
||||
import json
|
||||
|
||||
# Set cwd to script directory
|
||||
|
||||
@ -143,14 +197,23 @@ if "NO_UPDATE_EXISTING_USERS" in os.environ:
|
||||
if "NO_UPDATE_EXISTING_PROJECTS" in os.environ:
|
||||
NO_UPDATE_EXISTING_PROJECTS = bool(os.environ["NO_UPDATE_EXISTING_PROJECTS"])
|
||||
|
||||
if "INCLUDE_WIKI" in os.environ:
|
||||
INCLUDE_WIKI = bool(os.environ["INCLUDE_WIKI"])
|
||||
|
||||
if "INCLUDE_ISSUES" in os.environ:
|
||||
INCLUDE_ISSUES = bool(os.environ["INCLUDE_ISSUES"])
|
||||
|
||||
if "INCLUDE_MERGE_REQUESTS" in os.environ:
|
||||
INCLUDE_MERGE_REQUESTS = bool(os.environ["INCLUDE_MERGE_REQUESTS"])
|
||||
|
||||
if "OVERWRITE_EXISTING_GROUPS" in os.environ:
|
||||
OVERWRITE_EXISTING_GROUPS = bool(os.environ["OVERWRITE_EXISTING_GROUPS"])
|
||||
OVERRIDE_EXISTING_GROUPS = bool(os.environ["OVERRIDE_EXISTING_GROUPS"])
|
||||
|
||||
if "OVERWRITE_EXISTING_USERS" in os.environ:
|
||||
OVERWRITE_EXISTING_USERS = bool(os.environ["OVERWRITE_EXISTING_USERS"])
|
||||
OVERRIDE_EXISTING_USERS = bool(os.environ["OVERRIDE_EXISTING_USERS"])
|
||||
|
||||
if "OVERWRITE_EXISTING_PROJECTS" in os.environ:
|
||||
OVERWRITE_EXISTING_PROJECTS = bool(os.environ["OVERWRITE_EXISTING_PROJECTS"])
|
||||
OVERRIDE_EXISTING_PROJECTS = bool(os.environ["OVERRIDE_EXISTING_PROJECTS"])
|
||||
|
||||
if "ONLY_GROUPS" in os.environ:
|
||||
ONLY_GROUPS = bool(os.environ["ONLY_GROUPS"])
|
||||
@ -232,17 +295,29 @@ if os.path.exists(".env"):
|
||||
if value.lower() == "true" or value == "1":
|
||||
NO_UPDATE_EXISTING_PROJECTS = True
|
||||
|
||||
if key == "OVERWRITE_EXISTING_GROUPS":
|
||||
if key == "INCLUDE_WIKI":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERWRITE_EXISTING_GROUPS = True
|
||||
INCLUDE_WIKI = True
|
||||
|
||||
if key == "OVERWRITE_EXISTING_USERS":
|
||||
if key == "INCLUDE_ISSUES":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERWRITE_EXISTING_USERS = True
|
||||
INCLUDE_ISSUES = True
|
||||
|
||||
if key == "OVERWRITE_EXISTING_PROJECTS":
|
||||
if key == "INCLUDE_MERGE_REQUESTS":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERWRITE_EXISTING_PROJECTS = True
|
||||
INCLUDE_MERGE_REQUESTS = True
|
||||
|
||||
if key == "OVERRIDE_EXISTING_GROUPS":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERRIDE_EXISTING_GROUPS = True
|
||||
|
||||
if key == "OVERRIDE_EXISTING_USERS":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERRIDE_EXISTING_USERS = True
|
||||
|
||||
if key == "OVERRIDE_EXISTING_PROJECTS":
|
||||
if value.lower() == "true" or value == "1":
|
||||
OVERRIDE_EXISTING_PROJECTS = True
|
||||
|
||||
if key == "ONLY_GROUPS":
|
||||
if value.lower() == "true" or value == "1":
|
||||
@ -296,6 +371,9 @@ parser.add_argument("--no-create-missing-projects", help="Do not create missing
|
||||
parser.add_argument("--no-update-existing-groups", help="Do not update existing groups on Gitea", action="store_true")
|
||||
parser.add_argument("--no-update-existing-users", help="Do not update existing users on Gitea", action="store_true")
|
||||
parser.add_argument("--no-update-existing-projects", help="Do not update existing projects on Gitea", action="store_true")
|
||||
parser.add_argument("--include-wiki", help="Include wiki repositories", action="store_true")
|
||||
parser.add_argument("--include-issues", help="Include issues repositories", action="store_true")
|
||||
parser.add_argument("--include-merge-requests", help="Include merge requests repositories", action="store_true")
|
||||
parser.add_argument("--override-groups", help="Override existing groups on Gitea", action="store_true")
|
||||
parser.add_argument("--override-users", help="Override existing users on Gitea", action="store_true")
|
||||
parser.add_argument("--override-projects", help="Override existing projects on Gitea", action="store_true")
|
||||
@ -348,14 +426,23 @@ if args.no_update_existing_users:
|
||||
if args.no_update_existing_projects:
|
||||
NO_UPDATE_EXISTING_PROJECTS = True
|
||||
|
||||
if args.include_wiki:
|
||||
INCLUDE_WIKI = True
|
||||
|
||||
if args.include_issues:
|
||||
INCLUDE_ISSUES = True
|
||||
|
||||
if args.include_merge_requests:
|
||||
INCLUDE_MERGE_REQUESTS = True
|
||||
|
||||
if args.override_groups:
|
||||
OVERWRITE_EXISTING_GROUPS = True
|
||||
OVERRIDE_EXISTING_GROUPS = True
|
||||
|
||||
if args.override_users:
|
||||
OVERWRITE_EXISTING_USERS = True
|
||||
OVERRIDE_EXISTING_USERS = True
|
||||
|
||||
if args.override_projects:
|
||||
OVERWRITE_EXISTING_PROJECTS = True
|
||||
OVERRIDE_EXISTING_PROJECTS = True
|
||||
|
||||
if args.only_groups:
|
||||
ONLY_GROUPS = True
|
||||
@ -398,37 +485,37 @@ if ONLY_USERS and ONLY_PROJECTS:
|
||||
_error("Options --only-users and --only-projects are mutually exclusive!")
|
||||
sys.exit(1)
|
||||
|
||||
if NO_CREATE_MISSING_GROUPS and OVERWRITE_EXISTING_GROUPS:
|
||||
if NO_CREATE_MISSING_GROUPS and OVERRIDE_EXISTING_GROUPS:
|
||||
_error(
|
||||
"Options --no-create-missing-groups and --override-groups are mutually exclusive!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if NO_CREATE_MISSING_USERS and OVERWRITE_EXISTING_USERS:
|
||||
if NO_CREATE_MISSING_USERS and OVERRIDE_EXISTING_USERS:
|
||||
_error(
|
||||
"Options --no-create-missing-users and --override-users are mutually exclusive!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if NO_CREATE_MISSING_PROJECTS and OVERWRITE_EXISTING_PROJECTS:
|
||||
if NO_CREATE_MISSING_PROJECTS and OVERRIDE_EXISTING_PROJECTS:
|
||||
_error(
|
||||
"Options --no-create-missing-projects and --override-projects are mutually exclusive!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if NO_UPDATE_EXISTING_GROUPS and OVERWRITE_EXISTING_GROUPS:
|
||||
if NO_UPDATE_EXISTING_GROUPS and OVERRIDE_EXISTING_GROUPS:
|
||||
_error(
|
||||
"Options --no-update-existing-groups and --override-groups are mutually exclusive!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if NO_UPDATE_EXISTING_USERS and OVERWRITE_EXISTING_USERS:
|
||||
if NO_UPDATE_EXISTING_USERS and OVERRIDE_EXISTING_USERS:
|
||||
_error(
|
||||
"Options --no-update-existing-users and --override-users are mutually exclusive!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if NO_UPDATE_EXISTING_PROJECTS and OVERWRITE_EXISTING_PROJECTS:
|
||||
if NO_UPDATE_EXISTING_PROJECTS and OVERRIDE_EXISTING_PROJECTS:
|
||||
_error(
|
||||
"Options --no-update-existing-projects and --override-projects are mutually exclusive!"
|
||||
)
|
||||
@ -510,7 +597,22 @@ def _exception(exception, custom_message=None):
|
||||
|
||||
|
||||
def is_gitea_reserved_username(username: str) -> bool:
|
||||
return username in GITEA_RESERVED_USERNAMES
|
||||
return username.lower() in [name.lower() for name in GITEA_RESERVED_USERNAMES]
|
||||
|
||||
|
||||
def is_gitea_reserved_organame(organame: str) -> bool:
|
||||
return organame.lower() in [name.lower() for name in GITEA_RESERVED_ORGANAMES]
|
||||
|
||||
|
||||
def is_gitea_reserved_reponame(reponame: str) -> bool:
|
||||
return reponame.lower() in [name.lower() for name in GITEA_RESERVED_REPONAMES]
|
||||
|
||||
|
||||
def is_gitlab_project_in_subgroup(project: dict) -> bool:
|
||||
return (
|
||||
project["namespace"]["kind"] == "group"
|
||||
and project["namespace"]["parent_id"] is not None
|
||||
)
|
||||
|
||||
|
||||
def gitlab2gitea_visibility(visibility: str) -> str:
|
||||
@ -704,6 +806,63 @@ def get_gitlab_projects() -> list:
|
||||
return projects
|
||||
|
||||
|
||||
# Endpoint: POST /api/{GITLAB_API_VERSION}/repos/migrate
|
||||
def migrate_gitlab_project_to_gitea(gitlab_project: dict):
|
||||
|
||||
if not gitlab_project:
|
||||
raise Exception("GitLab project is missing!")
|
||||
|
||||
# Create Gitea project
|
||||
|
||||
_debug(f"REQUEST: POST {GITEA_URL}/api/{GITEA_API_VERSION}/repos/migrate")
|
||||
|
||||
response = requests.post(
|
||||
f"{GITEA_URL}/api/{GITEA_API_VERSION}/repos/migrate",
|
||||
json={
|
||||
"auth_token": GITEA_TOKEN,
|
||||
"repo_name": gitlab_project["path"],
|
||||
"repo_owner": (
|
||||
gitlab_project["namespace"]["path"]
|
||||
if gitlab_project["namespace"]["kind"] == "group"
|
||||
else gitlab_project["owner"]["username"]
|
||||
),
|
||||
"description": gitlab_project["description"],
|
||||
"clone_addr": gitlab_project["http_url_to_repo"],
|
||||
"service": "gitlab",
|
||||
"issues": INCLUDE_ISSUES,
|
||||
"pull_requests": INCLUDE_MERGE_REQUESTS,
|
||||
"wiki": INCLUDE_WIKI,
|
||||
"releases": True,
|
||||
"labels": True,
|
||||
"lfs": True,
|
||||
"milestones": True,
|
||||
"mirror": False,
|
||||
# fmt: off
|
||||
"private": gitlab2gitea_visibility(gitlab_project["visibility"]) == "private",
|
||||
# fmt: on
|
||||
},
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"token {GITEA_TOKEN}",
|
||||
},
|
||||
)
|
||||
|
||||
_trace(f"RESPONSE: {response.json()}")
|
||||
|
||||
if response.status_code != 201:
|
||||
response_message = (
|
||||
response.json()["message"]
|
||||
if "message" in response.json()
|
||||
else "Unknown error"
|
||||
)
|
||||
raise Exception(f"Failed to create Gitea project: {response_message}")
|
||||
else:
|
||||
project = response.json()
|
||||
|
||||
return project
|
||||
|
||||
|
||||
# Endpoint: POST /api/{GITEA_API_VERSION}/admin/users
|
||||
def migrate_gitlab_user_to_gitea(user: dict):
|
||||
|
||||
@ -921,6 +1080,87 @@ def update_gitea_org(data: dict) -> dict:
|
||||
return group
|
||||
|
||||
|
||||
# Endpoint: PATCH /api/{GITEA_API_VERSION}/admin/users/{username}
|
||||
def update_gitea_user(data: dict) -> dict:
|
||||
|
||||
if not data:
|
||||
raise Exception("Data is missing!")
|
||||
|
||||
username = data["username"]
|
||||
current_user = get_gitea_user(username)
|
||||
updated_user = convert_gitlab_user_to_gitea(data)
|
||||
|
||||
to_compare = {
|
||||
"active": True,
|
||||
"login": True,
|
||||
"login_name": True,
|
||||
"username": True,
|
||||
"email": True,
|
||||
"full_name": True,
|
||||
"admin": True,
|
||||
"prohibit_login": True,
|
||||
"restricted": True,
|
||||
"website": True,
|
||||
"visibility": True,
|
||||
}
|
||||
|
||||
mapping = {
|
||||
"is_admin": "admin",
|
||||
}
|
||||
|
||||
ignore = {"allow_create_organization": True}
|
||||
|
||||
is_different, results = cmp_dicts(
|
||||
current_user, updated_user, to_compare, mapping, ignore
|
||||
)
|
||||
|
||||
_trace(f"COMPARISON: {results}")
|
||||
|
||||
if not is_different:
|
||||
_info(f'User "{username}" is already up-to-date on Gitea')
|
||||
return current_user
|
||||
|
||||
if is_gitea_reserved_username(username):
|
||||
_warn(f'User "{username}" is a reserved username on Gitea!')
|
||||
return None
|
||||
|
||||
if not current_user:
|
||||
raise Exception(f'User "{username}" not found on Gitea!')
|
||||
|
||||
_debug(f"REQUEST: PATCH {GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}")
|
||||
|
||||
# TODO seems to have issues, since users do not get updated properly
|
||||
response = requests.patch(
|
||||
f"{GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}",
|
||||
json=updated_user,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"token {GITEA_TOKEN}",
|
||||
},
|
||||
)
|
||||
|
||||
_trace(f"RESPONSE: {response.json()}")
|
||||
|
||||
if response.status_code != 200:
|
||||
response_message = (
|
||||
response.json()["message"]
|
||||
if "message" in response.json()
|
||||
else "Unknown error"
|
||||
)
|
||||
raise Exception(f"Failed to update Gitea user: {response_message}")
|
||||
else:
|
||||
user = response.json()
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def update_gitea_project(data: dict) -> dict:
|
||||
|
||||
_warn("Function update_gitea_project is not implemented yet!")
|
||||
pass
|
||||
|
||||
|
||||
# Endpoint: GET /api/{GITEA_API_VERSION}/users/{username}
|
||||
def get_gitea_user(username: str) -> dict:
|
||||
|
||||
@ -950,68 +1190,86 @@ def get_gitea_user(username: str) -> dict:
|
||||
return user
|
||||
|
||||
|
||||
# Endpoint: PATCH /api/{GITEA_API_VERSION}/admin/users/{username}
|
||||
def update_gitea_user(data: dict) -> dict:
|
||||
def remap_keys(d, mapping):
|
||||
def nested_get(d, keys):
|
||||
for key in keys.split("/"):
|
||||
d = d[key]
|
||||
return d
|
||||
|
||||
if not data:
|
||||
raise Exception("Data is missing!")
|
||||
def nested_set(d, keys, value):
|
||||
keys = keys.split("/")
|
||||
for key in keys[:-1]:
|
||||
d = d.setdefault(key, {})
|
||||
d[keys[-1]] = value
|
||||
|
||||
username = data["username"]
|
||||
current_user = get_gitea_user(username)
|
||||
updated_user = convert_gitlab_user_to_gitea(data, current_user)
|
||||
|
||||
if is_gitea_reserved_username(username):
|
||||
_warn(f'User "{username}" is a reserved username on Gitea!')
|
||||
return None
|
||||
|
||||
if not current_user:
|
||||
raise Exception(f'User "{username}" not found on Gitea!')
|
||||
|
||||
_debug(f"REQUEST: PATCH {GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}")
|
||||
|
||||
response = requests.patch(
|
||||
f"{GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}",
|
||||
json=updated_user,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"token {GITEA_TOKEN}",
|
||||
remapped = {}
|
||||
for k, v in d.items():
|
||||
if k in mapping:
|
||||
nested_set(remapped, mapping[k], v)
|
||||
elif isinstance(v, dict):
|
||||
remapped[k] = remap_keys(
|
||||
v,
|
||||
{
|
||||
f"{k}/{subkey}": subvalue
|
||||
for subkey, subvalue in mapping.items()
|
||||
if subkey.startswith(f"{k}/")
|
||||
},
|
||||
)
|
||||
|
||||
_trace(f"RESPONSE: {response.json()}")
|
||||
|
||||
if response.status_code != 200:
|
||||
response_message = (
|
||||
response.json()["message"]
|
||||
if "message" in response.json()
|
||||
else "Unknown error"
|
||||
)
|
||||
raise Exception(f"Failed to update Gitea user: {response_message}")
|
||||
else:
|
||||
user = response.json()
|
||||
|
||||
return user
|
||||
remapped[k] = v
|
||||
return remapped
|
||||
|
||||
|
||||
def cmp_gitea_userdata(userdata_a: dict, userdata_b: dict) -> bool:
|
||||
def is_ignored(path_to_check, ignore_dict):
|
||||
def nested_get(d, keys):
|
||||
for key in keys:
|
||||
if key in d:
|
||||
d = d[key]
|
||||
else:
|
||||
return False
|
||||
return True if isinstance(d, dict) and d else False
|
||||
|
||||
return nested_get(ignore_dict, path_to_check)
|
||||
|
||||
|
||||
def cmp_dicts(
|
||||
dict_a: dict,
|
||||
dict_b: dict,
|
||||
check: dict = None,
|
||||
mapping: dict = None,
|
||||
ignore: dict = None,
|
||||
) -> bool:
|
||||
result = {}
|
||||
|
||||
for key in userdata_a:
|
||||
if key in userdata_b:
|
||||
if userdata_a[key] != userdata_b[key]:
|
||||
result[key] = True
|
||||
else:
|
||||
result[key] = False
|
||||
else:
|
||||
result[key] = True
|
||||
if mapping:
|
||||
dict_a = remap_keys(dict_a, mapping)
|
||||
|
||||
has_changes = False
|
||||
for key in result:
|
||||
if result[key]:
|
||||
has_changes = True
|
||||
break
|
||||
def compare_dicts(a, b, to_check, path=[]):
|
||||
all_keys = set(a.keys()).union(b.keys())
|
||||
if to_check is not None:
|
||||
all_keys = all_keys.intersection(to_check.keys())
|
||||
|
||||
for key in all_keys:
|
||||
current_path = path + [key]
|
||||
if ignore and is_ignored(current_path, ignore):
|
||||
continue
|
||||
|
||||
if key in a and key in b:
|
||||
if (
|
||||
isinstance(a[key], dict)
|
||||
and isinstance(b[key], dict)
|
||||
and to_check
|
||||
and key in to_check
|
||||
):
|
||||
compare_dicts(a[key], b[key], to_check[key], current_path)
|
||||
else:
|
||||
result[tuple(current_path)] = a[key] != b[key]
|
||||
else:
|
||||
result[tuple(current_path)] = True
|
||||
|
||||
compare_dicts(dict_a, dict_b, check)
|
||||
|
||||
has_changes = any(result.values())
|
||||
|
||||
return has_changes, result
|
||||
|
||||
@ -1020,15 +1278,17 @@ def convert_gitlab_user_to_gitea(user: dict, extra_data: dict = None) -> dict:
|
||||
|
||||
gitea_user = {
|
||||
"active": user["state"] == "active",
|
||||
"login": user["username"],
|
||||
"login_name": user["username"],
|
||||
"avatar_url": user["avatar_url"],
|
||||
"created": user["created_at"],
|
||||
"description": user["bio"],
|
||||
"username": user["username"],
|
||||
"email": user["email"],
|
||||
"full_name": user["name"],
|
||||
"is_admin": user["is_admin"],
|
||||
"admin": user["is_admin"],
|
||||
"prohibit_login": user["state"] == "blocked" or user["locked"],
|
||||
"restricted": user["state"] == "blocked" or user["locked"],
|
||||
"website": user["website_url"],
|
||||
"visibility": "private" if user["private_profile"] else "public",
|
||||
"allow_create_organization": user["can_create_group"],
|
||||
}
|
||||
|
||||
if extra_data:
|
||||
@ -1115,7 +1375,45 @@ def cmp_gitlab_gitea_users(gitlab_users: list, gitea_users: list) -> dict:
|
||||
|
||||
def cmp_gitlab_gitea_projects(gitlab_projects: list, gitea_projects: list) -> dict:
|
||||
|
||||
return {}, 0
|
||||
compare_result = {}
|
||||
missing_matches = 0
|
||||
|
||||
for gitlab_project in gitlab_projects:
|
||||
exists = False
|
||||
name = gitlab_project["path"]
|
||||
owner = gitlab_project["namespace"]["path"]
|
||||
|
||||
for gitea_project in gitea_projects["data"]:
|
||||
# fmt: off
|
||||
if (name == gitea_project["name"] and owner == gitea_project["owner"]["username"]):
|
||||
exists = True
|
||||
break
|
||||
|
||||
if exists:
|
||||
_info(f'GITLAB: Project "{owner}/{name}" exists on both GitLab and Gitea')
|
||||
else:
|
||||
_warn(f'GITLAB: Project "{owner}/{name}" exists on GitLab only')
|
||||
missing_matches += 1
|
||||
|
||||
compare_result[name] = 0 if exists else 1
|
||||
|
||||
for gitea_project in gitea_projects["data"]:
|
||||
exists = False
|
||||
name = gitea_project["name"]
|
||||
owner = gitea_project["owner"]["username"]
|
||||
|
||||
for gitlab_project in gitlab_projects:
|
||||
# fmt: off
|
||||
if name == gitlab_project["path"] and owner == gitlab_project["namespace"]["path"]:
|
||||
exists = True
|
||||
break
|
||||
# fmt: on
|
||||
|
||||
if not exists:
|
||||
_warn(f'GITEA: Project "{owner}/{name}" exists on Gitea only')
|
||||
compare_result[name] = 2
|
||||
|
||||
return compare_result, missing_matches
|
||||
|
||||
|
||||
def create_missing_groups(gitlab_groups: list, gitea_groups: list):
|
||||
@ -1129,10 +1427,15 @@ def create_missing_groups(gitlab_groups: list, gitea_groups: list):
|
||||
exists = True
|
||||
break
|
||||
|
||||
if is_gitea_reserved_organame(name):
|
||||
_warn(f'Skipping group "{name}": Group name is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if not exists:
|
||||
_info(f'Creating missing group "{name}" on Gitea...')
|
||||
|
||||
try:
|
||||
_info(f'Migrating Gitlab group "{name}" to Gitea...')
|
||||
migrate_gitlab_group_to_gitea(gitlab_group)
|
||||
except Exception as e:
|
||||
_exception(f'Failed to create Gitea group "{name}": {e}', e)
|
||||
@ -1160,20 +1463,61 @@ def create_missing_users(gitlab_users: list, gitea_users: list):
|
||||
continue
|
||||
|
||||
if is_gitea_reserved_username(name):
|
||||
_warn(f'User "{name}" is a reserved username on Gitea!')
|
||||
_warn(f'Skipping user "{name}": Username is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if not exists:
|
||||
_info(f'Creating missing user "{name}" on Gitea...')
|
||||
|
||||
try:
|
||||
_info(f'Migrating Gitlab user "{name}" to Gitea...')
|
||||
migrate_gitlab_user_to_gitea(gitlab_user)
|
||||
except Exception as e:
|
||||
_exception(f'Failed to create Gitea user "{name}": {e}', e)
|
||||
|
||||
|
||||
def create_missing_projects(gitlab_projects: list, gitea_projects: list):
|
||||
pass
|
||||
|
||||
for gitlab_project in gitlab_projects:
|
||||
exists = False
|
||||
name = gitlab_project["path"]
|
||||
group = gitlab_project["namespace"]["path"]
|
||||
|
||||
for gitea_project in gitea_projects["data"]:
|
||||
if (
|
||||
name == gitea_project["name"]
|
||||
and group == gitea_project["owner"]["username"]
|
||||
):
|
||||
exists = True
|
||||
break
|
||||
|
||||
if is_gitea_reserved_reponame(name):
|
||||
_warn(f'Skipping project "{name}": Project name is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if is_gitlab_project_in_subgroup(gitlab_project):
|
||||
_warn(
|
||||
f'Skipping project "{name}": Project is in a subgroup and not supported by Gitea!'
|
||||
)
|
||||
continue
|
||||
|
||||
if not exists:
|
||||
_info(f'Creating missing project "{name}" on Gitea...')
|
||||
|
||||
try:
|
||||
_info(f'Migrating Gitlab project "{name}" to Gitea...')
|
||||
migrate_gitlab_project_to_gitea(gitlab_project)
|
||||
|
||||
if DEBUG:
|
||||
_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:
|
||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||
break
|
||||
|
||||
|
||||
def update_existing_groups(gitlab_groups: list, gitea_groups: list):
|
||||
@ -1186,10 +1530,13 @@ def update_existing_groups(gitlab_groups: list, gitea_groups: list):
|
||||
exists = True
|
||||
break
|
||||
|
||||
if exists:
|
||||
_info(f'Updating existing group "{name}" on Gitea...')
|
||||
if is_gitea_reserved_organame(name):
|
||||
_warn(f'Skipping group "{name}": Group name is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if exists:
|
||||
try:
|
||||
_info(f'Updating existing group "{name}" on Gitea...')
|
||||
update_gitea_org(
|
||||
{
|
||||
"path": gitlab_group["path"],
|
||||
@ -1226,9 +1573,13 @@ def update_existing_users(gitlab_users: list, gitea_users: list):
|
||||
_warn(f'User "{name}" does not have a username and will not be updated!')
|
||||
continue
|
||||
|
||||
if is_gitea_reserved_username(name):
|
||||
_warn(f'Skipping user "{name}": Username is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if exists:
|
||||
_info(f'Updating existing user "{name}" on Gitea...')
|
||||
try:
|
||||
_info(f'Updating existing user "{name}" on Gitea...')
|
||||
update_gitea_user(gitlab_user)
|
||||
except Exception as e:
|
||||
_exception(f'Failed to update Gitea user "{name}": {e}', e)
|
||||
@ -1237,10 +1588,86 @@ def update_existing_users(gitlab_users: list, gitea_users: list):
|
||||
|
||||
|
||||
def update_existing_projects(gitlab_projects: list, gitea_projects: list):
|
||||
pass
|
||||
|
||||
# update existing projects
|
||||
for gitlab_project in gitlab_projects:
|
||||
exists = False
|
||||
name = gitlab_project["path"]
|
||||
group = gitlab_project["namespace"]["path"]
|
||||
|
||||
for gitea_project in gitea_projects["data"]:
|
||||
if (
|
||||
name == gitea_project["name"]
|
||||
and group == gitea_project["owner"]["username"]
|
||||
):
|
||||
exists = True
|
||||
break
|
||||
|
||||
if is_gitea_reserved_reponame(name):
|
||||
_warn(f'Skipping project "{name}": Project name is reserved on Gitea!')
|
||||
continue
|
||||
|
||||
if is_gitlab_project_in_subgroup(gitlab_project):
|
||||
_warn(
|
||||
f'Skipping project "{name}": Project is in a subgroup and not supported by Gitea!'
|
||||
)
|
||||
continue
|
||||
|
||||
if exists:
|
||||
try:
|
||||
_info(f'Updating existing project "{name}" on Gitea...')
|
||||
if OVERRIDE_EXISTING_PROJECTS:
|
||||
delete_gitea_project(gitea_project)
|
||||
migrate_gitlab_project_to_gitea(gitlab_project)
|
||||
else:
|
||||
update_gitea_project(gitlab_project)
|
||||
|
||||
if DEBUG:
|
||||
_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:
|
||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||
break
|
||||
else:
|
||||
_warn(f'Project "{name}" does not exist on Gitea!')
|
||||
|
||||
|
||||
# ENDPOINT: DELETE /api/{GITEA_API_VERSION}/repos/{owner}/{repo}
|
||||
def delete_gitea_project(project: dict):
|
||||
|
||||
owner = project["owner"]["username"]
|
||||
repo = project["name"]
|
||||
|
||||
_debug(f"REQUEST: DELETE {GITEA_URL}/api/{GITEA_API_VERSION}/repos/{owner}/{repo}")
|
||||
|
||||
response = requests.delete(
|
||||
f"{GITEA_URL}/api/{GITEA_API_VERSION}/repos/{owner}/{repo}",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"Authorization": f"token {GITEA_TOKEN}",
|
||||
},
|
||||
)
|
||||
|
||||
_trace(f"RESPONSE: {response.json()}")
|
||||
|
||||
if response.status_code != 204:
|
||||
response_message = (
|
||||
response.json()["message"]
|
||||
if "message" in response.json()
|
||||
else "Unknown error"
|
||||
)
|
||||
raise Exception(f"Failed to delete Gitea project: {response_message}")
|
||||
else:
|
||||
_info(f'Project "{owner}/{repo}" deleted on Gitea')
|
||||
|
||||
|
||||
def migrate_groups():
|
||||
|
||||
gitlab_groups = get_gitlab_groups()
|
||||
gitea_groups = get_gitea_groups()
|
||||
|
||||
@ -1332,12 +1759,14 @@ def migrate_users():
|
||||
|
||||
|
||||
def migrate_projects():
|
||||
|
||||
if OVERRIDE_EXISTING_PROJECTS:
|
||||
_warn("EXISTING PROJECTS WILL BE OVERRIDDEN!")
|
||||
|
||||
gitlab_projects = get_gitlab_projects()
|
||||
gitea_projects = get_gitea_projects()
|
||||
|
||||
# dump the projects to json files
|
||||
import json
|
||||
|
||||
with open("gitlab_projects.json", "w") as f:
|
||||
json.dump(gitlab_projects, f, indent=4)
|
||||
|
||||
@ -1419,8 +1848,10 @@ def run_migration():
|
||||
|
||||
|
||||
def main():
|
||||
_info("Gitlab2Gitea v1.0 - by Zion Networks")
|
||||
_info("------------------------------------")
|
||||
header_info = f"{APP_NAME} v{APP_VERSION} - by {APP_AUTHOR}"
|
||||
|
||||
_info(f"{header_info}")
|
||||
_info("-" * len(header_info))
|
||||
_info("")
|
||||
|
||||
if sys.version_info < (3, 6):
|
||||
|
Reference in New Issue
Block a user