8 Commits

Author SHA1 Message Date
16ab8a2ffd Track+log some migration failures instead of actually failing 2023-12-28 05:11:21 -08:00
c6e2244694 Tweak logging 2023-12-28 05:10:54 -08:00
388a0235dd Log each repo during migration 2023-12-28 04:20:42 -08:00
06b90e515c README correction 2023-12-28 03:16:34 -08:00
cb06d54d4c Bump python and deps 2023-12-28 03:08:08 -08:00
e665ba79d7 Fix SSL shenanigans with a hack to allow the user to specify the CA bundle file 2023-08-12 04:45:57 -07:00
1a6dcdeb78 typos 2023-08-12 04:45:40 -07:00
c478284764 Start using pyenv and relock pipenv 2023-08-12 04:45:15 -07:00
6 changed files with 90 additions and 25 deletions

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.12.1

View File

@ -9,4 +9,5 @@ giteapy-soteria = {git = "https://github.com/Yousif-CS/giteapy.git"}
[dev-packages]
[requires]
python_version = "3.10"
python_version = "3.12"

18
Pipfile.lock generated
View File

@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
"sha256": "1dc9e96fd5a12468ed7d0869b11b9fbca2464e4b806fb8b9c17391a41b6f0eb8"
"sha256": "5041607b8b692ebdc03484547b2d4336083196ff75b38c3140b608a7d59abaf8"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.10"
"python_version": "3.12"
},
"sources": [
{
@ -18,11 +18,11 @@
"default": {
"certifi": {
"hashes": [
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
"sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1",
"sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"
],
"markers": "python_version >= '3.6'",
"version": "==2022.12.7"
"version": "==2023.11.17"
},
"giteapy-soteria": {
"git": "https://github.com/Yousif-CS/giteapy.git",
@ -46,11 +46,11 @@
},
"urllib3": {
"hashes": [
"sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72",
"sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"
"sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3",
"sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.26.14"
"markers": "python_version >= '3.8'",
"version": "==2.1.0"
}
},
"develop": {}

View File

@ -6,11 +6,11 @@ Supports changing destination names a bit, and adding topics to each transferred
by Mike Peralta
Current license: You are free to clone and use this program but all other rights reserved, provided you accept 100% of all liability of any outcome of use/download/etc this program. Better license coming soon.
Current license: You are free to clone and use this program but all other rights reserved, provided you accept 100% of all liability of any outcome of use/download/etc. this program. Better license coming soon.
## Requirements
* python 3.10
* python 3.12
* pipenv
## Installation

View File

@ -4,6 +4,8 @@ import giteapy
import logging
import sys
import certifi
class Migrator:
@ -101,6 +103,15 @@ class Migrator:
self.__verify_ssl = b
def set_ca_bundle(self, bundle_path: str):
self.__logger.info("Setting certificate bundle path")
# Hacky but oh well
self.__logger.info(f"Old path: {certifi.where()}")
certifi.core._CACERT_PATH = bundle_path
self.__logger.info(f"New path: {certifi.where()}")
def migrate_entire_org(
self,
interactive: bool = True,
@ -118,6 +129,9 @@ class Migrator:
api_source: giteapy.OrganizationApi
api_destination: giteapy.OrganizationApi
# Tattle on certify
self.__logger.info(f"Certifi is currently using CA bundle: {certifi.where()}")
# Grab all org repos
source_repos = self._fetch_all_org_repos(org=source_org)
self.__logger.info(f"Found {len(source_repos)} repos on source:")
@ -176,7 +190,9 @@ class Migrator:
for repo in repos_migrate:
repo: giteapy.Repository
destination_name = self._make_destination_repo_name(pattern=destination_repo_name, repo=repo)
self.__logger.info(f"#{repo.id} \"{repo.name}\" ==> \"{destination_name}\"")
self.__logger.info(
f"#{repo.id} \"{repo.name}\"\n> \"{destination_name}\""
)
else:
self.__logger.info("No repos marked to migrate")
@ -196,14 +212,27 @@ class Migrator:
if confirmation == "MIGRATE":
self.__logger.info("Confirmation received; Processing ... ")
source_repos_successful = self._migrate_repos(
source_repos_successful, source_repos_failed = self._migrate_repos(
destination_org_name=destination_org,
destination_repo_name=destination_repo_name,
destination_topics=destination_topics,
do_destination_copy_topics=do_destination_copy_topics,
repos=repos_migrate
)
self.__logger.info(f"{len(source_repos_successful)} of {len(repos_migrate)} repos successfully migrated.")
self.__logger.info(
f"{len(source_repos_successful)} of {len(repos_migrate)} repos successfully migrated."
)
if len(source_repos_failed) > 0:
self.__logger.error(f"Failed to migrate {len(source_repos_failed)} repos:")
for repo, exception in source_repos_failed:
self.__logger.error(
f"> {repo.name}"
)
self.__logger.error(f"Captured exception data:")
for repo, exception in source_repos_failed:
self.__logger.error(
f"Failed to migrate repo: {repo.name}\n> {exception}"
)
self._delete_migrated_repos(source_org_name=source_org, repos=source_repos_successful)
@ -307,12 +336,15 @@ class Migrator:
)
source_repos_successful = []
source_repos_failed = []
for source_repo in repos:
source_repo: giteapy.Repository
this_destination_repo_name = destination_repo_name.replace("%N%", source_repo.name)
self.__logger.info(f"Migrating: {source_repo.name} ==> {this_destination_repo_name}")
source_repo_topics = api_source_repos.repo_list_topics(owner=source_repo.owner.login, repo=source_repo.name)
source_repo_topics = source_repo_topics.topics
@ -333,12 +365,28 @@ class Migrator:
self.__logger.debug("Migrate body:")
self.__logger.debug(migrate_body)
try:
destination_api = self._get_repo_api(
hostname=self.__destination_host,
port=self.__destination_port,
token=self.__destination_token,
)
except giteapy.rest.ApiException as e:
self.__logger.error(f"Failed to generate destination API: {e}")
source_repos_failed.append(
(source_repo, e)
)
continue
try:
repo_new = destination_api.repo_migrate(body=migrate_body)
except giteapy.rest.ApiException as e:
self.__logger.error(f"Failed to execute repo migration request via API: {e}")
source_repos_failed.append(
(source_repo, e)
)
continue
self.__logger.debug(f"Migration result: {repo_new}")
repo_new: giteapy.Repository
@ -366,10 +414,14 @@ class Migrator:
source_repos_successful.append(source_repo)
return source_repos_successful
return source_repos_successful, source_repos_failed
def _delete_migrated_repos(self, source_org_name: str, repos: list[giteapy.Repository]):
if len(repos) == 0:
self.__logger.warning(f"Cannot delete any migrated repos because none were successful!")
return
repo_api = self._get_repo_api(
hostname=self.__source_host,
port=self.__source_port,
@ -378,7 +430,7 @@ class Migrator:
repo_api: giteapy.RepositoryApi
self.__logger.info("")
self.__logger.info("Can now delete the following successfully migrated repos:")
self.__logger.info(f"Can now delete {len(repos)} successfully migrated repos:")
for r in repos:
self.__logger.info(f"> #{r.id} \"{r.full_name}\" ==> {r.clone_url}")

15
main.py
View File

@ -23,7 +23,7 @@ def main():
dest="source_port",
required=False,
default=None,
help="Port of the source server"
help="Port of the source server. Requests will use https (not ssh), so you probably don't want to change this."
)
parser.add_argument(
"--source-token",
@ -57,7 +57,7 @@ def main():
dest="destination_port",
required=False,
default=None,
help="Port of the destination server"
help="Port of the destination server. Requests will use https (not ssh), so you probably don't want to change this."
)
parser.add_argument(
"--destination-token", "--dest-token",
@ -124,6 +124,13 @@ def main():
help="Don't verify SSL certificates",
)
parser.add_argument(
"--ca-bundle",
dest="ca_bundle",
default=None,
help="Specify the location of your system-wide CA Bundle, in case python is not using it."
)
args = parser.parse_args()
mig = Migrator(
source_host=args.source_hostname,
@ -133,7 +140,11 @@ def main():
destination_port=args.destination_port,
destination_token=args.destination_token
)
mig.set_verify_ssl(args.verify_ssl)
if args.ca_bundle:
mig.set_ca_bundle(args.ca_bundle)
mig.migrate_entire_org(
interactive=args.interactive,
source_org=args.source_org,