Skip to content

Commit b5ddb27

Browse files
authored
Fix #1100: Fix steps and add a 'jbi lint' command to make sure we don't fall into this anymore (#1104)
* Add 'jbi lint' command * Lock dependencies * Run lint on all envs by default * Check order of steps * Factorize status and resolution maps * Check that status and resolution maps are provided * Do not require settings environment vars to just run linting
1 parent 647f136 commit b5ddb27

18 files changed

+205
-125
lines changed

bin/lint.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ MYPY_CMD="$POETRY_RUN mypy jbi"
1818

1919
YAMLLINT_CMD="$POETRY_RUN yamllint -c .yamllint config/*.yaml"
2020

21+
ACTIONS_LINT_CMD="$POETRY_RUN jbi lint"
22+
2123
all () {
2224
echo "running bandit"
2325
$BANDIT_CMD
@@ -31,6 +33,8 @@ all () {
3133
$MYPY_CMD
3234
echo "running yamllint"
3335
$YAMLLINT_CMD
36+
echo "running actions lint"
37+
$ACTIONS_LINT_CMD
3438
}
3539

3640
usage () {
@@ -43,6 +47,7 @@ usage () {
4347
echo " lint"
4448
echo " mypy"
4549
echo " yamllint"
50+
echo " actions"
4651
}
4752

4853
if [ -z "$1" ]; then
@@ -78,6 +83,9 @@ else
7883
"detect-secrets")
7984
$DETECT_SECRETS_CMD
8085
;;
86+
"actions")
87+
$ACTIONS_LINT_CMD
88+
;;
8189
*)
8290
usage
8391
;;

config/config.nonprod.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
- add_link_to_bugzilla
1515
- add_link_to_jira
1616
- maybe_assign_jira_user
17-
- maybe_update_issue_status
1817
- maybe_update_issue_severity
1918
- sync_whiteboard_labels
2019
- sync_keywords_labels
2120
existing:
2221
- update_issue_summary
2322
- maybe_assign_jira_user
24-
- maybe_update_issue_status
2523
- maybe_update_issue_severity
2624
- sync_whiteboard_labels
2725
- sync_keywords_labels

config/config.prod.yaml

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,21 @@
150150
- add_link_to_jira
151151
- maybe_assign_jira_user
152152
- maybe_update_components
153-
- maybe_update_issue_resolution
154153
- maybe_update_issue_status
154+
- maybe_update_issue_resolution
155155
- sync_whiteboard_labels
156156
- sync_keywords_labels
157157
existing:
158158
- update_issue_summary
159159
- maybe_update_components
160160
- maybe_assign_jira_user
161-
- maybe_update_issue_resolution
162161
- maybe_update_issue_status
162+
- maybe_update_issue_resolution
163163
- sync_whiteboard_labels
164164
- sync_keywords_labels
165165
comment:
166166
- create_comment
167-
status_map:
167+
status_map: &basic-status-map
168168
UNCONFIRMED: Backlog
169169
NEW: Backlog
170170
ASSIGNED: In Progress
@@ -179,7 +179,7 @@
179179
WORKSFORME: Done
180180
INCOMPLETE: Done
181181
MOVED: Done
182-
resolution_map:
182+
resolution_map: &basic-resolution-map
183183
FIXED: Done
184184
INVALID: Invalid
185185
WONTFIX: "Won't Do"
@@ -337,30 +337,8 @@
337337
- sync_keywords_labels
338338
comment:
339339
- create_comment
340-
status_map:
341-
UNCONFIRMED: Backlog
342-
NEW: Backlog
343-
ASSIGNED: In Progress
344-
REOPENED: In Progress
345-
RESOLVED: Done
346-
VERIFIED: Done
347-
FIXED: Done
348-
INVALID: Done
349-
WONTFIX: Done
350-
INACTIVE: Done
351-
DUPLICATE: Done
352-
WORKSFORME: Done
353-
INCOMPLETE: Done
354-
MOVED: Done
355-
resolution_map:
356-
FIXED: Done
357-
INVALID: Invalid
358-
WONTFIX: "Won't Do"
359-
INACTIVE: Inactive
360-
DUPLICATE: Duplicate
361-
WORKSFORME: "Cannot Reproduce"
362-
INCOMPLETE: Incomplete
363-
MOVED: Moved
340+
status_map: *basic-status-map
341+
resolution_map: *basic-resolution-map
364342

