diff --git a/src/devx/ci/publish.py b/src/devx/ci/publish.py index 4c1dd50..10a10c2 100644 --- a/src/devx/ci/publish.py +++ b/src/devx/ci/publish.py @@ -202,18 +202,27 @@ def main(tag: str, repo: str, registry_url: str | None, skip_build: bool) -> Non if not skip_build: build_package() - if pypi_token: - # Standard PyPI flow takes precedence when PYPI_TOKEN is set - publish_to_pypi(pypi_token) - elif registry_url: - # Gitea PyPI registry flow - publish_to_gitea_registry(registry_url, gitea_token) - else: + try: + if pypi_token: + # Standard PyPI flow takes precedence when PYPI_TOKEN is set + publish_to_pypi(pypi_token) + elif registry_url: + # Gitea PyPI registry flow + publish_to_gitea_registry(registry_url, gitea_token) + else: + click.echo( + _( + "PYPI_TOKEN not set and no registry URL configured — " + "skipping PyPI publish. No worries, we'll just create the Gitea release." + ) + ) + except click.ClickException as e: click.echo( _( - "PYPI_TOKEN not set and no registry URL configured — " - "skipping PyPI publish. No worries, we'll just create the Gitea release." - ) + "PyPI publish failed (non-fatal — continuing to Gitea release):\n{error}", + error=str(e), + ), + err=True, ) else: click.echo(_("--skip-build: skipping package build and PyPI publish.")) diff --git a/src/devx/translations.json b/src/devx/translations.json index 1d8fa86..8cf7843 100644 --- a/src/devx/translations.json +++ b/src/devx/translations.json @@ -911,6 +911,14 @@ "ru": "Ой! Публикация в PyPI не удалась:\n{stderr}", "zh": "哎呀!PyPI 发布失败:\n{stderr}" }, + "PyPI publish failed (non-fatal — continuing to Gitea release):\n{error}": { + "bg": "Публикуването в PyPI неуспешно (некритично — продължава към Gitea release):\n{error}", + "de": "PyPI-Veröffentlichung fehlgeschlagen (nicht fatal — Gitea-Release wird fortgesetzt):\n{error}", + "en": "PyPI publish failed (non-fatal — continuing to Gitea release):\n{error}", + "pl": "Publikacja PyPI nie powiodła się (niekrytyczne — kontynuacja Gitea release):\n{error}", + "ru": "Публикация в PyPI не удалась (некритично — продолжаем создание Gitea release):\n{error}", + "zh": "PyPI 发布失败(非致命 — 继续创建 Gitea release):\n{error}" + }, "PASSED: {pair}": { "bg": "PASSED: {pair}", "de": "PASSED: {pair}", diff --git a/tests/unit/test_publish.py b/tests/unit/test_publish.py index 409d415..d82b557 100644 --- a/tests/unit/test_publish.py +++ b/tests/unit/test_publish.py @@ -301,14 +301,21 @@ class TestMain: @patch("devx.ci.publish.TeaCLI") @patch("devx.ci.publish.publish_to_pypi") @patch("devx.ci.publish.build_package") - def test_publish_failure_raises_click( + def test_publish_failure_continues_to_gitea_release( self, mock_build: MagicMock, mock_publish: MagicMock, mock_tea_cls: MagicMock, mock_notes: MagicMock ) -> None: + """PyPI publish failure is non-fatal — Gitea release is still created.""" + mock_tea = MagicMock() + mock_tea.list_releases.return_value = [] + mock_tea_cls.return_value = mock_tea mock_publish.side_effect = click.ClickException("publish failed") runner = CliRunner() result = runner.invoke(main, ["v1.0.0", "owner/repo"]) - assert result.exit_code == 1 - assert "publish" in result.output + assert result.exit_code == 0 + assert "non-fatal" in result.output + mock_tea.create_release.assert_called_once_with( + "owner/repo", tag="v1.0.0", title="v1.0.0", body="Release notes" + ) @patch.dict("os.environ", {"REPO_TOKEN": "gitea-tok", "PYPI_TOKEN": "pypi-tok"}) @patch("devx.ci.publish.generate_release_notes", return_value="Release notes")