GRM-35: fix: release push permission and notify_failure label IDs

Fix two issues found during release workflow testing.

Closes GRM-35
This commit is contained in:
2026-06-21 17:49:12 +00:00
parent 996a8dc806
commit 63e25d1245
4 changed files with 29 additions and 25 deletions
+10 -8
View File
@@ -51,15 +51,17 @@ def main(repo: str, run_id: str, workflow: str, commit: str) -> None:
)
try:
issue = client.create_issue(title=title, body=body, labels=["bug"])
# Look up label IDs by name (Gitea API expects integer IDs, not strings)
label_ids: list[int] = []
for label in client.list_labels():
if label.get("name") == "bug":
label_ids.append(int(label["id"]))
break
issue = client.create_issue(title=title, body=body, labels=label_ids if label_ids else None)
except APIError as e:
# If labels don't exist, retry without labels
if e.status == 404:
issue = client.create_issue(title=title, body=body)
else:
raise click.ClickException(
_("Failed to create issue: HTTP {status}{message}", status=e.status, message=e.message)
) from None
raise click.ClickException(
_("Failed to create issue: HTTP {status}{message}", status=e.status, message=e.message)
) from None
click.echo(
_(
+6 -2
View File
@@ -105,8 +105,12 @@ class GiteaClient:
return None
return self.create_label(name, color, description)
def create_issue(self, title: str, body: str = "", labels: list[str] | None = None) -> dict[str, Any]:
"""Create a new issue in the repository."""
def create_issue(self, title: str, body: str = "", labels: list[int] | None = None) -> dict[str, Any]:
"""Create a new issue in the repository.
Args:
labels: List of label IDs (integers, not names).
"""
payload: dict[str, Any] = {"title": title, "body": body}
if labels:
payload["labels"] = labels
+2 -2
View File
@@ -275,12 +275,12 @@ class TestGiteaClient:
client = GiteaClient("https://git.example.com", "tok", "owner", "repo")
client._session.request = MagicMock(return_value=_mock_response({"id": 42, "title": "bug"}))
result = client.create_issue(title="bug", body="description", labels=["bug"])
result = client.create_issue(title="bug", body="description", labels=[1])
assert result["id"] == 42
client._session.request.assert_called_once_with(
"POST",
"https://git.example.com/repos/owner/repo/issues",
json={"title": "bug", "body": "description", "labels": ["bug"]},
json={"title": "bug", "body": "description", "labels": [1]},
timeout=DEFAULT_TIMEOUT,
)
+11 -13
View File
@@ -1,6 +1,5 @@
"""Unit tests for scripts/notify_failure.py."""
import http
from unittest.mock import MagicMock, patch
from click.testing import CliRunner
@@ -14,6 +13,7 @@ class TestNotifyFailure:
@patch("scripts.notify_failure.GiteaClient")
def test_creates_issue_with_labels(self, mock_client_cls: MagicMock) -> None:
mock_client = MagicMock()
mock_client.list_labels.return_value = [{"id": 5, "name": "bug"}]
mock_client.create_issue.return_value = {"id": 42}
mock_client_cls.return_value = mock_client
@@ -35,17 +35,15 @@ class TestNotifyFailure:
assert "issue #42" in result.output
mock_client.create_issue.assert_called_once()
call_kwargs = mock_client.create_issue.call_args
assert "release" in call_kwargs.kwargs["title"]
assert call_kwargs.kwargs["labels"] == ["bug"]
assert call_kwargs.kwargs["labels"] == [5]
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("scripts.notify_failure.GiteaClient")
def test_creates_issue_without_labels_on_404(self, mock_client_cls: MagicMock) -> None:
def test_creates_issue_without_bug_label(self, mock_client_cls: MagicMock) -> None:
"""When 'bug' label doesn't exist, create issue without labels."""
mock_client = MagicMock()
mock_client.create_issue.side_effect = [
APIError(http.HTTPStatus.NOT_FOUND, "label not found"),
{"id": 43},
]
mock_client.list_labels.return_value = [{"id": 1, "name": "enhancement"}]
mock_client.create_issue.return_value = {"id": 43}
mock_client_cls.return_value = mock_client
runner = CliRunner()
@@ -64,16 +62,16 @@ class TestNotifyFailure:
)
assert result.exit_code == 0
assert "issue #43" in result.output
assert mock_client.create_issue.call_count == 2
# Second call should not have labels
second_call = mock_client.create_issue.call_args_list[1]
assert "labels" not in second_call.kwargs or second_call.kwargs.get("labels") is None
mock_client.create_issue.assert_called_once()
call_kwargs = mock_client.create_issue.call_args
assert call_kwargs.kwargs.get("labels") is None
@patch.dict("os.environ", {"REPO_TOKEN": "tok"})
@patch("scripts.notify_failure.GiteaClient")
def test_api_error_raises(self, mock_client_cls: MagicMock) -> None:
mock_client = MagicMock()
mock_client.create_issue.side_effect = APIError(http.HTTPStatus.FORBIDDEN, "forbidden")
mock_client.list_labels.return_value = []
mock_client.create_issue.side_effect = APIError(403, "forbidden")
mock_client_cls.return_value = mock_client
runner = CliRunner()