365343
- whiteboard_tag: proton
366344
bugzilla_user_id: tbd
@@ -488,16 +466,16 @@
488466
- add_link_to_jira
489467
- maybe_assign_jira_user
490468
- maybe_update_components
491-
- maybe_update_issue_resolution
492469
- maybe_update_issue_status
470+
- maybe_update_issue_resolution
493471
- sync_whiteboard_labels
494472
- sync_keywords_labels
495473
existing:
496474
- update_issue_summary
497475
- maybe_assign_jira_user
498476
- maybe_update_components
499-
- maybe_update_issue_resolution
500477
- maybe_update_issue_status
478+
- maybe_update_issue_resolution
501479
- sync_whiteboard_labels
502480
- sync_keywords_labels
503481
comment:
@@ -517,15 +495,7 @@
517495
WORKSFORME: Done
518496
INCOMPLETE: Done
519497
MOVED: Done
520-
resolution_map:
521-
FIXED: Done
522-
INVALID: Invalid
523-
WONTFIX: "Won't Do"
524-
INACTIVE: Inactive
525-
DUPLICATE: Duplicate
526-
WORKSFORME: "Cannot Reproduce"
527-
INCOMPLETE: Incomplete
528-
MOVED: Moved
498+
resolution_map: *basic-resolution-map
529499

530500
- whiteboard_tag: dataplatform
531501
bugzilla_user_id: tbd
@@ -550,21 +520,7 @@
550520
- maybe_update_issue_status
551521
comment:
552522
- create_comment
553-
status_map:
554-
UNCONFIRMED: Backlog
555-
NEW: Backlog
556-
ASSIGNED: In Progress
557-
REOPENED: In Progress
558-
RESOLVED: Done
559-
VERIFIED: Done
560-
FIXED: Done
561-
INVALID: Done
562-
WONTFIX: Done
563-
INACTIVE: Done
564-
DUPLICATE: Done
565-
WORKSFORME: Done
566-
INCOMPLETE: Done
567-
MOVED: Done
523+
status_map: *basic-status-map
568524

569525
- whiteboard_tag: dataquality
570526
bugzilla_user_id: tbd
@@ -582,12 +538,10 @@
582538
- add_link_to_bugzilla
583539
- add_link_to_jira
584540
- maybe_assign_jira_user
585-
- maybe_update_issue_resolution
586541
- maybe_update_issue_status
587542
existing:
588543
- update_issue_summary
589544
- maybe_assign_jira_user
590-
- maybe_update_issue_resolution
591545
- maybe_update_issue_status
592546
comment:
593547
- create_comment
@@ -703,18 +657,4 @@
703657
enhancement: Story
704658
task: Task
705659
defect: Bug
706-
status_map:
707-
UNCONFIRMED: Backlog
708-
NEW: Backlog
709-
ASSIGNED: In Progress
710-
REOPENED: In Progress
711-
RESOLVED: Done
712-
VERIFIED: Done
713-
FIXED: Done
714-
INVALID: Done
715-
WONTFIX: Done
716-
INACTIVE: Done
717-
DUPLICATE: Done
718-
WORKSFORME: Done
719-
INCOMPLETE: Done
720-
MOVED: Done
660+
status_map: *basic-status-map

jbi/__main__.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import click
2+
3+
from jbi.configuration import get_actions
4+
5+
6+
@click.group()
7+
def cli():
8+
pass
9+
10+
11+
@cli.command()
12+
@click.argument("env", default="all")
13+
def lint(env):
14+
click.echo(f"Linting: {env} action configuration")
15+
16+
if env == "all":
17+
envs = ["local", "nonprod", "prod"]
18+
else:
19+
envs = [env]
20+
21+
for env in envs:
22+
get_actions(env)
23+
click.secho(f"No issues found for {env}.", fg="green")
24+
25+
26+
if __name__ == "__main__":
27+
cli()

jbi/app.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@
2323

2424
import jbi
2525
import jbi.queue
26-
from jbi.configuration import ACTIONS
26+
from jbi.configuration import get_actions
2727
from jbi.environment import get_settings
2828
from jbi.log import CONFIG
2929
from jbi.router import router
3030

3131
SRC_DIR = Path(__file__).parent
3232
APP_DIR = Path(__file__).parents[1]
3333

