Improved error handling; added stats; implemented remaining overrride flags
This commit is contained in:
parent
d81e5700ef
commit
034a819259
11
README.md
11
README.md
@ -10,8 +10,9 @@ We're planning to release a growing amount of open source software, that is free
|
|||||||
|
|
||||||
### What scripts can be found here?
|
### What scripts can be found here?
|
||||||
|
|
||||||
| Name | Description | License | Current Version | Written in | Supported Distros | File |
|
| Name | Description | License | Current Version | Written in | Supported Distros | Path |
|
||||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------- | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------- | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------ |
|
||||||
| Borgmatic Setup Tool | If you plan to use borg as backup solution, you should also take a look at [borgmatic](https://torsion.org/borgmatic/). It's a Python wrapper for the award winning backup tool [borgbackup](https://borgbackup.readthedocs.io/en/stable/index.html) that simplifies creating secure and reliable backups even more. You can even store your configurations in files. This script will do the setup for you to. | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.2.1 | Bash | Debian and derivates | [bormatic_setup.sh](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/borgmatic/borgmatic_setup.sh) |
|
| Borgmatic Setup Tool | If you plan to use borg as backup solution, you should also take a look at [borgmatic](https://torsion.org/borgmatic/). It's a Python wrapper for the award winning backup tool [borgbackup](https://borgbackup.readthedocs.io/en/stable/index.html) that simplifies creating secure and reliable backups even more. You can even store your configurations in files. This script will do the setup for you to. | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.2.1 | Bash | Debian and derivates | [Borgmatic Setup](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/borgmatic) |
|
||||||
| Git Rewrite Author | **USE WITH CAUTION!!!**<br><br> This script will rewrite the entire history of the remote end and set the author email to the provided one.<br><br>**This is NOT reversible!** | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Bash | Most Linux distros | [git_rewrite_author.sh](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/git/git_rewrite_author.sh) |
|
| Git Rewrite Author | **USE WITH CAUTION!!!**<br><br> This script will rewrite the entire history of the remote end and set the author email to the provided one.<br><br>**This is NOT reversible!** | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Bash | Most Linux distros | [Git Scripts](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/git) |
|
||||||
| UFW Beautifier | Simple Python script to get a fancy formatted `ufw.log` | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Python | Most Linux distros | [ufw_beautifier.py](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/ufw/ufw_beautifier.py) |
|
| UFW Beautifier | Simple Python script to get a fancy formatted `ufw.log` | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0 | Python | Most Linux distros | [UFW Beautifier](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/ufw) |
|
||||||
|
| Gitlab2Gitea Migration | Python script to perform a full migration from Gitlab to Gitea | [MIT]([LICENSE](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/LICENSE)) | v1.0.0-pre | Python | Most Linux distros | [Gitlab to Gitea](https://git.zion-networks.de/ZionNetworks/linux-bash-scripts/src/branch/main/gitlab2gitea) |
|
@ -29,12 +29,15 @@
|
|||||||
# --include-issues Include issues repositories (default: False)
|
# --include-issues Include issues repositories (default: False)
|
||||||
# --include-merge-requests Include merge requests 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-groups Override existing groups on Gitea (default: False)
|
||||||
# --override-users Override existing users on Gitea (default: False) - not implemented yet
|
# --override-users Override existing users on Gitea (default: False)
|
||||||
# --override-projects Override existing projects on Gitea (default: False)
|
# --override-projects Override existing projects on Gitea (default: False)
|
||||||
#
|
#
|
||||||
# --skip-empty-groups Skip empty groups (default: False) - not implemented yet
|
# --skip-empty-groups Skip empty groups (default: False) - not implemented yet
|
||||||
# --skip-empty-projects Skip empty projects (default: False) - not implemented yet
|
# --skip-empty-projects Skip empty projects (default: False) - not implemented yet
|
||||||
|
# --skip-users [user1,user2,...] Skip specific users (default: None) - not implemented yet
|
||||||
|
# --skip-groups [group1,group2,...] Skip specific groups (default: None) - not implemented yet
|
||||||
|
# --skip-projects [project1,project2,...] Skip specific projects (default: None) - not implemented yet
|
||||||
#
|
#
|
||||||
# --only-groups Migrate only groups (default: False)
|
# --only-groups Migrate only groups (default: False)
|
||||||
# --only-users Migrate only users (default: False)
|
# --only-users Migrate only users (default: False)
|
||||||
@ -85,6 +88,12 @@ OVERRIDE_EXISTING_GROUPS = False
|
|||||||
OVERRIDE_EXISTING_USERS = False
|
OVERRIDE_EXISTING_USERS = False
|
||||||
OVERRIDE_EXISTING_PROJECTS = False
|
OVERRIDE_EXISTING_PROJECTS = False
|
||||||
|
|
||||||
|
SKIP_EMPTY_GROUPS = False
|
||||||
|
SKIP_EMPTY_PROJECTS = False
|
||||||
|
SKIP_USERS = []
|
||||||
|
SKIP_GROUPS = []
|
||||||
|
SKIP_PROJECTS = []
|
||||||
|
|
||||||
ONLY_GROUPS = False
|
ONLY_GROUPS = False
|
||||||
ONLY_USERS = False
|
ONLY_USERS = False
|
||||||
ONLY_PROJECTS = False
|
ONLY_PROJECTS = False
|
||||||
@ -154,6 +163,36 @@ GITEA_RESERVED_REPONAMES = [
|
|||||||
"wiki",
|
"wiki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Runtime variables
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
STATS = {
|
||||||
|
"users": {
|
||||||
|
"deleted": [],
|
||||||
|
"created": [],
|
||||||
|
"skipped": [],
|
||||||
|
"updated": [],
|
||||||
|
"errors": []
|
||||||
|
},
|
||||||
|
|
||||||
|
"groups": {
|
||||||
|
"deleted": [],
|
||||||
|
"created": [],
|
||||||
|
"skipped": [],
|
||||||
|
"updated": [],
|
||||||
|
"errors": []
|
||||||
|
},
|
||||||
|
|
||||||
|
"projects": {
|
||||||
|
"deleted": [],
|
||||||
|
"created": [],
|
||||||
|
"skipped": [],
|
||||||
|
"updated": [],
|
||||||
|
"errors": []
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -585,20 +624,27 @@ def _exception(exception, custom_message=None):
|
|||||||
lineno = exc_tb.tb_lineno
|
lineno = exc_tb.tb_lineno
|
||||||
formatted_traceback = traceback.format_exc()
|
formatted_traceback = traceback.format_exc()
|
||||||
|
|
||||||
# Prepare the exception message
|
# Prepare formatted and clear exception messages
|
||||||
exception_message = (f"{custom_message}\n" if custom_message else "") + (
|
formatted_exception_message = (
|
||||||
f"\033[1m\033[31m[EXC]\033[0m {exception} "
|
(f"{custom_message}\n" if custom_message else "")
|
||||||
|
+ f"\033[1m\033[31m[EXC]\033[0m {exception} "
|
||||||
f"(file: {filename}, line: {lineno})\n"
|
f"(file: {filename}, line: {lineno})\n"
|
||||||
f"{formatted_traceback}\n"
|
f"{formatted_traceback}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Print the exception message to the console
|
clear_exception_message = (
|
||||||
print(exception_message)
|
(f"{custom_message}\n" if custom_message else "")
|
||||||
|
+ f"[EXC] {exception} (file: {filename}, line: {lineno})\n"
|
||||||
|
+ formatted_traceback
|
||||||
|
)
|
||||||
|
|
||||||
# Write the exception message to the log file if defined
|
# Print the formatted exception message to the console
|
||||||
|
print(formatted_exception_message)
|
||||||
|
|
||||||
|
# Write the clear exception message to the log file if defined
|
||||||
if LOG_FILE:
|
if LOG_FILE:
|
||||||
with open(LOG_FILE, "a") as log_file:
|
with open(LOG_FILE, "a") as log_file:
|
||||||
log_file.write(f"[EXC] {exception_message}\n")
|
log_file.write(clear_exception_message + "\n")
|
||||||
|
|
||||||
|
|
||||||
# PROGRAM
|
# PROGRAM
|
||||||
@ -638,16 +684,20 @@ def gitlab2gitea_visibility(visibility: str) -> str:
|
|||||||
def check_gitlab():
|
def check_gitlab():
|
||||||
_debug(f"REQUEST: GET {GITLAB_URL}/api/{GITLAB_API_VERSION}/version")
|
_debug(f"REQUEST: GET {GITLAB_URL}/api/{GITLAB_API_VERSION}/version")
|
||||||
|
|
||||||
response = requests.get(
|
try:
|
||||||
f"{GITLAB_URL}/api/{GITLAB_API_VERSION}/version",
|
response = requests.get(
|
||||||
headers={
|
f"{GITLAB_URL}/api/{GITLAB_API_VERSION}/version",
|
||||||
"Content-Type": "application/json",
|
headers={
|
||||||
"Accept": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": f"Bearer {GITLAB_TOKEN}",
|
"Accept": "application/json",
|
||||||
},
|
"Authorization": f"Bearer {GITLAB_TOKEN}",
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
|
||||||
_trace(f"RESPONSE: {response.json()}")
|
_trace(f"RESPONSE: {response.json()}")
|
||||||
|
except Exception as e:
|
||||||
|
_exception(e, f"Failed to get GitLab version: {e}")
|
||||||
|
EXIT_REQUESTED = True
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
response_message = (
|
response_message = (
|
||||||
@ -664,16 +714,20 @@ def check_gitlab():
|
|||||||
def check_gitea():
|
def check_gitea():
|
||||||
_debug(f"REQUEST: GET {GITEA_URL}/api/{GITEA_API_VERSION}/version")
|
_debug(f"REQUEST: GET {GITEA_URL}/api/{GITEA_API_VERSION}/version")
|
||||||
|
|
||||||
response = requests.get(
|
try:
|
||||||
f"{GITEA_URL}/api/{GITEA_API_VERSION}/version",
|
response = requests.get(
|
||||||
headers={
|
f"{GITEA_URL}/api/{GITEA_API_VERSION}/version",
|
||||||
"Content-Type": "application/json",
|
headers={
|
||||||
"Accept": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Authorization": f"token {GITEA_TOKEN}",
|
"Accept": "application/json",
|
||||||
},
|
"Authorization": f"token {GITEA_TOKEN}",
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
|
||||||
_trace(f"RESPONSE: {response.json()}")
|
_trace(f"RESPONSE: {response.json()}")
|
||||||
|
except Exception as e:
|
||||||
|
_exception(e, f"Failed to get Gitea version: {e}")
|
||||||
|
EXIT_REQUESTED = True
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
response_message = (
|
response_message = (
|
||||||
@ -867,10 +921,33 @@ def migrate_gitlab_project_to_gitea(gitlab_project: dict):
|
|||||||
if "message" in response.json()
|
if "message" in response.json()
|
||||||
else "Unknown error"
|
else "Unknown error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["projects"]["errors"].append(
|
||||||
|
{
|
||||||
|
"group": (
|
||||||
|
gitlab_project["namespace"]["path"]
|
||||||
|
if gitlab_project["namespace"]["kind"] == "group"
|
||||||
|
else gitlab_project["owner"]["username"]
|
||||||
|
),
|
||||||
|
"name": gitlab_project["path"],
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
raise Exception(f"Failed to create Gitea project: {response_message}")
|
raise Exception(f"Failed to create Gitea project: {response_message}")
|
||||||
else:
|
else:
|
||||||
project = response.json()
|
project = response.json()
|
||||||
|
|
||||||
|
STATS["projects"]["created"].append(
|
||||||
|
{
|
||||||
|
"group": (
|
||||||
|
gitlab_project["namespace"]["path"]
|
||||||
|
if gitlab_project["namespace"]["kind"] == "group"
|
||||||
|
else gitlab_project["owner"]["username"]
|
||||||
|
),
|
||||||
|
"name": gitlab_project["path"],
|
||||||
|
}
|
||||||
|
)
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
|
||||||
@ -911,6 +988,15 @@ def migrate_gitlab_user_to_gitea(user: dict):
|
|||||||
if "message" in response.json()
|
if "message" in response.json()
|
||||||
else "Unknown error"
|
else "Unknown error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["users"]["errors"].append(
|
||||||
|
{
|
||||||
|
"username": user["username"],
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
"admin": user["is_admin"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
raise Exception(f"Failed to create Gitea user: {response_message}")
|
raise Exception(f"Failed to create Gitea user: {response_message}")
|
||||||
else:
|
else:
|
||||||
user = response.json()
|
user = response.json()
|
||||||
@ -920,6 +1006,14 @@ def migrate_gitlab_user_to_gitea(user: dict):
|
|||||||
else:
|
else:
|
||||||
_info(f'User "{user["username"]}" created on Gitea')
|
_info(f'User "{user["username"]}" created on Gitea')
|
||||||
|
|
||||||
|
STATS["users"]["created"].append(
|
||||||
|
{
|
||||||
|
"username": user["username"],
|
||||||
|
"email": user["email"],
|
||||||
|
"admin": user["is_admin"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@ -1049,10 +1143,22 @@ def migrate_gitlab_group_to_gitea(gitlab_group: dict):
|
|||||||
if "message" in response.json()
|
if "message" in response.json()
|
||||||
else "Unknown error"
|
else "Unknown error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["groups"]["errors"].append(
|
||||||
|
{
|
||||||
|
"name": gitlab_group["path"],
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
raise Exception(f"Failed to create Gitea group: {response_message}")
|
raise Exception(f"Failed to create Gitea group: {response_message}")
|
||||||
else:
|
else:
|
||||||
group = response.json()
|
group = response.json()
|
||||||
|
|
||||||
|
STATS["groups"]["created"].append(
|
||||||
|
{"name": gitlab_group["path"], "full_name": gitlab_group["full_name"]}
|
||||||
|
)
|
||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
@ -1497,6 +1603,9 @@ def create_missing_groups(gitlab_groups: list, gitea_groups: list):
|
|||||||
|
|
||||||
if is_gitea_reserved_organame(name):
|
if is_gitea_reserved_organame(name):
|
||||||
_warn(f'Skipping group "{name}": Group name is reserved on Gitea!')
|
_warn(f'Skipping group "{name}": Group name is reserved on Gitea!')
|
||||||
|
|
||||||
|
STATS["groups"]["skipped"].append({"name": name, "reason": "Reserved"})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not exists:
|
if not exists:
|
||||||
@ -1505,8 +1614,22 @@ def create_missing_groups(gitlab_groups: list, gitea_groups: list):
|
|||||||
try:
|
try:
|
||||||
_info(f'Migrating Gitlab group "{name}" to Gitea...')
|
_info(f'Migrating Gitlab group "{name}" to Gitea...')
|
||||||
migrate_gitlab_group_to_gitea(gitlab_group)
|
migrate_gitlab_group_to_gitea(gitlab_group)
|
||||||
|
_info(f'Group "{name}" created on Gitea')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to create Gitea group "{name}": {e}', e)
|
_exception(f'Failed to create Gitea group "{name}": {e}', e)
|
||||||
|
else:
|
||||||
|
if OVERRIDE_EXISTING_GROUPS:
|
||||||
|
try:
|
||||||
|
_info(
|
||||||
|
f'Group "{name}" already exists on Gitea and will be overridden...'
|
||||||
|
)
|
||||||
|
delete_gitea_group(name)
|
||||||
|
_info(f'Group "{name}" deleted on Gitea')
|
||||||
|
_info(f'Creating missing group "{name}" on Gitea...')
|
||||||
|
migrate_gitlab_group_to_gitea(gitlab_group)
|
||||||
|
_info(f'Group "{name}" created on Gitea')
|
||||||
|
except Exception as e:
|
||||||
|
_exception(f'Failed to override Gitea group "{name}": {e}', e)
|
||||||
|
|
||||||
|
|
||||||
def create_missing_users(gitlab_users: list, gitea_users: list):
|
def create_missing_users(gitlab_users: list, gitea_users: list):
|
||||||
@ -1530,14 +1653,27 @@ def create_missing_users(gitlab_users: list, gitea_users: list):
|
|||||||
_warn(
|
_warn(
|
||||||
f'User "{name}" does not have an email address and will not be created!'
|
f'User "{name}" does not have an email address and will not be created!'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["users"]["skipped"].append(
|
||||||
|
{"username": name, "reason": "No email address"}
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if gitea_user["username"] == None or gitea_user["username"] == "":
|
if gitea_user["username"] == None or gitea_user["username"] == "":
|
||||||
_warn(f'User "{name}" does not have a username and will not be created!')
|
_warn(f'User "{name}" does not have a username and will not be created!')
|
||||||
|
|
||||||
|
STATS["users"]["skipped"].append(
|
||||||
|
{"username": name, "reason": "No username"}
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if is_gitea_reserved_username(name):
|
if is_gitea_reserved_username(name):
|
||||||
_warn(f'Skipping user "{name}": Username is reserved on Gitea!')
|
_warn(f'Skipping user "{name}": Username is reserved on Gitea!')
|
||||||
|
|
||||||
|
STATS["users"]["skipped"].append({"username": name, "reason": "Reserved"})
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not exists:
|
if not exists:
|
||||||
@ -1546,8 +1682,22 @@ def create_missing_users(gitlab_users: list, gitea_users: list):
|
|||||||
try:
|
try:
|
||||||
_info(f'Migrating Gitlab user "{name}" to Gitea...')
|
_info(f'Migrating Gitlab user "{name}" to Gitea...')
|
||||||
migrate_gitlab_user_to_gitea(gitlab_user)
|
migrate_gitlab_user_to_gitea(gitlab_user)
|
||||||
|
_info(f'User "{name}" created on Gitea')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to create Gitea user "{name}": {e}', e)
|
_exception(f'Failed to create Gitea user "{name}": {e}', e)
|
||||||
|
else:
|
||||||
|
if OVERRIDE_EXISTING_USERS:
|
||||||
|
try:
|
||||||
|
_info(
|
||||||
|
f'User "{name}" already exists on Gitea and will be overridden...'
|
||||||
|
)
|
||||||
|
delete_gitea_user(name)
|
||||||
|
_info(f'User "{name}" deleted on Gitea')
|
||||||
|
_info(f'Creating missing user "{name}" on Gitea...')
|
||||||
|
migrate_gitlab_user_to_gitea(gitlab_user)
|
||||||
|
_info(f'User "{name}" created on Gitea')
|
||||||
|
except Exception as e:
|
||||||
|
_exception(f'Failed to override Gitea user "{name}": {e}', e)
|
||||||
|
|
||||||
|
|
||||||
def create_missing_projects(gitlab_projects: list, gitea_projects: list):
|
def create_missing_projects(gitlab_projects: list, gitea_projects: list):
|
||||||
@ -1572,32 +1722,69 @@ def create_missing_projects(gitlab_projects: list, gitea_projects: list):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if is_gitea_reserved_reponame(name):
|
if is_gitea_reserved_reponame(name):
|
||||||
_warn(f'Skipping project "{name}": Project name is reserved on Gitea!')
|
_warn(
|
||||||
|
f'Skipping project "{group}/{name}": Project name is reserved on Gitea!'
|
||||||
|
)
|
||||||
|
|
||||||
|
STATS["projects"]["skipped"].append(
|
||||||
|
{"group": group, "name": name, "reason": "Reserved"}
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if is_gitlab_project_in_subgroup(gitlab_project):
|
if is_gitlab_project_in_subgroup(gitlab_project):
|
||||||
_warn(
|
_warn(
|
||||||
f'Skipping project "{name}": Project is in a subgroup and not supported by Gitea!'
|
f'Skipping project "{group}/{name}": Project is in a subgroup and not supported by Gitea!'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["projects"]["skipped"].append(
|
||||||
|
{"group": group, "name": name, "reason": "Unsupported Subgroup"}
|
||||||
|
)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not exists:
|
if not exists:
|
||||||
_info(f'Creating missing project "{name}" on Gitea...')
|
_info(f'Creating missing project "{group}/{name}" on Gitea...')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_info(f'Migrating Gitlab project "{name}" to Gitea...')
|
_info(f'Migrating Gitlab project "{group}/{name}" to Gitea...')
|
||||||
migrate_gitlab_project_to_gitea(gitlab_project)
|
migrate_gitlab_project_to_gitea(gitlab_project)
|
||||||
|
_info(f'Project "{group}/{name}" created on Gitea')
|
||||||
|
|
||||||
if ONLY_ONE_PROJECT:
|
if ONLY_ONE_PROJECT:
|
||||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to create Gitea project "{name}": {e}', e)
|
_exception(f'Failed to create Gitea project "{group}/{name}": {e}', e)
|
||||||
|
|
||||||
if ONLY_ONE_PROJECT:
|
if ONLY_ONE_PROJECT:
|
||||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
if OVERRIDE_EXISTING_PROJECTS:
|
||||||
|
try:
|
||||||
|
_info(
|
||||||
|
f'Project "{group}/{name}" already exists on Gitea and will be overridden...'
|
||||||
|
)
|
||||||
|
delete_gitea_project(gitea_project)
|
||||||
|
_info(f'Project "{group}/{name}" deleted on Gitea')
|
||||||
|
_info(f'Creating missing project "{group}/{name}" on Gitea...')
|
||||||
|
migrate_gitlab_project_to_gitea(gitlab_project)
|
||||||
|
_info(f'Project "{group}/{name}" created on Gitea')
|
||||||
|
|
||||||
|
if ONLY_ONE_PROJECT:
|
||||||
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
_exception(
|
||||||
|
f'Failed to override Gitea project "{group}/{name}": {e}', e
|
||||||
|
)
|
||||||
|
|
||||||
|
if ONLY_ONE_PROJECT:
|
||||||
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
def update_existing_groups(gitlab_groups: list, gitea_groups: list):
|
def update_existing_groups(gitlab_groups: list, gitea_groups: list):
|
||||||
@ -1634,6 +1821,7 @@ def update_existing_groups(gitlab_groups: list, gitea_groups: list):
|
|||||||
"full_name": gitlab_group["full_name"],
|
"full_name": gitlab_group["full_name"],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
_info(f'Group "{name}" updated on Gitea')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to update Gitea group "{name}": {e}', e)
|
_exception(f'Failed to update Gitea group "{name}": {e}', e)
|
||||||
|
|
||||||
@ -1673,6 +1861,7 @@ def update_existing_users(gitlab_users: list, gitea_users: list):
|
|||||||
try:
|
try:
|
||||||
_info(f'Updating existing user "{name}" on Gitea...')
|
_info(f'Updating existing user "{name}" on Gitea...')
|
||||||
update_gitea_user(gitlab_user)
|
update_gitea_user(gitlab_user)
|
||||||
|
_info(f'User "{name}" updated on Gitea')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to update Gitea user "{name}": {e}', e)
|
_exception(f'Failed to update Gitea user "{name}": {e}', e)
|
||||||
else:
|
else:
|
||||||
@ -1702,36 +1891,109 @@ def update_existing_projects(gitlab_projects: list, gitea_projects: list):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if is_gitea_reserved_reponame(name):
|
if is_gitea_reserved_reponame(name):
|
||||||
_warn(f'Skipping project "{name}": Project name is reserved on Gitea!')
|
_warn(
|
||||||
|
f'Skipping project "{group}/{name}": Project name is reserved on Gitea!'
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if is_gitlab_project_in_subgroup(gitlab_project):
|
if is_gitlab_project_in_subgroup(gitlab_project):
|
||||||
_warn(
|
_warn(
|
||||||
f'Skipping project "{name}": Project is in a subgroup and not supported by Gitea!'
|
f'Skipping project "{group}/{name}": Project is in a subgroup and not supported by Gitea!'
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if exists:
|
if exists:
|
||||||
try:
|
try:
|
||||||
_info(f'Updating existing project "{name}" on Gitea...')
|
_info(f'Updating existing project "{group}/{name}" on Gitea...')
|
||||||
if OVERRIDE_EXISTING_PROJECTS:
|
update_gitea_project(gitlab_project)
|
||||||
delete_gitea_project(gitea_project)
|
_info(f'Project "{group}/{name}" updated on Gitea')
|
||||||
migrate_gitlab_project_to_gitea(gitlab_project)
|
|
||||||
else:
|
|
||||||
update_gitea_project(gitlab_project)
|
|
||||||
|
|
||||||
if ONLY_ONE_PROJECT:
|
if ONLY_ONE_PROJECT:
|
||||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f'Failed to update Gitea project "{name}": {e}', e)
|
_exception(f'Failed to update Gitea project "{group}/{name}": {e}', e)
|
||||||
|
|
||||||
if ONLY_ONE_PROJECT:
|
if ONLY_ONE_PROJECT:
|
||||||
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
_warn("DEBUG MODE ENABLED - BREAKING AFTER FIRST PROJECT")
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
_warn(f'Project "{name}" does not exist on Gitea!')
|
_warn(f'Project "{group}/{name}" does not exist on Gitea!')
|
||||||
|
|
||||||
|
|
||||||
|
# ENDPOINT: DELETE /api/{GITEA_API_VERSION}/orgs/{org}
|
||||||
|
def delete_gitea_group(name: str):
|
||||||
|
|
||||||
|
_debug(f"REQUEST: DELETE {GITEA_URL}/api/{GITEA_API_VERSION}/orgs/{name}")
|
||||||
|
|
||||||
|
response = requests.delete(
|
||||||
|
f"{GITEA_URL}/api/{GITEA_API_VERSION}/orgs/{name}",
|
||||||
|
headers={
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Authorization": f"token {GITEA_TOKEN}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 204:
|
||||||
|
_trace(f"RESPONSE: {response.json()}")
|
||||||
|
response_message = (
|
||||||
|
response.json()["message"]
|
||||||
|
if "message" in response.json()
|
||||||
|
else "Unknown error"
|
||||||
|
)
|
||||||
|
|
||||||
|
STATS["groups"]["errors"].append(
|
||||||
|
{
|
||||||
|
"name": name,
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
raise Exception(f"Failed to delete Gitea group: {response_message}")
|
||||||
|
else:
|
||||||
|
_info(f'Group "{name}" deleted on Gitea')
|
||||||
|
|
||||||
|
STATS["groups"]["deleted"].append({"name": name})
|
||||||
|
|
||||||
|
|
||||||
|
# ENDPOINT: DELETE /api/{GITEA_API_VERSION}/admin/users/{username}
|
||||||
|
def delete_gitea_user(username: str):
|
||||||
|
|
||||||
|
_debug(
|
||||||
|
f"REQUEST: DELETE {GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = requests.delete(
|
||||||
|
f"{GITEA_URL}/api/{GITEA_API_VERSION}/admin/users/{username}",
|
||||||
|
headers={
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Authorization": f"token {GITEA_TOKEN}",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 204:
|
||||||
|
_trace(f"RESPONSE: {response.json()}")
|
||||||
|
response_message = (
|
||||||
|
response.json()["message"]
|
||||||
|
if "message" in response.json()
|
||||||
|
else "Unknown error"
|
||||||
|
)
|
||||||
|
|
||||||
|
STATS["users"]["errors"].append(
|
||||||
|
{
|
||||||
|
"username": username,
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
raise Exception(f"Failed to delete Gitea user: {response_message}")
|
||||||
|
else:
|
||||||
|
_info(f'User "{username}" deleted on Gitea')
|
||||||
|
|
||||||
|
STATS["users"]["deleted"].append({"username": username})
|
||||||
|
|
||||||
|
|
||||||
# ENDPOINT: DELETE /api/{GITEA_API_VERSION}/repos/{owner}/{repo}
|
# ENDPOINT: DELETE /api/{GITEA_API_VERSION}/repos/{owner}/{repo}
|
||||||
@ -1751,21 +2013,34 @@ def delete_gitea_project(project: dict):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
_trace(f"RESPONSE: {response.json()}")
|
|
||||||
|
|
||||||
if response.status_code != 204:
|
if response.status_code != 204:
|
||||||
|
_trace(f"RESPONSE: {response.json()}")
|
||||||
response_message = (
|
response_message = (
|
||||||
response.json()["message"]
|
response.json()["message"]
|
||||||
if "message" in response.json()
|
if "message" in response.json()
|
||||||
else "Unknown error"
|
else "Unknown error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
STATS["projects"]["errors"].append(
|
||||||
|
{
|
||||||
|
"group": owner,
|
||||||
|
"name": repo,
|
||||||
|
"error": response_message if response_message else "Unknown error",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
raise Exception(f"Failed to delete Gitea project: {response_message}")
|
raise Exception(f"Failed to delete Gitea project: {response_message}")
|
||||||
else:
|
else:
|
||||||
_info(f'Project "{owner}/{repo}" deleted on Gitea')
|
_info(f'Project "{owner}/{repo}" deleted on Gitea')
|
||||||
|
|
||||||
|
STATS["projects"]["deleted"].append({"group": owner, "name": repo})
|
||||||
|
|
||||||
|
|
||||||
def migrate_groups():
|
def migrate_groups():
|
||||||
|
|
||||||
|
if OVERRIDE_EXISTING_GROUPS:
|
||||||
|
_warn("EXISTING GROUPS WILL BE OVERRIDDEN!")
|
||||||
|
|
||||||
gitlab_groups = get_gitlab_groups()
|
gitlab_groups = get_gitlab_groups()
|
||||||
gitea_groups = get_gitea_groups()
|
gitea_groups = get_gitea_groups()
|
||||||
|
|
||||||
@ -1785,7 +2060,9 @@ def migrate_groups():
|
|||||||
if missing_matches > 0:
|
if missing_matches > 0:
|
||||||
_warn(f"{missing_matches} groups are missing on Gitea!")
|
_warn(f"{missing_matches} groups are missing on Gitea!")
|
||||||
|
|
||||||
if missing_matches > 0 and not NO_CREATE_MISSING_GROUPS:
|
if OVERRIDE_EXISTING_GROUPS or (
|
||||||
|
missing_matches > 0 and not NO_CREATE_MISSING_GROUPS
|
||||||
|
):
|
||||||
_info("Creating missing groups on Gitea...")
|
_info("Creating missing groups on Gitea...")
|
||||||
|
|
||||||
if not DRY_RUN:
|
if not DRY_RUN:
|
||||||
@ -1821,6 +2098,10 @@ def migrate_groups():
|
|||||||
|
|
||||||
|
|
||||||
def migrate_users():
|
def migrate_users():
|
||||||
|
|
||||||
|
if OVERRIDE_EXISTING_USERS:
|
||||||
|
_warn("EXISTING USERS WILL BE OVERRIDDEN!")
|
||||||
|
|
||||||
gitlab_users = get_gitlab_users()
|
gitlab_users = get_gitlab_users()
|
||||||
gitea_users = get_gitea_users()
|
gitea_users = get_gitea_users()
|
||||||
|
|
||||||
@ -1840,7 +2121,7 @@ def migrate_users():
|
|||||||
if missing_matches > 0:
|
if missing_matches > 0:
|
||||||
_warn(f"{missing_matches} users are missing on Gitea!")
|
_warn(f"{missing_matches} users are missing on Gitea!")
|
||||||
|
|
||||||
if missing_matches > 0 and not NO_CREATE_MISSING_USERS:
|
if OVERRIDE_EXISTING_USERS or (missing_matches > 0 and not NO_CREATE_MISSING_USERS):
|
||||||
_info("Creating missing users on Gitea...")
|
_info("Creating missing users on Gitea...")
|
||||||
|
|
||||||
if not DRY_RUN:
|
if not DRY_RUN:
|
||||||
@ -1919,7 +2200,9 @@ def migrate_projects():
|
|||||||
if missing_matches > 0:
|
if missing_matches > 0:
|
||||||
_warn(f"{missing_matches} projects are missing on Gitea!")
|
_warn(f"{missing_matches} projects are missing on Gitea!")
|
||||||
|
|
||||||
if missing_matches > 0 and not NO_CREATE_MISSING_PROJECTS:
|
if OVERRIDE_EXISTING_PROJECTS or (
|
||||||
|
missing_matches > 0 and not NO_CREATE_MISSING_PROJECTS
|
||||||
|
):
|
||||||
_info("Creating missing projects on Gitea...")
|
_info("Creating missing projects on Gitea...")
|
||||||
|
|
||||||
if not DRY_RUN:
|
if not DRY_RUN:
|
||||||
@ -1998,6 +2281,48 @@ def run_migration():
|
|||||||
_info("Project migration completed!")
|
_info("Project migration completed!")
|
||||||
|
|
||||||
|
|
||||||
|
def print_stats():
|
||||||
|
|
||||||
|
_info("Migration Statistics:")
|
||||||
|
_info("")
|
||||||
|
|
||||||
|
_info("Groups:")
|
||||||
|
_info(f" - Created: {len(STATS['groups']['created'])}")
|
||||||
|
_info(f" - Updated: {len(STATS['groups']['updated'])}")
|
||||||
|
_info(f" - Deleted: {len(STATS['groups']['deleted'])}")
|
||||||
|
_info(f" - Skipped: {len(STATS['groups']['skipped'])}")
|
||||||
|
_info(f" - Errors: {len(STATS['groups']['errors'])}")
|
||||||
|
_info("")
|
||||||
|
|
||||||
|
_info("Errors:")
|
||||||
|
for error in STATS["groups"]["errors"]:
|
||||||
|
_error(f' - Group "{error["name"]}": {error["error"]}')
|
||||||
|
|
||||||
|
_info("Users:")
|
||||||
|
_info(f" - Created: {len(STATS['users']['created'])}")
|
||||||
|
_info(f" - Updated: {len(STATS['users']['updated'])}")
|
||||||
|
_info(f" - Deleted: {len(STATS['users']['deleted'])}")
|
||||||
|
_info(f" - Skipped: {len(STATS['users']['skipped'])}")
|
||||||
|
_info(f" - Errors: {len(STATS['users']['errors'])}")
|
||||||
|
_info("")
|
||||||
|
|
||||||
|
_info("Errors:")
|
||||||
|
for error in STATS["users"]["errors"]:
|
||||||
|
_error(f' - User "{error["username"]}": {error["error"]}')
|
||||||
|
|
||||||
|
_info("Projects:")
|
||||||
|
_info(f" - Created: {len(STATS['projects']['created'])}")
|
||||||
|
_info(f" - Updated: {len(STATS['projects']['updated'])}")
|
||||||
|
_info(f" - Deleted: {len(STATS['projects']['deleted'])}")
|
||||||
|
_info(f" - Skipped: {len(STATS['projects']['skipped'])}")
|
||||||
|
_info(f" - Errors: {len(STATS['projects']['errors'])}")
|
||||||
|
_info("")
|
||||||
|
|
||||||
|
_info("Errors:")
|
||||||
|
for error in STATS["projects"]["errors"]:
|
||||||
|
_error(f' - Project "{error["group"]}/{error["name"]}": {error["error"]}')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
@ -2045,8 +2370,13 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
run_migration()
|
run_migration()
|
||||||
|
|
||||||
|
_info("Migration completed!")
|
||||||
|
_info("")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_exception(f"An error occurred: {e}", e)
|
_exception(f"An error occurred: {e}", e)
|
||||||
|
finally:
|
||||||
|
print_stats()
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
|
Reference in New Issue
Block a user