Skip to content

Run multiple actions when multiple whiteboard tags #1109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ graph TD

# Development

We use [pandoc](https://pandoc.org) to convert markdown to the Jira syntax. Make sure the binary is found in path or [specify your custom location](https://github.com/JessicaTegner/pypandoc#specifying-the-location-of-pandoc-binaries).
We use [pandoc](https://pandoc.org) to convert markdown to the Jira syntax. Make sure the binary is found in path or [specify your custom location](https://github.com/JessicaTegner/pypandoc#specifying-the-location-of-pandoc-binaries) and its version is sufficiently recent to support Jira syntax (e.g. 3.6.3).

- `make start`: run the application locally (http://localhost:8000)
- `make test`: run the unit tests suites
Expand Down
2 changes: 1 addition & 1 deletion jbi/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class RunnerContext(Context, extra="forbid"):

operation: Operation
event: WebhookEvent
action: Optional[Action] = None
actions: Optional[list[Action]] = None
bug: BugId | Bug


Expand Down
13 changes: 8 additions & 5 deletions jbi/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,23 @@ def groups2operation(steps: ActionSteps):
return by_operation


def lookup_action(bug: bugzilla_models.Bug, actions: Actions) -> Action:
def lookup_actions(bug: bugzilla_models.Bug, actions: Actions) -> list[Action]:
"""
Find first matching action from bug's whiteboard field.
Find matching actions from bug's whiteboard field.

Tags are strings between brackets and can have prefixes/suffixes
using dashes (eg. ``[project]``, ``[project-moco]``, ``[project-moco-sprint1]``).
"""

if bug.whiteboard:
relevant_actions = []
for tag, action in actions.by_tag.items():
# [tag-word], [tag-], [tag], but not [word-tag] or [tagword]
search_string = r"\[" + tag + r"(-[^\]]*)*\]"
if re.search(search_string, bug.whiteboard, flags=re.IGNORECASE):
return action
relevant_actions.append(action)
if len(relevant_actions):
return relevant_actions

raise ActionNotFoundError(", ".join(actions.by_tag.keys()))

Expand Down Expand Up @@ -221,7 +224,7 @@ def execute_action(
raise IgnoreInvalidRequestError("private bugs are not supported")

try:
action = lookup_action(bug, actions)
action = lookup_actions(bug, actions)[0]
except ActionNotFoundError as err:
raise IgnoreInvalidRequestError(
f"no bug whiteboard matching action tags: {err}"
Expand All @@ -240,7 +243,7 @@ def execute_action(

runner_context = runner_context.update(bug=bug)

runner_context = runner_context.update(action=action)
runner_context = runner_context.update(actions=[action])

linked_issue_key: Optional[str] = bug.extract_from_see_also(
project_key=action.jira_project_key
Expand Down
38 changes: 34 additions & 4 deletions tests/unit/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest
import requests
import responses
import tests.fixtures.factories as factories

from jbi import Operation
from jbi.bugzilla.client import BugNotAccessibleError
Expand All @@ -15,7 +16,7 @@
Executor,
execute_action,
execute_or_queue,
lookup_action,
lookup_actions,
)


Expand Down Expand Up @@ -187,7 +188,7 @@ def test_action_is_logged_as_success_if_returns_true(
("Action 'devtest' executed successfully for Bug 654321", Operation.SUCCESS),
]
assert capturelogs.records[-1].bug["id"] == 654321
assert capturelogs.records[-1].action["whiteboard_tag"] == "devtest"
assert capturelogs.records[-1].actions[0]["whiteboard_tag"] == "devtest"


def test_action_is_logged_as_ignore_if_returns_false(
Expand Down Expand Up @@ -619,11 +620,40 @@ def test_counter_is_incremented_for_attachment(
)
def test_lookup_action_found(whiteboard, actions, bug_factory):
bug = bug_factory(id=1234, whiteboard=whiteboard)
action = lookup_action(bug, actions)
action = lookup_actions(bug, actions)[0]
assert action.whiteboard_tag == "devtest"
assert "test config" in action.description


@pytest.mark.parametrize(
"whiteboard,expected_tags",
[
("[example][DevTest]", ["devtest"]),
("[DevTest][example]", ["devtest"]),
("[example][DevTest][other]", ["devtest", "other"]),
],
)
def test_multiple_lookup_actions_found(whiteboard, expected_tags, bug_factory):
actions = factories.ActionsFactory(root=[
factories.ActionFactory(
whiteboard_tag="devtest",
bugzilla_user_id="tbd",
description="test config",
),
factories.ActionFactory(
whiteboard_tag="other",
bugzilla_user_id="tbd",
description="test config",
),
])
bug = bug_factory(id=1234, whiteboard=whiteboard)
acts = lookup_actions(bug, actions)
assert len(acts) == len(expected_tags)
looked_up_tags = [a.whiteboard_tag for a in acts]
assert sorted(looked_up_tags) == sorted(expected_tags)
assert all(["test config" == a.description for a in acts])


@pytest.mark.parametrize(
"whiteboard",
[
Expand All @@ -649,5 +679,5 @@ def test_lookup_action_found(whiteboard, actions, bug_factory):
def test_lookup_action_not_found(whiteboard, actions, bug_factory):
bug = bug_factory(id=1234, whiteboard=whiteboard)
with pytest.raises(ActionNotFoundError) as exc_info:
lookup_action(bug, actions)
lookup_actions(bug, actions)[0]
assert str(exc_info.value) == "devtest"
Loading