t-bug-catcher 0.2.6__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/PKG-INFO +1 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/setup.cfg +1 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/setup.py +1 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/__init__.py +1 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/config.py +9 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/jira.py +102 -19
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/workitems.py +2 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/PKG-INFO +1 -1
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/MANIFEST.in +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/README.rst +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/pyproject.toml +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/requirements.txt +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/bug_catcher.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/bug_snag.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/exceptions.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/utils/__init__.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/utils/common.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher/utils/logger.py +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/SOURCES.txt +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/not-zip-safe +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/requires.txt +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/t_bug_catcher.egg-info/top_level.txt +0 -0
- {t_bug_catcher-0.2.6 → t_bug_catcher-0.3.1}/tests/test_t_bug_catcher.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
|
|
3
|
-
from .workitems import variables
|
|
3
|
+
from .workitems import metadata, variables
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class Config:
|
|
@@ -12,6 +12,9 @@ class Config:
|
|
|
12
12
|
MAX_ATTACHMENTS: int = 5
|
|
13
13
|
MAX_ISSUE_ATTACHMENTS: int = 100
|
|
14
14
|
MAX_DESCRIPTION_LENGTH: int = 250
|
|
15
|
+
SUMMARY_LENGTH: int = 120
|
|
16
|
+
|
|
17
|
+
SUPPORT_BOARD = "AB"
|
|
15
18
|
|
|
16
19
|
RC_RUN_LINK = (
|
|
17
20
|
f"https://cloud.robocorp.com/organizations/{os.environ.get('RC_ORGANIZATION_ID')}"
|
|
@@ -25,5 +28,10 @@ class Config:
|
|
|
25
28
|
else variables.get("environment", "local")
|
|
26
29
|
)
|
|
27
30
|
|
|
31
|
+
STAGE = metadata.get("process", dict()).get("implementationStage", "")
|
|
32
|
+
ADMIN_CODE = metadata.get("process", dict()).get("adminCode", "")
|
|
33
|
+
WORKER_NAME = metadata.get("process", dict()).get("name", "")
|
|
34
|
+
EMPOWER_URL = metadata.get("process", dict()).get("processRunUrl") or variables.get("processRunUrl")
|
|
35
|
+
|
|
28
36
|
|
|
29
37
|
CONFIG = Config()
|
|
@@ -7,6 +7,7 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import sys
|
|
9
9
|
import traceback
|
|
10
|
+
from importlib.metadata import version
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from types import TracebackType
|
|
12
13
|
from typing import List, Optional, Union
|
|
@@ -89,7 +90,12 @@ class Jira:
|
|
|
89
90
|
)
|
|
90
91
|
|
|
91
92
|
def config(
|
|
92
|
-
self,
|
|
93
|
+
self,
|
|
94
|
+
login: str,
|
|
95
|
+
api_token: str,
|
|
96
|
+
project_key: str,
|
|
97
|
+
webhook_url: Optional[str] = None,
|
|
98
|
+
default_assignee: Optional[str] = None,
|
|
93
99
|
) -> bool:
|
|
94
100
|
"""Sets the webhook URL for the Jira project.
|
|
95
101
|
|
|
@@ -104,7 +110,7 @@ class Jira:
|
|
|
104
110
|
bool: True if the configuration was successful, False otherwise.
|
|
105
111
|
"""
|
|
106
112
|
try:
|
|
107
|
-
self._project_key = project_key
|
|
113
|
+
self._project_key = project_key if CONFIG.STAGE not in ["hypercare", "support"] else CONFIG.SUPPORT_BOARD
|
|
108
114
|
self._default_assignee = default_assignee
|
|
109
115
|
if not webhook_url:
|
|
110
116
|
logger.warning("No JIRA webhook URL provided. All issues will be posted to backlog.")
|
|
@@ -227,6 +233,8 @@ class Jira:
|
|
|
227
233
|
}
|
|
228
234
|
if labels:
|
|
229
235
|
fields["fields"]["labels"] = labels
|
|
236
|
+
if self._project_key == CONFIG.SUPPORT_BOARD and CONFIG.ADMIN_CODE:
|
|
237
|
+
fields["fields"]["customfield_10077"] = [CONFIG.ADMIN_CODE]
|
|
230
238
|
payload = json.dumps(fields)
|
|
231
239
|
return payload
|
|
232
240
|
|
|
@@ -435,6 +443,34 @@ class Jira:
|
|
|
435
443
|
}
|
|
436
444
|
]
|
|
437
445
|
|
|
446
|
+
@staticmethod
|
|
447
|
+
def __bot_name_markup() -> List[dict]:
|
|
448
|
+
"""Create the ai worker markup.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
dict: The ai worker markup.
|
|
452
|
+
"""
|
|
453
|
+
return (
|
|
454
|
+
[
|
|
455
|
+
{
|
|
456
|
+
"type": "paragraph",
|
|
457
|
+
"content": [
|
|
458
|
+
{
|
|
459
|
+
"type": "text",
|
|
460
|
+
"text": "Process name: ",
|
|
461
|
+
"marks": [{"type": "strong"}],
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
"type": "text",
|
|
465
|
+
"text": f"{CONFIG.ADMIN_CODE} - {CONFIG.WORKER_NAME}",
|
|
466
|
+
},
|
|
467
|
+
],
|
|
468
|
+
}
|
|
469
|
+
]
|
|
470
|
+
if CONFIG.ADMIN_CODE and CONFIG.WORKER_NAME
|
|
471
|
+
else []
|
|
472
|
+
)
|
|
473
|
+
|
|
438
474
|
@staticmethod
|
|
439
475
|
def __traceback_markup(exc_traceback_info: str) -> List[dict]:
|
|
440
476
|
"""Create the traceback markup.
|
|
@@ -515,6 +551,14 @@ class Jira:
|
|
|
515
551
|
{"type": "subsup", "attrs": {"type": "sub"}},
|
|
516
552
|
],
|
|
517
553
|
},
|
|
554
|
+
{
|
|
555
|
+
"type": "text",
|
|
556
|
+
"text": f" (v{version('t_bug_catcher')})",
|
|
557
|
+
"marks": [
|
|
558
|
+
{"type": "em"},
|
|
559
|
+
{"type": "subsup", "attrs": {"type": "sub"}},
|
|
560
|
+
],
|
|
561
|
+
},
|
|
518
562
|
],
|
|
519
563
|
},
|
|
520
564
|
]
|
|
@@ -525,15 +569,34 @@ class Jira:
|
|
|
525
569
|
variables.get("environment"): [
|
|
526
570
|
{
|
|
527
571
|
"type": "text",
|
|
528
|
-
"text":
|
|
572
|
+
"text": CONFIG.EMPOWER_URL,
|
|
529
573
|
"marks": [
|
|
530
574
|
{
|
|
531
575
|
"type": "link",
|
|
532
|
-
"attrs": {"href":
|
|
576
|
+
"attrs": {"href": CONFIG.EMPOWER_URL},
|
|
533
577
|
},
|
|
534
578
|
{"type": "underline"},
|
|
535
579
|
],
|
|
536
|
-
}
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
"type": "text",
|
|
583
|
+
"text": " [Robocloud ",
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
"type": "text",
|
|
587
|
+
"text": "link",
|
|
588
|
+
"marks": [
|
|
589
|
+
{
|
|
590
|
+
"type": "link",
|
|
591
|
+
"attrs": {"href": CONFIG.RC_RUN_LINK},
|
|
592
|
+
},
|
|
593
|
+
{"type": "underline"},
|
|
594
|
+
],
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
"type": "text",
|
|
598
|
+
"text": "]",
|
|
599
|
+
},
|
|
537
600
|
],
|
|
538
601
|
"robocloud": [
|
|
539
602
|
{
|
|
@@ -594,6 +657,7 @@ class Jira:
|
|
|
594
657
|
"type": "doc",
|
|
595
658
|
"content": []
|
|
596
659
|
+ (self.__error_string_markup(error_string, exc_info) if error_string else [])
|
|
660
|
+
+ self.__bot_name_markup()
|
|
597
661
|
+ self.__date_markup()
|
|
598
662
|
+ self.__runlink_markup()
|
|
599
663
|
+ self.__environment_markup()
|
|
@@ -839,11 +903,20 @@ class Jira:
|
|
|
839
903
|
Returns:
|
|
840
904
|
The response from creating the ticket.
|
|
841
905
|
"""
|
|
842
|
-
|
|
843
|
-
self._issue_types.get("
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
)
|
|
906
|
+
if CONFIG.STAGE.lower() == "hypercare":
|
|
907
|
+
issue_type = self._issue_types.get("hypercare") or self._issue_types.get("epic")
|
|
908
|
+
elif CONFIG.STAGE.lower() == "support":
|
|
909
|
+
issue_type = self._issue_types.get("support") or self._issue_types.get("epic")
|
|
910
|
+
elif CONFIG.STAGE.lower() == "delivery" and self._project_key == CONFIG.SUPPORT_BOARD:
|
|
911
|
+
issue_type = self._issue_types.get("development") or self._issue_types.get("epic")
|
|
912
|
+
else:
|
|
913
|
+
issue_type = (
|
|
914
|
+
self._issue_types.get("bug")
|
|
915
|
+
or self._issue_types.get("task")
|
|
916
|
+
or self._issue_types.get("support")
|
|
917
|
+
or self._issue_types.get("epic")
|
|
918
|
+
)
|
|
919
|
+
|
|
847
920
|
issue = self.__generate_issue_body(
|
|
848
921
|
summary=summary[:255].split("\n")[0],
|
|
849
922
|
description=description,
|
|
@@ -877,7 +950,7 @@ class Jira:
|
|
|
877
950
|
for attachment in attachments:
|
|
878
951
|
if os.path.exists(str(attachment)):
|
|
879
952
|
self.add_attachment(attachment, ticket_id)
|
|
880
|
-
if self._webhook_url:
|
|
953
|
+
if self._webhook_url and self._project_key != CONFIG.SUPPORT_BOARD:
|
|
881
954
|
self.move_ticket_to_board(ticket_id)
|
|
882
955
|
return response
|
|
883
956
|
|
|
@@ -1188,7 +1261,7 @@ class Jira:
|
|
|
1188
1261
|
return response[0]["accountId"]
|
|
1189
1262
|
|
|
1190
1263
|
@staticmethod
|
|
1191
|
-
def
|
|
1264
|
+
def sanitize_summary(exception: Union[Exception, str]) -> str:
|
|
1192
1265
|
"""Remove locators from the exception.
|
|
1193
1266
|
|
|
1194
1267
|
Args:
|
|
@@ -1197,9 +1270,11 @@ class Jira:
|
|
|
1197
1270
|
Returns:
|
|
1198
1271
|
str: The cleaned exception string.
|
|
1199
1272
|
"""
|
|
1273
|
+
message = re.sub("<([a-z]+)(?![^>]*\/>)[^>]*>", r"<\1>", str(exception))
|
|
1274
|
+
message = re.sub(">([^<]+)<\/", ">...</", message)
|
|
1200
1275
|
if "selenium" not in exception.__class__.__name__.lower() and not isinstance(exception, AssertionError):
|
|
1201
|
-
return str(
|
|
1202
|
-
return re.sub(r"\'(.+)\'", "'...'",
|
|
1276
|
+
return str(message)
|
|
1277
|
+
return re.sub(r"\'(.+)\'", "'...'", message)
|
|
1203
1278
|
|
|
1204
1279
|
def __create_summary(self, exc_type: type, exc_value: Union[Exception, str], exc_traceback: TracebackType) -> str:
|
|
1205
1280
|
"""Create the summary of the ticket.
|
|
@@ -1214,8 +1289,16 @@ class Jira:
|
|
|
1214
1289
|
"""
|
|
1215
1290
|
frames = get_frames(exc_traceback)
|
|
1216
1291
|
file_name, line_no, _, _ = frames[-1]
|
|
1217
|
-
summary = (
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
)
|
|
1221
|
-
|
|
1292
|
+
summary = f"[{exc_type.__name__}:{os.path.basename(file_name)}:{line_no}]"
|
|
1293
|
+
if self._project_key == CONFIG.SUPPORT_BOARD and CONFIG.ADMIN_CODE:
|
|
1294
|
+
summary = CONFIG.ADMIN_CODE + " - " + summary
|
|
1295
|
+
if CONFIG.LIMITS.SUMMARY_LENGTH <= len(summary):
|
|
1296
|
+
return summary
|
|
1297
|
+
else:
|
|
1298
|
+
message = self.sanitize_summary(exc_value)
|
|
1299
|
+
message = (
|
|
1300
|
+
message
|
|
1301
|
+
if len(message) <= CONFIG.LIMITS.SUMMARY_LENGTH - len(summary)
|
|
1302
|
+
else message[: CONFIG.LIMITS.SUMMARY_LENGTH - len(summary)] + "..."
|
|
1303
|
+
)
|
|
1304
|
+
return summary + " " + message
|
|
@@ -7,6 +7,8 @@ try:
|
|
|
7
7
|
work_items.get_input_work_item()
|
|
8
8
|
work_item = work_items.get_work_item_variables()
|
|
9
9
|
variables = work_item.get("variables", dict())
|
|
10
|
+
metadata = work_item.get("metadata", dict())
|
|
10
11
|
except (ImportError, KeyError):
|
|
11
12
|
logger.warning("Workitems unavailable. Variables will be empty.")
|
|
12
13
|
variables = {}
|
|
14
|
+
metadata = {}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|