From f64912017214d3c861a2bc63fe001112bdf1ee11 Mon Sep 17 00:00:00 2001 From: emil Date: Mon, 22 Jun 2026 10:26:28 +0000 Subject: [PATCH] GRM-59: fix: post-merge workflow failures (4 jobs) --- .gitea/workflows/post-merge.yml | 5 +++-- scripts/ci/post_merge.py | 24 ++++++++++++++---------- scripts/configure_repo.py | 5 +++-- src/gitea_runner_manager/config.py | 4 +++- tests/unit/test_config.py | 4 +++- tests/unit/test_configure_repo.py | 9 +++++++++ tests/unit/test_post_merge.py | 16 +++++++--------- 7 files changed, 42 insertions(+), 25 deletions(-) diff --git a/.gitea/workflows/post-merge.yml b/.gitea/workflows/post-merge.yml index 7cace75..d8b376c 100644 --- a/.gitea/workflows/post-merge.yml +++ b/.gitea/workflows/post-merge.yml @@ -54,8 +54,9 @@ jobs: env: PYTHONPATH: .:src run: | - git log -1 --format=%B > /tmp/commit-msg.txt - python3 scripts/ci/validate_commit_msg.py /tmp/commit-msg.txt --branch master + git log -1 --format=%B > commit-msg.txt + python3 scripts/ci/validate_commit_msg.py commit-msg.txt --branch master + rm -f commit-msg.txt release: needs: [detect-type] diff --git a/scripts/ci/post_merge.py b/scripts/ci/post_merge.py index 8c0d960..76f3302 100644 --- a/scripts/ci/post_merge.py +++ b/scripts/ci/post_merge.py @@ -64,11 +64,11 @@ def extract_conventional_msg(commit_msg: str) -> str: return re.sub(r"^GRM-\d+[:\s]\s*", "", first_line) -def resolve_task_id(client: VikunjaClient, task_id: str) -> int: +def resolve_task_id(client: VikunjaClient, task_id: str) -> int | None: """Resolve GRM-N identifier to Vikunja numeric task ID. Paginates through the project's tasks to handle projects with more - than 50 tasks. + than 50 tasks. Returns None if the task is not found. """ page = 1 while True: @@ -81,13 +81,7 @@ def resolve_task_id(client: VikunjaClient, task_id: str) -> int: if len(tasks) < DEFAULT_PER_PAGE: break page += 1 - raise click.ClickException( - _( - "Could not find Vikunja task for {task_id} in project {project_id}.", - task_id=task_id, - project_id=VIKUNJA_PROJECT_ID, - ) - ) + return None def build_comment(task_id: str, conv_msg: str, commit_sha: str) -> str: @@ -162,7 +156,17 @@ def main(commit_msg: str | None, commit_sha: str, from_git: bool, git_sha: str) client = VikunjaClient(VIKUNJA_API_URL, token) vikunja_task_id = 0 try: - vikunja_task_id = resolve_task_id(client, task_id) + vikunja_task_id = resolve_task_id(client, task_id) or 0 + if not vikunja_task_id: + click.echo( + _( + "Warning: Could not find Vikunja task {task_id} in project {project_id}. " + "The merge succeeded — please update the Vikunja task manually.", + task_id=task_id, + project_id=VIKUNJA_PROJECT_ID, + ) + ) + return conv_msg = extract_conventional_msg(commit_msg) sha = commit_sha or "unknown" html = build_comment(task_id, conv_msg, sha) diff --git a/scripts/configure_repo.py b/scripts/configure_repo.py index 43aa907..98e847b 100644 --- a/scripts/configure_repo.py +++ b/scripts/configure_repo.py @@ -50,6 +50,7 @@ def _ensure_label_via_tea(tea: TeaCLI, repo: str, name: str, color: str, descrip """Create a label via tea if it doesn't already exist. Returns True if created, False if it already existed. + Falls back to GiteaClient if tea is not installed or fails. """ try: existing = tea.list_labels(repo) @@ -57,8 +58,8 @@ def _ensure_label_via_tea(tea: TeaCLI, repo: str, name: str, color: str, descrip return False tea.create_label(repo, name=name, color=color, description=description) return True - except TeaCLIError: - # Fall back to GiteaClient if tea fails + except (TeaCLIError, FileNotFoundError): + # Fall back to GiteaClient if tea is not installed or fails return _ensure_label_via_client(name, color, description) diff --git a/src/gitea_runner_manager/config.py b/src/gitea_runner_manager/config.py index f8d5b56..15fb193 100644 --- a/src/gitea_runner_manager/config.py +++ b/src/gitea_runner_manager/config.py @@ -21,7 +21,9 @@ DEFAULT_PER_PAGE = 50 BRANCH_PROTECTION_CONFIG: dict[str, object] = { "branch_name": "master", - "enable_push": False, + "enable_push": True, + "enable_push_whitelist": True, + "push_whitelist_usernames": ["emil"], "enable_status_check": True, "status_check_contexts": [ "CI / quality (pull_request)", diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index a1467c0..2271554 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -46,7 +46,9 @@ class TestConfigConstants: def test_branch_protection_config(self) -> None: assert BRANCH_PROTECTION_CONFIG["branch_name"] == "master" - assert BRANCH_PROTECTION_CONFIG["enable_push"] is False + assert BRANCH_PROTECTION_CONFIG["enable_push"] is True + assert BRANCH_PROTECTION_CONFIG["enable_push_whitelist"] is True + assert "emil" in BRANCH_PROTECTION_CONFIG["push_whitelist_usernames"] assert BRANCH_PROTECTION_CONFIG["required_approvals"] == 0 contexts = BRANCH_PROTECTION_CONFIG["status_check_contexts"] assert isinstance(contexts, list) diff --git a/tests/unit/test_configure_repo.py b/tests/unit/test_configure_repo.py index 214e529..0919631 100644 --- a/tests/unit/test_configure_repo.py +++ b/tests/unit/test_configure_repo.py @@ -62,6 +62,15 @@ class TestEnsureLabelViaTea: assert result is True mock_fallback.assert_called_once_with("ready-to-merge", "2ecc71", "desc") + def test_tea_not_installed_falls_back_to_client(self) -> None: + """When tea CLI is not installed (FileNotFoundError), fall back to GiteaClient.""" + mock_tea = MagicMock() + mock_tea.list_labels.side_effect = FileNotFoundError("[Errno 2] No such file or directory: 'tea'") + with patch("scripts.configure_repo._ensure_label_via_client", return_value=True) as mock_fallback: + result = _ensure_label_via_tea(mock_tea, "owner/repo", "ready-to-merge", "2ecc71", "desc") + assert result is True + mock_fallback.assert_called_once_with("ready-to-merge", "2ecc71", "desc") + class TestEnsureLabelViaClient: @patch.dict("os.environ", {"REPO_TOKEN": "tok"}, clear=True) diff --git a/tests/unit/test_post_merge.py b/tests/unit/test_post_merge.py index 28d4fa0..c664aed 100644 --- a/tests/unit/test_post_merge.py +++ b/tests/unit/test_post_merge.py @@ -58,12 +58,10 @@ class TestResolveTaskId: assert resolve_task_id(mock_client, "GRM-19") == 42 mock_client.list_project_tasks.assert_called_once() - def test_not_found_raises(self) -> None: + def test_not_found_returns_none(self) -> None: mock_client = MagicMock() mock_client.list_project_tasks.return_value = [] - with pytest.raises(click.ClickException) as exc: - resolve_task_id(mock_client, "GRM-99") - assert "Could not find" in str(exc.value) + assert resolve_task_id(mock_client, "GRM-99") is None def test_found_on_second_page(self) -> None: """Task is on page 2 when project has more than 50 tasks.""" @@ -79,9 +77,7 @@ class TestResolveTaskId: mock_client = MagicMock() page1 = [{"id": i, "identifier": f"GRM-{i}"} for i in range(10)] mock_client.list_project_tasks.return_value = page1 - with pytest.raises(click.ClickException) as exc: - resolve_task_id(mock_client, "GRM-99") - assert "Could not find" in str(exc.value) + assert resolve_task_id(mock_client, "GRM-99") is None assert mock_client.list_project_tasks.call_count == 1 def test_http_error_propagates(self) -> None: @@ -168,14 +164,16 @@ class TestMain: @patch.dict("os.environ", {"VIKUNJA_TOKEN": "tok"}) @patch("scripts.ci.post_merge.VikunjaClient") - def test_resolve_failure_propagates(self, mock_client_cls: MagicMock) -> None: + def test_resolve_failure_warns(self, mock_client_cls: MagicMock) -> None: + """Missing Vikunja task should warn, not fail — the merge already succeeded.""" mock_client = MagicMock() mock_client.list_project_tasks.return_value = [] mock_client_cls.return_value = mock_client runner = CliRunner() result = runner.invoke(main, ["GRM-20: fix: bug"]) - assert result.exit_code == 1 + assert result.exit_code == 0 assert "Could not find" in result.output + assert "Warning" in result.output @patch.dict("os.environ", {"VIKUNJA_TOKEN": "tok"}) @patch("scripts.ci.post_merge.VikunjaClient")