23 Commits

Author SHA1 Message Date
mike
ab977cf3d8 note to self 2026-06-09 05:31:55 -07:00
mike
b60a7a82f7 working on the FRIKKIN ssl verification issue 2026-06-09 05:25:50 -07:00
mike
25bfe8cd0a remove old commented code 2026-06-09 03:53:06 -07:00
mike
31edd1e613 Bump pyenv+pipenv 2026-06-09 03:43:00 -07:00
mike
f08b4956fd Comment out some debugs (whoops) 2026-01-05 14:39:27 -08:00
mike
e6052e1f40 Update README 2026-01-05 14:29:39 -08:00
mike
52f39a5f9e Give user the option to disable SSL/TLS for the source and destination individually 2026-01-05 14:28:57 -08:00
mike
9afa6277bb Bumping python, which I thought I just did ... 2026-01-05 14:27:36 -08:00
mike
15f59d4741 Bump pyenv+pipenv+deps 2026-01-05 13:21:54 -08:00
mike
782729ee58 Give option to specify working directory (totally useless but too late now) 2025-01-04 02:13:40 -08:00
mike
c43fee7024 Bump python/pipenv/pipenvlock and add init-environment script. 2025-01-04 01:02:05 -08:00
mike
64ef546799 Point Pipfile to my forked repo (for now), and relock. 2024-06-18 02:20:52 -07:00
mike
ec81cab79d Seems to be working now 2024-06-18 02:04:43 -07:00
mike
27a8f73390 Work 2024-06-18 00:57:09 -07:00
mike
17a6422b1b Started refactoring to py-gitea 2024-06-17 23:45:40 -07:00
mike
16ab8a2ffd Track+log some migration failures instead of actually failing 2023-12-28 05:11:21 -08:00
mike
c6e2244694 Tweak logging 2023-12-28 05:10:54 -08:00
mike
388a0235dd Log each repo during migration 2023-12-28 04:20:42 -08:00
mike
06b90e515c README correction 2023-12-28 03:16:34 -08:00
mike
cb06d54d4c Bump python and deps 2023-12-28 03:08:08 -08:00
mike
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
mike
1a6dcdeb78 typos 2023-08-12 04:45:40 -07:00
mike
c478284764 Start using pyenv and relock pipenv 2023-08-12 04:45:15 -07:00
9 changed files with 585 additions and 206 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.14.5

View File

@@ -4,9 +4,14 @@ verify_ssl = true
name = "pypi" name = "pypi"
[packages] [packages]
giteapy-soteria = {git = "https://github.com/Yousif-CS/giteapy.git"} py-gitea = "*"
# This forces an earlier version of urllib3 that doesn't have newer strict restrictions on CA certs,
# which I believe was blocking my self-signed chains
urllib3 = "<2.4"
[dev-packages] [dev-packages]
[requires] [requires]
python_version = "3.10" python_version = "3.14"

197
Pipfile.lock generated
View File

