|
6 | 6 | from datetime import datetime |
7 | 7 | from pathlib import Path |
8 | 8 | from time import sleep |
| 9 | +from typing import Optional |
9 | 10 |
|
10 | | -import requests |
11 | | -from requests import Response |
| 11 | +import requests # type: ignore[import-untyped] |
| 12 | +from requests import Response # type: ignore[import-untyped] |
12 | 13 |
|
13 | 14 | from sutta_publisher.shared import EDITIONS_REPO_URL, LAST_RUN_DATE_FILE_URL |
14 | 15 | from sutta_publisher.shared.value_objects.edition import EditionResult |
|
19 | 20 | ERROR_SLEEP_TIME = 1 # in seconds |
20 | 21 |
|
21 | 22 |
|
22 | | -def worker(queue: list[dict], api_key: str = None, silent: bool = False) -> list[Response]: |
| 23 | +def worker(queue: list[dict], api_key: Optional[str] = None, silent: bool = False) -> list[Response]: |
23 | 24 |
|
24 | 25 | _queue: list[tuple[int, dict]] = [(_i, _t) for _i, _t in enumerate(queue)] |
25 | 26 | errors = 0 |
@@ -62,15 +63,37 @@ def worker(queue: list[dict], api_key: str = None, silent: bool = False) -> list |
62 | 63 |
|
63 | 64 | def get_last_commit_sha(repo_url: str, api_key: str, branch: str) -> str: |
64 | 65 | """Get SHA of the last commit""" |
| 66 | + last_commit_sha, _ = get_last_commit_and_tree_sha(repo_url=repo_url, api_key=api_key, branch=branch) |
| 67 | + return last_commit_sha |
| 68 | + |
| 69 | + |
| 70 | +def get_last_commit_and_tree_sha(repo_url: str, api_key: str, branch: str) -> tuple[str, str]: |
| 71 | + """Get SHAs of the last commit and its tree.""" |
65 | 72 | _request = { |
66 | 73 | "method": "get", |
67 | 74 | "url": f"{repo_url}/branches/{branch}", |
68 | 75 | "help_text": "get last commit sha", |
69 | 76 | } |
70 | 77 | _response: Response = worker(queue=[_request], api_key=api_key)[0] |
71 | 78 |
|
72 | | - sha: str = _response.json()["commit"]["sha"] |
73 | | - return sha |
| 79 | + _response_data = _response.json() |
| 80 | + _commit = _response_data.get("commit") |
| 81 | + if not isinstance(_commit, dict): |
| 82 | + raise ValueError(f"Unexpected GitHub response for branch '{branch}': missing 'commit' object.") |
| 83 | + |
| 84 | + commit_sha = _commit.get("sha") |
| 85 | + _nested_commit = _commit.get("commit") |
| 86 | + if not isinstance(_nested_commit, dict): |
| 87 | + raise ValueError(f"Unexpected GitHub response for branch '{branch}': missing nested 'commit' object.") |
| 88 | + |
| 89 | + _tree = _nested_commit.get("tree") |
| 90 | + if not isinstance(_tree, dict): |
| 91 | + raise ValueError(f"Unexpected GitHub response for branch '{branch}': missing 'tree' object.") |
| 92 | + |
| 93 | + tree_sha = _tree.get("sha") |
| 94 | + if not commit_sha or not tree_sha: |
| 95 | + raise ValueError(f"Unexpected GitHub response for branch '{branch}': missing commit SHA or tree SHA.") |
| 96 | + return commit_sha, tree_sha |
74 | 97 |
|
75 | 98 |
|
76 | 99 | def get_blob_shas(file_paths: list[Path], repo_url: str, api_key: str) -> list[str]: |
@@ -129,46 +152,27 @@ def get_old_files_shas(file_paths: list[Path], repo_url: str, repo_path: str, ap |
129 | 152 |
|
130 | 153 | def create_new_tree( |
131 | 154 | file_paths: list[Path], |
132 | | - repo_url: str, |
133 | 155 | repo_path: str, |
134 | | - api_key: str, |
135 | | - last_commit_sha: str, |
136 | 156 | blob_shas: list[str], |
137 | | - old_files_shas: list[str], |
138 | 157 | ) -> list[dict]: |
139 | | - """Create new Git tree with updated files""" |
140 | | - _request = { |
141 | | - "method": "get", |
142 | | - "url": f"{repo_url}/git/trees/{last_commit_sha}?recursive=1", |
143 | | - "help_text": "create new tree", |
144 | | - } |
145 | | - _responses: list[Response] = worker(queue=[_request], api_key=api_key, silent=True) |
146 | | - |
147 | | - _old_tree = _responses[0].json().get("tree", []) if _responses else [] |
148 | | - |
149 | | - new_tree: list[dict] = [ |
150 | | - _item for _item in _old_tree if _item.get("type") == "blob" and not _item.get("sha") in old_files_shas |
| 158 | + """Create incremental Git tree entries with updated files only.""" |
| 159 | + return [ |
| 160 | + { |
| 161 | + "path": f"{repo_path}{'/' if repo_path else ''}{_file.name}", |
| 162 | + "mode": "100644", |
| 163 | + "type": "blob", |
| 164 | + "sha": _sha, |
| 165 | + } |
| 166 | + for _file, _sha in zip(file_paths, blob_shas) |
151 | 167 | ] |
152 | | - new_tree.extend( |
153 | | - [ |
154 | | - { |
155 | | - "path": f"{repo_path}{'/' if repo_path else ''}{_file.name}", |
156 | | - "mode": "100644", |
157 | | - "type": "blob", |
158 | | - "sha": _sha, |
159 | | - } |
160 | | - for _file, _sha in zip(file_paths, blob_shas) |
161 | | - ] |
162 | | - ) |
163 | | - return new_tree |
164 | 168 |
|
165 | 169 |
|
166 | | -def get_tree_sha(repo_url: str, api_key: str, tree: list[dict]) -> str: |
167 | | - """Post a new tree and return its SHA""" |
| 170 | +def get_tree_sha(repo_url: str, api_key: str, tree: list[dict], base_tree_sha: str) -> str: |
| 171 | + """Post a new tree on top of base tree and return its SHA.""" |
168 | 172 | _request = { |
169 | 173 | "method": "post", |
170 | 174 | "url": f"{repo_url}/git/trees", |
171 | | - "body": json.dumps({"tree": tree}), |
| 175 | + "body": json.dumps({"base_tree": base_tree_sha, "tree": tree}), |
172 | 176 | "help_text": "create new tree", |
173 | 177 | } |
174 | 178 | _response: Response = worker(queue=[_request], api_key=api_key)[0] |
@@ -217,20 +221,16 @@ def update_head(repo_url: str, api_key: str, new_commit_sha: str) -> None: |
217 | 221 |
|
218 | 222 |
|
219 | 223 | def upload_files_to_repo( |
220 | | - file_paths: list[Path], repo_url: str, repo_path: str, api_key: str, edition: EditionResult = None |
| 224 | + file_paths: list[Path], repo_url: str, repo_path: str, api_key: str, edition: Optional[EditionResult] = None |
221 | 225 | ) -> None: |
222 | 226 |
|
223 | | - last_commit_sha: str = get_last_commit_sha(repo_url, api_key, "main") |
| 227 | + last_commit_sha, base_tree_sha = get_last_commit_and_tree_sha(repo_url=repo_url, api_key=api_key, branch="main") |
224 | 228 |
|
225 | 229 | blob_shas: list[str] = get_blob_shas(file_paths, repo_url, api_key) |
226 | 230 |
|
227 | | - old_files_shas: list[str] = get_old_files_shas(file_paths, repo_url, repo_path, api_key) |
228 | | - |
229 | | - new_tree: list[dict] = create_new_tree( |
230 | | - file_paths, repo_url, repo_path, api_key, last_commit_sha, blob_shas, old_files_shas |
231 | | - ) |
| 231 | + new_tree: list[dict] = create_new_tree(file_paths=file_paths, repo_path=repo_path, blob_shas=blob_shas) |
232 | 232 |
|
233 | | - tree_sha: str = get_tree_sha(repo_url, api_key, new_tree) |
| 233 | + tree_sha: str = get_tree_sha(repo_url=repo_url, api_key=api_key, tree=new_tree, base_tree_sha=base_tree_sha) |
234 | 234 |
|
235 | 235 | new_commit_sha: str = get_new_commit_sha(edition, file_paths, repo_url, api_key, last_commit_sha, tree_sha) |
236 | 236 |
|
|
0 commit comments