34+
ACTIONS = get_actions()
3435
settings = get_settings()
3536
version_info: dict[str, str] = get_version(APP_DIR)
3637
VERSION: str = version_info["version"]
@@ -60,8 +61,8 @@ def traces_sampler(sampling_context: dict[str, Any]) -> float:
6061
# https://github.com/tiangolo/fastapi/discussions/9241
6162
@asynccontextmanager
6263
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
63-
jira_service = jbi.jira.get_service()
64-
bugzilla_service = jbi.bugzilla.get_service()
64+
jira_service = jbi.jira.service.get_service()
65+
bugzilla_service = jbi.bugzilla.service.get_service()
6566
queue = jbi.queue.get_dl_queue()
6667

6768
checks.register(bugzilla_service.check_bugzilla_connection, name="bugzilla.up")

jbi/bugzilla/__init__.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +0,0 @@
1-
from .models import (
2-
Bug,
3-
BugId,
4-
WebhookEvent,
5-
WebhookRequest,
6-
)
7-
from .service import BugzillaService, get_service

jbi/configuration.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from jbi import environment
1111
from jbi.models import Actions
1212

13-
settings = environment.get_settings()
1413
logger = logging.getLogger(__name__)
1514

1615

@@ -30,9 +29,9 @@ def get_actions_from_file(jbi_config_file: str) -> Actions:
3029
raise ConfigError("Errors exist.") from exception
3130

3231

33-
def get_actions(env=settings.env):
32+
def get_actions(env=None) -> Actions:
3433
"""Load actions from file determined by ENV name"""
34+
if env is None:
35+
settings = environment.get_settings()
36+
env = settings.env
3537
return get_actions_from_file(f"config/config.{env}.yaml")
36-
37-
38-
ACTIONS = get_actions()

jbi/jira/service.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from dockerflow import checks
1515
from requests import exceptions as requests_exceptions
1616

17-
from jbi import Operation, bugzilla, environment
17+
from jbi import Operation, environment
18+
from jbi.bugzilla import models as bugzilla_models
1819
from jbi.jira.utils import markdown_to_jira
1920
from jbi.models import ActionContext
2021

@@ -184,7 +185,7 @@ def add_jira_comments_for_changes(self, context: ActionContext):
184185
return jira_response_comments
185186

186187
def delete_jira_issue_if_duplicate(
187-
self, context: ActionContext, latest_bug: bugzilla.Bug
188+
self, context: ActionContext, latest_bug: bugzilla_models.Bug
188189
):
189190
"""Rollback the Jira issue creation if there is already a linked Jira issue
190191
on the Bugzilla ticket"""

jbi/models.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919

2020
from jbi import Operation, steps
21-
from jbi.bugzilla import Bug, BugId, WebhookEvent
21+
from jbi.bugzilla.models import Bug, BugId, WebhookEvent
2222

2323
logger = logging.getLogger(__name__)
2424

@@ -51,14 +51,25 @@ class ActionSteps(BaseModel, frozen=True):
5151
@classmethod
5252
def validate_steps(cls, function_names: list[str]):
5353
"""Validate that all configure step functions exist in the steps module"""
54-
5554
invalid_functions = [
5655
func_name for func_name in function_names if not hasattr(steps, func_name)
5756
]
5857
if invalid_functions:
5958
raise ValueError(
6059
f"The following functions are not available in the `steps` module: {', '.join(invalid_functions)}"
6160
)
61+
62+
# Make sure `maybe_update_resolution` comes after `maybe_update_status`.
63+
try:
64+
idx_resolution = function_names.index("maybe_update_issue_resolution")
65+
idx_status = function_names.index("maybe_update_issue_status")
66+
assert idx_resolution > idx_status, (
67+
"Step `maybe_update_resolution` should be put after `maybe_update_issue_status`"
68+
)
69+
except ValueError:
70+
# One of these 2 steps not listed.
71+
pass
72+
6273
return function_names
6374

6475

@@ -191,6 +202,16 @@ def validate_actions(cls, actions: list[Action]):
191202
f"Provide bugzilla_user_id data for `{action.whiteboard_tag}` action."
192203
)
193204

205+
assert action.parameters.status_map or (
206+
"maybe_update_issue_status" not in action.parameters.steps.new
207+
and "maybe_update_issue_status" not in action.parameters.steps.existing
208+
), "`maybe_update_issue_status` was used without `status_map`"
209+
assert action.parameters.resolution_map or (
210+
"maybe_update_issue_resolution" not in action.parameters.steps.new
211+
and "maybe_update_issue_resolution"
212+
not in action.parameters.steps.existing
213+
), "`maybe_update_issue_resolution` was used without `resolution_map`"
214+
194215
return actions
195216

196217
model_config = ConfigDict(ignored_types=(functools.cached_property,))

0 commit comments

Comments
 (0)