From b8cbaa6b82bd340941df329422bee17003530dfc Mon Sep 17 00:00:00 2001 From: Vladimir Bryzgalov Date: Sat, 15 Aug 2026 21:54:48 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BD=D1=8F=D1=82=D0=BD=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D0=BA=D0=B0?= =?UTF-8?q?=D0=B7=D0=B5=20=D0=B2=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF?= =?UTF-8?q?=D0=B5=20=D0=BA=20=D1=80=D0=B5=D0=BB=D0=B8=D0=B7=D0=B0=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea на приватном репозитории отвечает 404 вместо 403, и сообщение "404 Not Found" не подсказывало, что дело в токене. Теперь при 401/403/404 объясняется причина и куда вписать токен. Co-Authored-By: Claude Opus 5 (1M context) --- app/updater.py | 14 ++++++++++++++ app/version.py | 2 +- tests/test_updater.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/app/updater.py b/app/updater.py index f06dc12..953ded5 100644 --- a/app/updater.py +++ b/app/updater.py @@ -133,6 +133,20 @@ def install_update(archive_bytes: bytes, base_dir: Path, def _api(session, url: str, token: str): headers = {"Authorization": f"token {token}"} if token else {} response = session.get(url, headers=headers, timeout=30) + + # На приватном репозитории Gitea отвечает 404, а не 403: она скрывает сам + # факт его существования. Поэтому 404 почти всегда означает не «нет релиза», + # а «этот токен сюда не пускают». + if response.status_code in (401, 403, 404): + if not token: + raise UpdateError( + "репозиторий не отдаётся без токена. Впишите его в config.toml, " + "раздел [update], поле token (Gitea: Settings - Applications - " + "Generate Token, право read:repository)") + raise UpdateError( + f"Gitea ответила {response.status_code}. Обычно это значит, что токен " + "в config.toml неверный или у него нет права read:repository") + response.raise_for_status() return response diff --git a/app/version.py b/app/version.py index b3f4756..ae73625 100644 --- a/app/version.py +++ b/app/version.py @@ -1 +1 @@ -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/tests/test_updater.py b/tests/test_updater.py index af838bd..f304794 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -158,3 +158,34 @@ class TestPipelineImportSafety: replacements_path=tmp_path / "r.txt", base_dir=tmp_path) with pytest.raises(ModelsMissing, match="ffmpeg"): _ = p.ffmpeg + + +class TestApiDiagnostics: + """404 на приватном репозитории Gitea означает проблему с токеном, не отсутствие релиза.""" + + class FakeResponse: + def __init__(self, status): + self.status_code = status + + def raise_for_status(self): + raise AssertionError("до сюда доходить не должно") + + class FakeSession: + def __init__(self, status): + self.status = status + + def get(self, url, headers=None, timeout=None): + return TestApiDiagnostics.FakeResponse(self.status) + + @pytest.mark.parametrize("status", [401, 403, 404]) + def test_auth_errors_mention_token(self, status): + from app.updater import _api + + with pytest.raises(UpdateError, match="токен"): + _api(self.FakeSession(status), "http://x", token="какой-то") + + def test_missing_token_says_where_to_put_it(self): + from app.updater import _api + + with pytest.raises(UpdateError, match="config.toml"): + _api(self.FakeSession(404), "http://x", token="")