@@ -1,11 +1,11 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "1dc9e96fd5a12468ed7d0869b11b9fbca2464e4b806fb8b9c17391a41b6f0eb8" "sha256": "01d2c2fe8bf6b5aa4dd2fa2f26fe6a1e3ee053ceffd38e99253422153ffc5300"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
"python_version": "3.10" "python_version": "3.14"
}, },
"sources": [ "sources": [
{ {
@@ -18,39 +18,188 @@
"default": { "default": {
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897",
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.7'",
"version": "==2022.12.7" "version": "==2026.5.20"
}, },
"giteapy-soteria": { "charset-normalizer": {
"git": "https://github.com/Yousif-CS/giteapy.git",
"ref": "e0a089bdfb7ef6130b43727c50e78f176379db20"
},
"python-dateutil": {
"hashes": [ "hashes": [
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc",
"sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c",
"sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67",
"sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4",
"sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0",
"sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c",
"sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5",
"sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444",
"sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153",
"sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9",
"sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01",
"sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217",
"sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b",
"sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c",
"sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a",
"sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83",
"sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5",
"sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7",
"sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb",
"sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c",
"sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1",
"sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42",
"sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab",
"sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df",
"sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e",
"sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207",
"sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18",
"sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734",
"sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38",
"sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110",
"sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18",
"sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44",
"sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d",
"sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48",
"sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e",
"sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5",
"sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d",
"sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53",
"sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790",
"sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c",
"sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b",
"sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116",
"sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d",
"sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10",
"sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6",
"sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2",
"sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776",
"sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a",
"sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265",
"sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008",
"sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943",
"sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374",
"sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246",
"sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e",
"sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5",
"sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616",
"sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15",
"sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41",
"sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960",
"sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752",
"sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e",
"sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72",
"sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7",
"sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8",
"sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b",
"sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4",
"sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545",
"sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706",
"sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366",
"sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb",
"sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a",
"sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e",
"sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00",
"sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f",
"sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a",
"sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1",
"sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66",
"sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356",
"sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319",
"sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4",
"sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad",
"sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d",
"sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5",
"sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7",
"sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0",
"sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686",
"sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34",
"sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49",
"sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c",
"sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1",
"sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e",
"sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60",
"sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0",
"sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274",
"sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d",
"sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0",
"sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae",
"sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f",
"sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d",
"sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe",
"sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3",
"sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393",
"sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1",
"sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af",
"sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44",
"sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00",
"sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c",
"sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3",
"sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7",
"sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd",
"sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e",
"sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b",
"sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8",
"sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259",
"sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859",
"sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46",
"sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30",
"sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b",
"sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46",
"sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24",
"sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a",
"sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24",
"sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc",
"sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215",
"sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063",
"sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832",
"sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6",
"sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79",
"sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "markers": "python_version >= '3.7'",
"version": "==2.8.2" "version": "==3.4.7"
}, },
"six": { "idna": {
"hashes": [ "hashes": [
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "markers": "python_version >= '3.9'",
"version": "==1.16.0" "version": "==3.18"
},
"immutabledict": {
"hashes": [
"sha256:c9facdc0ff30fdb8e35bd16532026cac472a549e182c94fa201b51b25e4bf7bf",
"sha256:f844a669106cfdc73f47b1a9da003782fb17dc955a54c80972e0d93d1c63c514"
],
"markers": "python_version >= '3.8' and python_version < '4.0'",
"version": "==4.3.1"
},
"py-gitea": {
"hashes": [
"sha256:87bd5a684d29e8af4cef8cde3bd7abcc3f7dfc7394c58f7f313468ea91656cd4",
"sha256:f32a73aa951cb499b8cc2c0891a313794cd582c9dc2dc6aa80c237cbaacef361"
],
"index": "pypi",
"markers": "python_version >= '3.11'",
"version": "==0.2.10"
},
"requests": {
"hashes": [
"sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0",
"sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"
],
"markers": "python_version >= '3.10'",
"version": "==2.34.2"
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72", "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df",
"sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1" "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "index": "pypi",
"version": "==1.26.14" "markers": "python_version >= '3.9'",
"version": "==2.3.0"
} }
}, },
"develop": {} "develop": {}

View File

@@ -1,3 +1,4 @@
# Mike's Gitea Repo Migrator # Mike's Gitea Repo Migrator
Just a script to help make it a little easier to migrate an entire organization (with bulk selection) from one Gitea instance to another. Just a script to help make it a little easier to migrate an entire organization (with bulk selection) from one Gitea instance to another.
@@ -6,11 +7,11 @@ Supports changing destination names a bit, and adding topics to each transferred
by Mike Peralta 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 ## Requirements
* python 3.10 * python 3.14.2
* pipenv * pipenv
## Installation ## Installation
@@ -43,9 +44,20 @@ $ python ./main.py --help
You'll need to generate an *Application Token* for both the source and destination servers, and pass the token along to the command line. You'll need to generate an *Application Token* for both the source and destination servers, and pass the token along to the command line.
### SSL Verification ### SSL/TLS Verification
Pass the long switch `--no-verify-ssl` if any server sits behind a self-signed or wonky SSL certificate. You can disable SSL/TLS verification (risky) in the following ways:
* Pass the long switch `--no-verify-ssl` to completely disable SSL/TLS verification.
* Pass the switch `--no-verify-source-ssl` to disable certificate verification for the source server.
* Pass the switch `--no-verify-destination-ssl` to disable certificate verification for the destination server.
Also, I believe the newer version of urllib3 is either buggy or restricts self-signed CA/chains, as it wasn't working with mine. I had to put something in Pipfile to limit the urllib3 version to less than version 2.4:
```
urllib3 = "<2.4"
```
One test I forgot to disable was that I also added my intemediary Web CA certificate into the system certs directory, so I was unable to narrow down whether that contributed to success, or if it was only the older version of urllib3. When I run this program next, I might want to remove the intermediate cert from /usr/local/share/ca-certificates and rebuild to see if it still works.
### Destination Repo Names ### Destination Repo Names
@@ -55,3 +67,6 @@ You can tweak the destination repo names a bit by using a string that includes `
Topics will be duplicated from all source repos to their corresponding destination repos. You can specify additional topics with the `--destination-topic` switch. For example, to add the topic `migrated` to every repo, pass the switch `--destination-topic migrated`. Topics will be duplicated from all source repos to their corresponding destination repos. You can specify additional topics with the `--destination-topic` switch. For example, to add the topic `migrated` to every repo, pass the switch `--destination-topic migrated`.

56
domain/API.py Normal file
View File

@@ -0,0 +1,56 @@
import gitea
class API:
__DEFAULT_API_PATH = "/api/v1"
def __init__(self, verify_ssl, ca_bundle):
self.__verify_ssl = verify_ssl
self.__ca_bundle = ca_bundle
@staticmethod
def _make_api_base_url(hostname, port):
base = f"https://{hostname}"
if port is not None:
base += f":{port}"
return base
def get(
self,
hostname: str,
port: int,
token: str,
verify_ssl: bool = None
) -> gitea.Gitea:
url = API._make_api_base_url(
hostname=hostname,
port=port
)
ssl_verify_arg = True
if verify_ssl is not None:
ssl_verify_arg = verify_ssl
else:
ssl_verify_arg = self.__verify_ssl
# print(f"API::get -> hostname {hostname}")
# print(f"API::get -> verify_ssl was {verify_ssl}")
# print(f"API::get -> ssl_verify_arg became {ssl_verify_arg}")
if self.__ca_bundle is not None:
ssl_verify_arg = self.__ca_bundle
g = gitea.Gitea(
gitea_url=url,
token_text=token,
verify=ssl_verify_arg
)
return g

View File

@@ -1,27 +1,32 @@
import giteapy from domain.API import API
import certifi
import gitea
import logging import logging
import os
import sys import sys
class Migrator: class Migrator:
__DEFAULT_API_PATH = "/api/v1"
__REPO_ORIGINAL_NAME_TOKEN = "%N%" __REPO_ORIGINAL_NAME_TOKEN = "%N%"
def __init__( def __init__(
self, self,
source_host, source_port, source_token, source_host, source_port, source_token,
destination_host, destination_port, destination_token, destination_host, destination_port, destination_token,
verify_ssl: bool = True, verify_source_ssl: bool = True, verify_destination_ssl: bool = True,
ca_bundle: str = None
): ):
# noinspection PyTypeChecker # noinspection PyTypeChecker
self.__logger: logging.Logger = None self.__logger: logging.Logger = None
self._init_logger() self._init_logger()
self.__verify_ssl = True
self.__source_host = source_host self.__source_host = source_host
self.__source_port = source_port self.__source_port = source_port
self.__source_token = source_token self.__source_token = source_token
@@ -30,6 +35,31 @@ class Migrator:
self.__destination_port = destination_port self.__destination_port = destination_port
self.__destination_token = destination_token self.__destination_token = destination_token
self.__verify_ssl = verify_ssl
self.__verify_source_ssl = verify_source_ssl
self.__verify_destination_ssl = verify_destination_ssl
self.__ca_bundle = ca_bundle
api = API(
verify_ssl=self.__verify_ssl,
ca_bundle=self.__ca_bundle,
)
self.__source_api = api.get(
hostname=self.__source_host,
port=self.__source_port,
token=self.__source_token,
verify_ssl=self.__verify_source_ssl,
)
self.__destination_api = api.get(
hostname=self.__destination_host,
port=self.__destination_port,
token=self.__destination_token,
verify_ssl=self.__verify_destination_ssl,
)
def _init_logger(self): def _init_logger(self):
logger = logging.Logger(name=f"{type(self).__name__}", level=logging.INFO) logger = logging.Logger(name=f"{type(self).__name__}", level=logging.INFO)
@@ -39,67 +69,27 @@ class Migrator:
self.__logger = logger self.__logger = logger
def _get_user_api(self, hostname, port, token) -> giteapy.UserApi: def _make_destination_repo_name(self, pattern: str, repo: gitea.Repository):
conf = giteapy.Configuration()
conf.api_key['access_token'] = token
conf.host = self._make_api_base(hostname=hostname, port=port)
conf.verify_ssl = self.__verify_ssl
api = giteapy.UserApi(giteapy.ApiClient(conf))
return api
def _get_repo_api(self, hostname, port, token) -> giteapy.RepositoryApi:
conf = giteapy.Configuration()
conf.api_key['access_token'] = token
conf.host = self._make_api_base(hostname=hostname, port=port)
conf.verify_ssl = self.__verify_ssl
api = giteapy.RepositoryApi(giteapy.ApiClient(conf))
return api
def _get_org_apis(self) -> (giteapy.OrganizationApi, giteapy.OrganizationApi):
api_source = self._get_org_api(
hostname=self.__source_host, port=self.__source_port,
token=self.__source_token
)
api_destination = self._get_org_api(
hostname=self.__destination_host, port=self.__destination_port,
token=self.__destination_token
)
return api_source, api_destination
def _get_org_api(self, hostname, port, token) -> giteapy.OrganizationApi:
conf = giteapy.Configuration()
conf.api_key['access_token'] = token
conf.host = self._make_api_base(hostname=hostname, port=port)
conf.verify_ssl = self.__verify_ssl
api = giteapy.OrganizationApi(giteapy.ApiClient(conf))
return api
def _make_api_base(self, hostname, port):
base = f"https://{hostname}"
if port is not None:
base += f":{port}"
base += self.__DEFAULT_API_PATH
return base
def _make_destination_repo_name(self, pattern: str, repo: giteapy.Repository):
repo_name = pattern.replace(self.__REPO_ORIGINAL_NAME_TOKEN, repo.name) repo_name = pattern.replace(self.__REPO_ORIGINAL_NAME_TOKEN, repo.name)
return repo_name return repo_name
def set_verify_ssl(self, b: bool): def set_ca_bundle(self, bundle_path: str):
self.__verify_ssl = b 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()}")
self.__logger.info(f"os.environ.REQUESTS_CA_BUNDLE before: {os.environ['REQUESTS_CA_BUNDLE']}")
os.environ["REQUESTS_CA_BUNDLE"] = bundle_path
self.__logger.info(f"os.environ.REQUESTS_CA_BUNDLE after: {os.environ['REQUESTS_CA_BUNDLE']}")
# TODO: JUST TESTING
self.__verify_ssl = bundle_path
def migrate_entire_org( def migrate_entire_org(
self, self,
@@ -115,33 +105,53 @@ class Migrator:
assert do_destination_copy_topics is not None, "Destination directive to copy source topics should be specified" assert do_destination_copy_topics is not None, "Destination directive to copy source topics should be specified"
# api_source, api_destination = self._get_org_apis() # api_source, api_destination = self._get_org_apis()
api_source: giteapy.OrganizationApi # api_source: giteapy.OrganizationApi
api_destination: giteapy.OrganizationApi # api_destination: giteapy.OrganizationApi
# Tattle on certify, then modify
self.__logger.info(f"Certifi is currently using CA bundle: {certifi.where()}")
if self.__ca_bundle is not None:
self.set_ca_bundle(
bundle_path=self.__ca_bundle
)
self.__logger.info(f"After modification, Certifi is now using CA bundle: {certifi.where()}")
# Grab all org repos # Grab all org repos
source_repos = self._fetch_all_org_repos(org=source_org) source_repos = self._fetch_all_org_repos(
org_name=source_org
)
self.__logger.info(f"Found {len(source_repos)} repos on source:") self.__logger.info(f"Found {len(source_repos)} repos on source:")
for repo in source_repos: for repo in source_repos:
repo: giteapy.Repository repo: gitea.Repository
self.__logger.info(f"- #{repo.id} {repo.full_name}") self.__logger.info(f"- {repo.get_full_name()}")
print() print()
# Filter # Filter
source_repos = self._filter_repos_for_required_topics(repos=source_repos, topics=source_topics) source_repos = self._filter_repos_for_required_topics(
repos=source_repos,
topics_required=source_topics
)
print() print()
self.__logger.info(f"Have {len(source_repos)} remaining repos after topic filtering:")
for repo in source_repos:
repo: gitea.Repository
self.__logger.info(f"- {repo.get_full_name()}")
repos_migrate = [] repos_migrate = []
repos_ignore = [] repos_ignore = []
go_right_now = False go_right_now = False
for repo in source_repos: for repo in source_repos:
repo: giteapy.Repository repo: gitea.Repository
while True: while True:
if interactive: if interactive:
response = input(f"Migrate repo #{repo.id} \"{repo.full_name}\" ? (Y)es, (N)o, (G)o right now, (Q)uit ==> ") response = input(
f"Migrate repo #{repo.id} \"{repo.full_name}\" ?"
" (Y)es, (N)o, (G)o right now, (Q)uit ==> "
)
response = response.lower() response = response.lower()
else: else:
response = "y" response = "y"
@@ -169,26 +179,32 @@ class Migrator:
if go_right_now: if go_right_now:
break break
# # Announce repo destination names
self.__logger.info("") self.__logger.info("")
if len(repos_migrate): if len(repos_migrate):
self.__logger.info("Repos to migrate:") self.__logger.info("Repos to migrate:")
for repo in repos_migrate: for repo in repos_migrate:
repo: giteapy.Repository repo: gitea.Repository
destination_name = self._make_destination_repo_name(pattern=destination_repo_name, repo=repo) destination_name = self._make_destination_repo_name(
self.__logger.info(f"#{repo.id} \"{repo.name}\" ==> \"{destination_name}\"") pattern=destination_repo_name, repo=repo
)
self.__logger.info(
f"#{repo.id} \"{repo.name}\"\n> \"{destination_name}\""
)
else: else:
self.__logger.info("No repos marked to migrate") self.__logger.info("No repos marked to migrate")
# Announce manually ignored repos
self.__logger.info("") self.__logger.info("")
if len(repos_ignore): if len(repos_ignore):
self.__logger.info("Repos to ignore:") self.__logger.info("Repos to ignore:")
for repo in repos_ignore: for repo in repos_ignore:
repo: giteapy.Repository repo: gitea.Repository
self.__logger.info(f"#{repo.id} \"{repo.name}\"") self.__logger.info(f"#{repo.id} \"{repo.get_full_name()}\"")
else: else:
self.__logger.info("No repos marked to ignore") self.__logger.info("No repos marked to ignore")
# Migrate
if len(repos_migrate): if len(repos_migrate):
confirmation = input("Do you confirm the above selections? Enter MIGRATE ==> ") confirmation = input("Do you confirm the above selections? Enter MIGRATE ==> ")
@@ -196,71 +212,87 @@ class Migrator:
if confirmation == "MIGRATE": if confirmation == "MIGRATE":
self.__logger.info("Confirmation received; Processing ... ") 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_org_name=destination_org,
destination_repo_name=destination_repo_name, destination_repo_name=destination_repo_name,
destination_topics=destination_topics, destination_topics=destination_topics,
do_destination_copy_topics=do_destination_copy_topics, do_destination_copy_topics=do_destination_copy_topics,
repos=repos_migrate 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) self._delete_migrated_repos(
source_org_name=source_org,
repos=source_repos_successful
)
else: else:
self.__logger.info("Confirmation not received; Won't do anything.") self.__logger.info("Confirmation not received; Won't do anything.")
def _fetch_all_org_repos(self, org: str): def _fetch_all_org_repos(self, org_name: str):
api_source, api_destination = self._get_org_apis() org = gitea.Organization.request(
api_source: giteapy.OrganizationApi gitea=self.__source_api,
name=org_name
)
source_repos = [] # Grabs all pages automatically
repos = org.get_repositories()
page = 0 return repos
while True:
page += 1 # Starts at 1 for some reason
source_repos_page = api_source.org_list_repos(org, page=page, limit=25)
if len(source_repos_page) == 0:
break
source_repos.extend(source_repos_page)
return source_repos
def _filter_repos_for_required_topics( def _filter_repos_for_required_topics(
self, self,
repos: list[giteapy.Repository], repos: list[gitea.Repository],
topics: list[str] topics_required: list[str]
) -> list[giteapy.Repository]: ) -> list[gitea.Repository]:
self.__logger.info(f"Filtering source repos for required topics: {topics}") self.__logger.info(
f"Filtering source repos for required topics: {topics_required}"
)
repos_keep = [] repos_keep = []
repos_reject = [] repos_reject = []
repo_topics = {} repo_topics = {}
api_source_repos = self._get_repo_api(
hostname=self.__source_host, port=self.__source_port,
token=self.__source_token
)
for repo in repos: for repo in repos:
repo_topics[repo.id] = api_source_repos.repo_list_topics(owner=repo.owner.login, repo=repo.name) repo: gitea.Repository
repo_topics[repo.id] = repo_topics[repo.id].topics
if self._check_required_topics(topics_present=repo_topics[repo.id], topics_required=topics): repo_key = repo.get_full_name()
topics_present = repo.get_topics()
repo_topics[repo_key] = topics_present
if self._check_required_topics(
topics_present=repo_topics[repo_key],
topics_required=topics_required
):
repos_keep.append(repo) repos_keep.append(repo)
else: else:
repos_reject.append(repo) repos_reject.append(repo)
self.__logger.info("") self.__logger.info("")
self.__logger.info(f"\nKeeping {len(repos_keep)} repos because they contain all required topics ({topics}):") self.__logger.info(
f"\nKeeping {len(repos_keep)} repos"
f" because they contain all required topics ({topics_required}):"
)
if len(repos_keep) > 0: if len(repos_keep) > 0:
for repo in repos_keep: for repo in repos_keep:
self.__logger.info(f"> {repo.full_name}") self.__logger.info(f"> {repo.full_name}")
@@ -268,10 +300,12 @@ class Migrator:
self.__logger.info("> None") self.__logger.info("> None")
self.__logger.info("") self.__logger.info("")
self.__logger.info(f"Rejecting {len(repos_reject)} repos because they don't contain all required topics:") self.__logger.info(
f"Rejecting {len(repos_reject)} repos because they don't contain all required topics:"
)
if len(repos_reject) > 0: if len(repos_reject) > 0:
for repo in repos_reject: for repo in repos_reject:
self.__logger.info(f"> {repo.full_name} ({repo_topics[repo.id]})") self.__logger.info(f"> {repo.full_name}")
else: else:
self.__logger.info("> None") self.__logger.info("> None")
@@ -295,95 +329,115 @@ class Migrator:
repos: list repos: list
): ):
api_source, api_destination = self._get_org_apis() # api_source, api_destination = self._get_org_apis()
# destination_org = api_destination.org_get(org=destination_org_name)
# destination_org: giteapy.Organization
destination_org = api_destination.org_get(org=destination_org_name) api_dest_org = gitea.Organization.request(
destination_org: giteapy.Organization gitea=self.__destination_api,
self.__logger.info(f"Destination organization: {destination_org.full_name}") name=destination_org_name
api_source_repos = self._get_repo_api(
hostname=self.__source_host, port=self.__source_port,
token=self.__source_token
) )
self.__logger.info(f"Destination organization: {api_dest_org.full_name}")
source_repos_successful = [] source_repos_successful = []
source_repos_failed = []
for source_repo in repos: for source_repo in repos:
source_repo: giteapy.Repository source_repo: gitea.Repository
this_destination_repo_name = destination_repo_name.replace("%N%", source_repo.name) this_destination_repo_name = destination_repo_name.replace(
"%N%",
source_repo.name
)
source_repo_topics = api_source_repos.repo_list_topics(owner=source_repo.owner.login, repo=source_repo.name) self.__logger.info(
source_repo_topics = source_repo_topics.topics f"Migrating: {source_repo.name} ==> {this_destination_repo_name}"
)
migrate_body = giteapy.MigrateRepoForm( source_repo_topics = source_repo.get_topics()
mirror=False,
try:
repo_new = gitea.Repository.migrate_repo(
gitea=self.__destination_api,
service="gitea", # type of remote service
clone_addr=source_repo.clone_url, clone_addr=source_repo.clone_url,
uid=destination_org.id,
private=source_repo.private,
repo_name=this_destination_repo_name, repo_name=this_destination_repo_name,
description=source_repo.description, description=source_repo.description,
labels=True, issues=True, pull_requests=True, releases=True, milestones=True, wiki=True private=source_repo.private,
auth_token=self.__source_token,
auth_username=None,
auth_password=None,
mirror=False,
mirror_interval=None,
# lfs=False,
# lfs_endpoint="",
wiki=True,
labels=True,
issues=True,
pull_requests=True,
releases=True,
milestones=True,
repo_owner=destination_org_name,
) )
# TODO: These three lines represent feature request to giteapy authors
migrate_body.auth_token = self.__source_token
migrate_body.swagger_types["auth_token"] = "str"
migrate_body.attribute_map["auth_token"] = "auth_token"
self.__logger.debug("Migrate body:") except Exception as e:
self.__logger.debug(migrate_body) self.__logger.error(
f"Failed to execute repo migration request:"
destination_api = self._get_repo_api( f"\n{e}"
hostname=self.__destination_host,
port=self.__destination_port,
token=self.__destination_token,
) )
repo_new = destination_api.repo_migrate(body=migrate_body) source_repos_failed.append(
(source_repo, e)
)
raise e
continue
self.__logger.debug(f"Migration result: {repo_new}") self.__logger.debug(f"Migration result: {repo_new}")
repo_new: giteapy.Repository repo_new: gitea.Repository
assert repo_new.name == this_destination_repo_name,\ assert repo_new.name == this_destination_repo_name, \
"New repository didn't end up with the correct name. Failure?" "New repository didn't end up with the correct name. Failure?"
# Copy source topics? # Copy source topics?
if do_destination_copy_topics: if do_destination_copy_topics:
for topic in source_repo_topics: for topic in source_repo_topics:
self.__logger.debug(f"Appending source topic to new repo: {topic}") self.__logger.debug(f"Appending source topic to new repo: {topic}")
destination_api.repo_add_topc(
owner=destination_org.username, repo_new.add_topic(topic=topic)
repo=repo_new.name,
topic=topic,
)
# Add specified topics # Add specified topics
for topic in destination_topics: for topic in destination_topics:
self.__logger.debug(f"Appending topic to new repo: {topic}") self.__logger.debug(f"Appending topic to new repo: {topic}")
destination_api.repo_add_topc(
owner=destination_org.username, repo_new.add_topic(topic=topic)
repo=repo_new.name,
topic=topic,
)
source_repos_successful.append(source_repo) 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]): def _delete_migrated_repos(self, source_org_name: str, repos: list[gitea.Repository]):
repo_api = self._get_repo_api( if len(repos) == 0:
hostname=self.__source_host, self.__logger.warning(f"Cannot delete any migrated repos because none were successful!")
port=self.__source_port, return
token=self.__source_token
)
repo_api: giteapy.RepositoryApi
self.__logger.info("") self.__logger.info("")
self.__logger.info("Can now delete the following successfully migrated repos:") self.__logger.info(f"Can now delete repos from source org: {source_org_name}")
self.__logger.info(f"Will delete {len(repos)} successfully migrated repos:")
for r in repos: for r in repos:
r: gitea.Repository
self.__logger.info(f"> #{r.id} \"{r.full_name}\" ==> {r.clone_url}") self.__logger.info(f"> #{r.id} \"{r.full_name}\" ==> {r.clone_url}")
response = input("Would you like to delete the successfully migrated repos? Type DELETE ==> ") # Ask the user to confirm deletion
response = input(
"Would you like to delete the successfully migrated repos? Type DELETE ==> "
)
if response != "DELETE": if response != "DELETE":
self.__logger.info("Okay, won't delete migrated repos.") self.__logger.info("Okay, won't delete migrated repos.")
return return
@@ -392,7 +446,9 @@ class Migrator:
do_delete_all = False do_delete_all = False
for repo in repos: for repo in repos:
self.__logger.info(f"Next repo to delete: #{repo.id} \"{repo.full_name}\"") repo: gitea.Repository
self.__logger.info(f"Next repo to delete: \"{repo.full_name}\"")
do_delete = True if do_delete_all else False do_delete = True if do_delete_all else False
if do_delete is False: if do_delete is False:
@@ -426,7 +482,4 @@ class Migrator:
self.__logger.info(f"Deleting repo: {repo.full_name}") self.__logger.info(f"Deleting repo: {repo.full_name}")
repo_api.repo_delete( repo.delete()
owner=source_org_name,
repo=repo.name
)

54
init-environment Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
log()
{
echo "[Mike's Gitea Repo Migrator - Init Env] $1"
}
complain()
{
echo "[Mike's Gitea Repo Migrator - Init Env] $1" 1>&2
}
die()
{
complain "Fatal: $1"
exit 1
}
SCRIPT_PATH=$(readlink -f "$0")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
SCRIPT_NAME=$(basename "$SCRIPT_PATH")
log "Begin ${SCRIPT_NAME}"
log "Script path: ${SCRIPT_PATH}"
log "Script dir: ${SCRIPT_DIR}"
log "PATH: ${PATH}"
log "PWD before switching: $(pwd)"
cd "${SCRIPT_DIR}" || die "Failed to switch to project directory: ${SCRIPT_DIR}"
log "PWD after switching: $(pwd)"
log "Printing environment:"
printenv
log "Ensuring python installation with pyenv"
pyenv versions
pyenv install --skip-existing || die "Failed to ensure python installation with pyenv"
log "Installing/upgrading pip and pipenv"
pip install --upgrade pip pipenv || die "Failed to install/upgrade pip and pipenv"
#log "Removing old pip environment"
#pipenv --rm # Don't die because this will return an error if the env didn't already exist
# Delete and relock, because frikkin nvidia driver deps are inconsistent between x86_64 and RPi
#log "Re-locking pip dependencies"
#rm Pipfile.lock
#pipenv lock || die "Unable to pipenv lock !"
# Actually install/sync
log "Syncing pip dependencies"
pipenv sync || die "Failed to sync pip environment with pipenv"

55
main.py
View File

@@ -4,6 +4,7 @@ from domain.Migrator import Migrator
import argparse import argparse
import os
def main(): def main():
@@ -12,6 +13,16 @@ def main():
prog="Mike's Gitea Repo Migrator - Move repositories from one Gitea instance to another" prog="Mike's Gitea Repo Migrator - Move repositories from one Gitea instance to another"
) )
parser.add_argument(
"--cwd", "--working-directory",
dest="working_directory",
required=False,
default=None,
help="Specify the working directory"
)
######################
##### Source Arguments
parser.add_argument( parser.add_argument(
"--source-hostname", "--source-host", "--source-hostname", "--source-host",
dest="source_hostname", dest="source_hostname",
@@ -23,7 +34,7 @@ def main():
dest="source_port", dest="source_port",
required=False, required=False,
default=None, 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( parser.add_argument(
"--source-token", "--source-token",
@@ -46,6 +57,8 @@ def main():
" Any repository that doesn't have all required topics will be skipped" " Any repository that doesn't have all required topics will be skipped"
) )
###########################
##### Destination Arguments
parser.add_argument( parser.add_argument(
"--destination-hostname", "--dest-hostname", "--destination-host", "--dest-host", "--destination-hostname", "--dest-hostname", "--destination-host", "--dest-host",
dest="destination_hostname", dest="destination_hostname",
@@ -57,7 +70,7 @@ def main():
dest="destination_port", dest="destination_port",
required=False, required=False,
default=None, 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( parser.add_argument(
"--destination-token", "--dest-token", "--destination-token", "--dest-token",
@@ -116,24 +129,56 @@ def main():
help="Do not ask to confirm each migration; Migrate all repos quickly.", help="Do not ask to confirm each migration; Migrate all repos quickly.",
) )
###################
##### SSL/TLS Stuff
parser.add_argument( parser.add_argument(
"--no-verify-ssl", "--no-verify-ssl",
dest="verify_ssl", dest="verify_ssl",
default=True, default=True,
action="store_false", action="store_false",
help="Don't verify SSL certificates", help="Don't verify SSL/TLS certificates",
)
parser.add_argument(
"--no-verify-source-ssl",
dest="verify_source_ssl",
default=None,
action="store_false",
help="Don't verify the SSL/TLS certificate for the source host",
)
parser.add_argument(
"--no-verify-destination-ssl",
dest="verify_destination_ssl",
default=None,
action="store_false",
help="Don't verify the SSL/TLS certificate for the destination host",
)
# Doesn't seem to be helpful?
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() args = parser.parse_args()
if args.working_directory is not None:
os.chdir(args.working_directory)
mig = Migrator( mig = Migrator(
source_host=args.source_hostname, source_host=args.source_hostname,
source_port=args.source_port, source_port=args.source_port,
source_token=args.source_token, source_token=args.source_token,
destination_host=args.destination_hostname, destination_host=args.destination_hostname,
destination_port=args.destination_port, destination_port=args.destination_port,
destination_token=args.destination_token destination_token=args.destination_token,
verify_ssl=args.verify_ssl,
verify_source_ssl=args.verify_source_ssl,
verify_destination_ssl=args.verify_destination_ssl,
ca_bundle=args.ca_bundle
) )
mig.set_verify_ssl(args.verify_ssl)
mig.migrate_entire_org( mig.migrate_entire_org(
interactive=args.interactive, interactive=args.interactive,
source_org=args.source_org, source_org=args.source_org,