From b93daa581a5a8523626d5c716c70232cd4b35f47 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 27 Jun 2026 16:31:42 +0800 Subject: [PATCH] fix(api): cap weekly gitea stats readback --- .../api/src/services/weekly_report_service.py | 17 ++++++++- .../tests/test_report_generation_service.py | 37 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/weekly_report_service.py b/apps/api/src/services/weekly_report_service.py index 1ac1a3c1..c26bddf2 100644 --- a/apps/api/src/services/weekly_report_service.py +++ b/apps/api/src/services/weekly_report_service.py @@ -162,12 +162,14 @@ class WeeklyReportService: deploys = 0 page = 1 limit = 50 + max_pages = 4 try: for api_url in self._gitea_api_candidates(): commits = deploys = 0 page = 1 + total_commits: int | None = None try: - while page <= 20: + while page <= max_pages: query = urlencode({ "sha": "main", "since": since_utc, @@ -177,6 +179,9 @@ class WeeklyReportService: url = f"{api_url}/api/v1/repos/{owner}/{repo}/commits?{query}" request = Request(url, headers=headers) with urlopen(request, timeout=3) as response: + total_header = response.headers.get("X-Total-Count") or response.headers.get("X-Total") + if total_header and total_header.isdigit(): + total_commits = int(total_header) payload = json.loads(response.read().decode("utf-8")) if not isinstance(payload, list): logger.warning("gitea_git_stats_unexpected_payload", payload_type=type(payload).__name__) @@ -189,8 +194,16 @@ class WeeklyReportService: if "deploy" in subject or subject.startswith("chore(cd):"): deploys += 1 if len(payload) < limit: - return commits, deploys, True + return total_commits or commits, deploys, True page += 1 + if total_commits is not None and total_commits > 0: + logger.info( + "gitea_git_stats_sampled", + total_commits=total_commits, + sampled_pages=max_pages, + sampled_deploys=deploys, + ) + return total_commits, deploys, True except Exception as candidate_exc: logger.warning( "gitea_git_stats_candidate_failed", diff --git a/apps/api/tests/test_report_generation_service.py b/apps/api/tests/test_report_generation_service.py index 373c0d82..52f9c45e 100644 --- a/apps/api/tests/test_report_generation_service.py +++ b/apps/api/tests/test_report_generation_service.py @@ -138,6 +138,8 @@ class TestWeeklyReportGitStats: ] class Response: + headers = {} + def __enter__(self): return self @@ -165,6 +167,41 @@ class TestWeeklyReportGitStats: assert deploys == 2 assert source_ok is True + def test_gitea_commit_stats_uses_total_header_with_limited_scan(self, monkeypatch): + payload = [{"commit": {"message": "fix(api): one\n"}} for _ in range(50)] + calls = [] + + class Response: + headers = {"X-Total-Count": "4804"} + + def __enter__(self): + return self + + def __exit__(self, *_args): + return None + + def read(self): + return weekly_report_module.json.dumps(payload).encode("utf-8") + + def fake_urlopen(request, timeout): + calls.append(request.full_url) + return Response() + + monkeypatch.setattr(weekly_report_module, "urlopen", fake_urlopen) + monkeypatch.setattr(weekly_report_module.settings, "GITEA_API_URL", "https://gitea.example.test") + monkeypatch.setattr(weekly_report_module.settings, "GITEA_REPO_OWNER", "wooo") + monkeypatch.setattr(weekly_report_module.settings, "GITEA_REPO_NAME", "awoooi") + monkeypatch.setattr(weekly_report_module.settings, "GITEA_API_TOKEN", "") + + svc = WeeklyReportService(stats_service=object(), k3s_monitor=object()) + monkeypatch.setattr(svc, "_gitea_api_candidates", lambda: ["https://gitea.example.test"]) + commits, deploys, source_ok = svc._get_gitea_commit_stats(datetime.now(_TZ_TAIPEI)) + + assert commits == 4804 + assert deploys == 0 + assert source_ok is True + assert len(calls) == 4 + # ============================================================================= # DailyKpi 計算屬性