mirror of
https://github.com/Xtao-Labs/RepoTransfer.git
synced 2024-11-21 22:48:05 +00:00
support migrate org repo
This commit is contained in:
parent
ac9ab94f8b
commit
f5bfca7631
3
.gitignore
vendored
3
.gitignore
vendored
@ -157,4 +157,5 @@ cython_debug/
|
|||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
.idea/
|
||||||
|
env.py
|
||||||
|
0
defs/__init__.py
Normal file
0
defs/__init__.py
Normal file
94
defs/client.py
Normal file
94
defs/client.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
from env import URL, TOKEN
|
||||||
|
|
||||||
|
from httpx import AsyncClient, TimeoutException
|
||||||
|
|
||||||
|
|
||||||
|
class GiteaError(Exception):
|
||||||
|
def __init__(self, status_code: int, message: str):
|
||||||
|
self.status_code = status_code
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"[{self.status_code}] {self.message}"
|
||||||
|
|
||||||
|
|
||||||
|
def retry(func):
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
for _ in range(3):
|
||||||
|
try:
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
except TimeoutException:
|
||||||
|
continue
|
||||||
|
raise GiteaError(500, "Failed to get response after 3 retries")
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class Gitea:
|
||||||
|
def __init__(self, url: str, token: str):
|
||||||
|
self.url = url
|
||||||
|
self.token = token
|
||||||
|
self.client = AsyncClient(timeout=10)
|
||||||
|
self.headers = {
|
||||||
|
"Content-type": "application/json",
|
||||||
|
}
|
||||||
|
if token:
|
||||||
|
self.headers["Authorization"] = "token " + token
|
||||||
|
|
||||||
|
def __get_url(self, endpoint):
|
||||||
|
url = self.url + "/api/v1" + endpoint
|
||||||
|
return url
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_result(result) -> dict:
|
||||||
|
"""Parses the result-JSON to a dict."""
|
||||||
|
if result.text and len(result.text) > 3:
|
||||||
|
return result.json()
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@retry
|
||||||
|
async def requests_get(self, endpoint: str, params: dict = None) -> dict:
|
||||||
|
combined_params = {}
|
||||||
|
if not params:
|
||||||
|
params = {}
|
||||||
|
combined_params.update(params)
|
||||||
|
request = await self.client.get(
|
||||||
|
url=self.__get_url(endpoint), headers=self.headers, params=combined_params
|
||||||
|
)
|
||||||
|
if request.status_code not in [200, 201]:
|
||||||
|
message = f"Received status code: {request.status_code} ({request.url})"
|
||||||
|
if request.status_code in [403]:
|
||||||
|
message = f"Unauthorized: {request.url} - Check your permissions and try again! ({message})"
|
||||||
|
raise GiteaError(request.status_code, message)
|
||||||
|
return self.parse_result(request)
|
||||||
|
|
||||||
|
@retry
|
||||||
|
async def requests_post(self, endpoint: str, data: dict):
|
||||||
|
request = await self.client.post(
|
||||||
|
url=self.__get_url(endpoint), headers=self.headers, json=data,
|
||||||
|
)
|
||||||
|
if request.status_code not in [200, 201, 202]:
|
||||||
|
raise GiteaError(
|
||||||
|
request.status_code,
|
||||||
|
f"Received status code: {request.status_code} ({request.url}), {request.text}"
|
||||||
|
)
|
||||||
|
return self.parse_result(request)
|
||||||
|
|
||||||
|
async def get_version(self) -> str:
|
||||||
|
result = await self.requests_get("/version")
|
||||||
|
return result["version"]
|
||||||
|
|
||||||
|
async def migrate_repo(self, owner: str, repo_name: str, git_url: str):
|
||||||
|
result = await self.requests_post(
|
||||||
|
"/repos/migrate",
|
||||||
|
data={
|
||||||
|
"clone_addr": git_url,
|
||||||
|
"repo_name": repo_name,
|
||||||
|
"repo_owner": owner,
|
||||||
|
"mirror": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
gitea = Gitea(URL, TOKEN)
|
12
defs/github.py
Normal file
12
defs/github.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from defs.models import GithubRepo
|
||||||
|
|
||||||
|
from httpx import AsyncClient
|
||||||
|
|
||||||
|
|
||||||
|
async def get_github_repos(org_name: str) -> List[GithubRepo]:
|
||||||
|
async with AsyncClient() as client:
|
||||||
|
resp = await client.get(f"https://api.github.com/orgs/{org_name}/repos")
|
||||||
|
data = resp.json()
|
||||||
|
return [GithubRepo(**repo) for repo in data]
|
9
defs/models.py
Normal file
9
defs/models.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class GithubRepo(BaseModel):
|
||||||
|
name: str
|
||||||
|
full_name: str
|
||||||
|
private: bool
|
||||||
|
html_url: str
|
||||||
|
clone_url: str
|
2
env.example.py
Normal file
2
env.example.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
URL = "http://"
|
||||||
|
TOKEN = ""
|
25
main.py
Normal file
25
main.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from defs.github import get_github_repos
|
||||||
|
from defs.client import gitea, GiteaError
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print("gitea version:", await gitea.get_version())
|
||||||
|
org_name = input("Please input the org name: ")
|
||||||
|
repos = await get_github_repos(org_name)
|
||||||
|
for repo in repos:
|
||||||
|
if repo.private:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
await gitea.migrate_repo(org_name, repo.name, repo.clone_url)
|
||||||
|
print(f"Repo {repo.name} migrated successfully!")
|
||||||
|
except GiteaError as e:
|
||||||
|
if e.status_code == 409:
|
||||||
|
print(f"Repo {repo.name} already exists!")
|
||||||
|
else:
|
||||||
|
print(f"Failed to migrate repo {repo.name}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
asyncio.run(main())
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
httpx
|
||||||
|
pydantic
|
Loading…
Reference in New Issue
Block a user