Skip to content

Commit 44a9350

Browse files
Uploading the API calls
1 parent 60d0c3a commit 44a9350

File tree

3 files changed

+269
-3
lines changed

3 files changed

+269
-3
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
# gitea-mirror-manager
2-
MIrrors external repositories into internal Gitea servers
1+
# Gitea Mirror Manager
2+
Mirrors external repositories into internal Gitea servers

entrypoint.sh

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#!/usr/bin/env sh
22

3-
python gitea_mirror_manager/mirrors.py
3+
python -c 'from gitea_mirror_manager import mirrors; mirrors.main()'

gitea_mirror_manager/mirrors.py

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
import json
2+
import logging
3+
import os
4+
from typing import Any
5+
6+
import requests
7+
from requests import Response
8+
from requests.auth import HTTPBasicAuth
9+
10+
logger = logging.getLogger("mirror_manager")
11+
logging.basicConfig(level=logging.INFO)
12+
13+
DEFAULT_TIMEOUT: int = 5 * 60
14+
15+
# Mirror server configuration
16+
17+
MIRROR_SERVER_TOKEN_NAME: str = "mirror_api_token"
18+
MIRROR_SERVER_URL: str = os.environ["MIRROR_SERVER_URL"]
19+
MIRROR_SERVER_USERNAME: str = os.environ["MIRROR_SERVER_USERNAME"]
20+
MIRROR_SERVER_PASSWORD: str = os.environ["MIRROR_SERVER_PASSWORD"]
21+
22+
# Workspace server configuration
23+
WORKSPACE_SERVER_TOKEN_NAME: str = "push_mirror_api_token"
24+
WORKSPACE_SERVER_URL: str = os.environ["WORKSPACE_SERVER_URL"]
25+
WORKSPACE_SERVER_USERNAME: str = os.environ["WORKSPACE_SERVER_USERNAME"]
26+
WORKSPACE_SERVER_PASSWORD: str = os.environ["WORKSPACE_SERVER_PASSWORD"]
27+
28+
REPOSITORY_DATA: dict[str, list[dict[str, str]]] = json.loads(
29+
os.environ["REPOSITORY_DATA"]
30+
)
31+
32+
33+
def delete_token(username: str, password: str, token_name: str, gitea_url: str) -> None:
34+
headers: dict[str, str] = {"Content-Type": "application/json"}
35+
basic_auth = HTTPBasicAuth(username, password)
36+
37+
response: Response = requests.delete(
38+
f"{gitea_url}/api/v1/users/{username}/tokens/{token_name}",
39+
auth=basic_auth,
40+
headers=headers,
41+
timeout=DEFAULT_TIMEOUT,
42+
)
43+
44+
if not response.status_code == requests.codes.no_content:
45+
logging.info(
46+
f"Cannot delete token {token_name} for user {username}."
47+
f" Status code: {response.status_code}"
48+
)
49+
else:
50+
logging.info(
51+
f"Token {token_name} for user {username} deleted."
52+
f" Status code: {response.status_code}"
53+
)
54+
55+
56+
def create_token(
57+
username: str, password: str, token_name: str, gitea_url: str, scopes: list[str]
58+
) -> Any:
59+
logger.info(f"Creating API token {token_name} for user {username}")
60+
61+
headers: dict[str, str] = {"Content-Type": "application/json"}
62+
basic_auth = HTTPBasicAuth(username, password)
63+
data: dict[str, str | list[str]] = {
64+
"name": token_name,
65+
"scopes": scopes,
66+
}
67+
68+
response: Response = requests.post(
69+
f"{gitea_url}/api/v1/users/{username}/tokens",
70+
auth=basic_auth,
71+
headers=headers,
72+
data=json.dumps(data),
73+
timeout=DEFAULT_TIMEOUT,
74+
)
75+
76+
if not response.status_code == requests.codes.created:
77+
error_message: str = (
78+
f"Cannot create tokens for user {username}."
79+
f" Status code: {response.status_code}."
80+
f" Response {response.json()}"
81+
)
82+
83+
raise Exception(error_message)
84+
85+
return response.json()["sha1"]
86+
87+
88+
def create_migration(
89+
repository_url: str,
90+
repository_name: str,
91+
repository_auth_token: str,
92+
gitea_url: str,
93+
token: str,
94+
service: str,
95+
) -> tuple[Any, Any]:
96+
logger.info(f"Creating a migration for repository {repository_url}")
97+
98+
headers: dict[str, str] = {"Content-Type": "application/json"}
99+
params: dict[str, str] = {"access_token": token}
100+
101+
data: dict[str, str | bool] = {
102+
"clone_addr": repository_url,
103+
"auth_token": repository_auth_token,
104+
"mirror": True,
105+
"mirror_interval": "0h10m0s",
106+
"private": False,
107+
"repo_name": repository_name,
108+
"service": service,
109+
}
110+
111+
response: Response = requests.post(
112+
f"{gitea_url}/api/v1/repos/migrate",
113+
params=params,
114+
headers=headers,
115+
data=json.dumps(data),
116+
timeout=DEFAULT_TIMEOUT,
117+
)
118+
119+
if not response.status_code == requests.codes.created:
120+
error_message: str = (
121+
f"Cannot create migration for repository {repository_url}."
122+
f" Status code: {response.status_code}. Response {response.json()}"
123+
)
124+
125+
raise Exception(error_message)
126+
127+
logger.info(
128+
f"Migration created for user {response.json()['owner']['username']}"
129+
f" at repository {response.json()['name']}"
130+
)
131+
return response.json()["owner"]["username"], response.json()["name"]
132+
133+
134+
def delete_repository(
135+
username: str, gitea_url: str, repository_name: str, token: str
136+
) -> None:
137+
logger.info(
138+
f"Attempting to delete repository {repository_name} for user {username}"
139+
)
140+
141+
headers: dict[str, str] = {"Content-Type": "application/json"}
142+
params: dict[str, str] = {"access_token": token}
143+
144+
response: Response = requests.delete(
145+
f"{gitea_url}/api/v1/repos/{username}/{repository_name}",
146+
params=params,
147+
headers=headers,
148+
timeout=DEFAULT_TIMEOUT,
149+
)
150+
151+
if response.status_code == requests.codes.no_content:
152+
logging.info("Repository successfully deleted.")
153+
else:
154+
logging.info(f"Cannot delete repository. Response {response.json()}")
155+
156+
157+
def obtain_api_token(
158+
token_name: str, username: str, password: str, scopes: list[str], gitea_url: str
159+
) -> Any:
160+
delete_token(
161+
username=username, password=password, token_name=token_name, gitea_url=gitea_url
162+
)
163+
164+
token_value = create_token(
165+
username=username,
166+
password=password,
167+
token_name=token_name,
168+
scopes=scopes,
169+
gitea_url=gitea_url,
170+
)
171+
172+
return token_value
173+
174+
175+
def get_repositories(owner: str, gitea_url: str, token: str) -> list[str]:
176+
logger.info(f"Searching for repositories of {owner} at {gitea_url}")
177+
178+
headers: dict[str, str] = {"Content-Type": "application/json"}
179+
params: dict[str, str] = {"access_token": token}
180+
181+
response: Response = requests.get(
182+
f"{gitea_url}/api/v1/repos/search",
183+
params=params,
184+
headers=headers,
185+
timeout=DEFAULT_TIMEOUT,
186+
)
187+
188+
if not response.status_code == requests.codes.ok:
189+
error_message: str = (
190+
f"Could not list repositories for user {owner}. "
191+
f"Status code: {response.status_code}. Response {response.json()}"
192+
)
193+
194+
raise Exception(error_message)
195+
196+
return [
197+
repository["name"]
198+
for repository in response.json()["data"]
199+
if repository["owner"]["username"] == owner
200+
]
201+
202+
203+
def main() -> None:
204+
gitea_mirror_token = obtain_api_token(
205+
token_name=MIRROR_SERVER_TOKEN_NAME,
206+
username=MIRROR_SERVER_USERNAME,
207+
password=MIRROR_SERVER_PASSWORD,
208+
scopes=["write:repository"],
209+
gitea_url=MIRROR_SERVER_URL,
210+
)
211+
212+
workspace_gitea_token = obtain_api_token(
213+
token_name=WORKSPACE_SERVER_TOKEN_NAME,
214+
username=WORKSPACE_SERVER_USERNAME,
215+
password=WORKSPACE_SERVER_PASSWORD,
216+
gitea_url=WORKSPACE_SERVER_URL,
217+
scopes=["write:repository", "write:user"],
218+
)
219+
220+
for repository_name in get_repositories(
221+
owner=MIRROR_SERVER_USERNAME,
222+
gitea_url=MIRROR_SERVER_URL,
223+
token=gitea_mirror_token,
224+
):
225+
delete_repository(
226+
username=MIRROR_SERVER_USERNAME,
227+
gitea_url=MIRROR_SERVER_URL,
228+
repository_name=repository_name,
229+
token=gitea_mirror_token,
230+
)
231+
232+
for repository_name in get_repositories(
233+
owner=WORKSPACE_SERVER_USERNAME,
234+
gitea_url=WORKSPACE_SERVER_URL,
235+
token=workspace_gitea_token,
236+
):
237+
delete_repository(
238+
username=WORKSPACE_SERVER_USERNAME,
239+
gitea_url=WORKSPACE_SERVER_URL,
240+
repository_name=repository_name,
241+
token=workspace_gitea_token,
242+
)
243+
244+
for repository in REPOSITORY_DATA["repositories"]:
245+
246+
owner, repository_name = create_migration(
247+
repository_name=repository["repository_name"],
248+
repository_url=repository["repository_url"],
249+
repository_auth_token=repository["repository_auth_token"],
250+
gitea_url=MIRROR_SERVER_URL,
251+
token=gitea_mirror_token,
252+
service="github", # TODO(cgavidia): Maybe this can be a parameter in config.
253+
)
254+
255+
create_migration(
256+
repository_name=f"{repository['repository_name']}-mirror",
257+
repository_url=f"{MIRROR_SERVER_URL}/{owner}/{repository_name}",
258+
repository_auth_token=gitea_mirror_token,
259+
gitea_url=WORKSPACE_SERVER_URL,
260+
token=workspace_gitea_token,
261+
service="gitea",
262+
)
263+
264+
265+
if __name__ == "__main__":
266+
main()

0 commit comments

Comments
 (0)