Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 3 additions & 1 deletion apps/jira_utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ pyutils-jira --help
```

## Config file
A config file with the jira connection parameters like url, token, resolved_statuses, skip_project_ids, target_versions should be passed to command line option `--cfg-file`
A config file with the jira connection parameters like url, token, resolved_statuses, skip_project_ids,
target_versions should be passed to command line option `--cfg-file`

### Example:

Expand All @@ -33,6 +34,7 @@ pyutils-jira:
target_versions:
- 1.0.0
- 2.0.1
- 3.0.z
issue_pattern: "([A-Z]+-[0-9]+)"
version_string_not_targeted_jiras: "vfuture"
```
Expand Down
47 changes: 35 additions & 12 deletions apps/jira_utils/jira_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
LOGGER = get_logger(name=__name__)


@retry(retry=retry_if_exception_type(JIRAError), stop=stop_after_attempt(3), wait=wait_fixed(2))
@retry(
retry=retry_if_exception_type(JIRAError),
stop=stop_after_attempt(3),
wait=wait_fixed(2),
)
@lru_cache
def get_issue(
jira: JIRA,
Expand Down Expand Up @@ -97,6 +101,8 @@ def get_jira_information(
file_name: str,
) -> tuple[str, str]:
jira_error_string = ""
re_compile = r"(?<![\d.])\d+\.\d+(?:\.(?:\d+|z))?\b"

try:
# check resolved status:
jira_issue_metadata = get_issue(jira=jira_object, jira_id=jira_id).fields
Expand All @@ -105,17 +111,20 @@ def get_jira_information(
if current_jira_status in resolved_status:
jira_error_string += f"{jira_id} current status: {current_jira_status} is resolved."

# validate correct target version if provided:
# validate a correct target version if provided:
if jira_target_versions:
if skip_project_ids and jira_id.startswith(tuple(skip_project_ids)):
return file_name, jira_error_string

fix_version = (
re.search(r"([\d.]+)", jira_issue_metadata.fixVersions[0].name)
if (jira_issue_metadata.fixVersions)
else None
)
current_target_version = fix_version.group(1) if fix_version else target_version_str
# If a bug has a fix version, extract it using regex
if (jira_fix_versions := jira_issue_metadata.fixVersions) and (
found_version := re.findall(re_compile, jira_fix_versions[0].name)
):
current_target_version = found_version[0]

else:
current_target_version = target_version_str

if not any([current_target_version == version for version in jira_target_versions]):
jira_error_string += (
f"{jira_id} target version: {current_target_version}, does not match expected "
Expand Down Expand Up @@ -174,8 +183,18 @@ def process_jira_command_line_config_file(
help="Provide comma separated list of Jira Project keys, against which version check should be skipped.",
type=ListParamType(),
)
@click.option("--url", help="Provide the Jira server URL", type=click.STRING, default=os.getenv("JIRA_SERVER_URL"))
@click.option("--token", help="Provide the Jira token.", type=click.STRING, default=os.getenv("JIRA_TOKEN"))
@click.option(
"--url",
help="Provide the Jira server URL",
type=click.STRING,
default=os.getenv("JIRA_SERVER_URL"),
)
@click.option(
"--token",
help="Provide the Jira token.",
type=click.STRING,
default=os.getenv("JIRA_TOKEN"),
)
@click.option(
"--issue-pattern",
help="Provide the regex for Jira ids",
Expand Down Expand Up @@ -226,11 +245,15 @@ def get_jira_mismatch(
target_versions=target_versions,
)

jira_obj = JIRA(token_auth=jira_config_dict["token"], options={"server": jira_config_dict["url"]})
jira_obj = JIRA(
token_auth=jira_config_dict["token"],
options={"server": jira_config_dict["url"]},
)
jira_error: dict[str, str] = {}

if jira_id_dict := get_jiras_from_python_files(
issue_pattern=jira_config_dict["issue_pattern"], jira_url=jira_config_dict["url"]
issue_pattern=jira_config_dict["issue_pattern"],
jira_url=jira_config_dict["url"],
):
with concurrent.futures.ThreadPoolExecutor() as executor:
for file_name, ids in jira_id_dict.items():
Expand Down
13 changes: 10 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ dependencies = [
"tenacity>=9.0.0,<10",
"python-simple-logger>=2.0.0,<3",
"pyhelper-utils>=1.0.1,<2",
"ast-comments>=1.2.2"
"ast-comments>=1.2.2",
"click (>=8.1.7)",
]

[[project.authors]]
Expand All @@ -39,7 +40,7 @@ dependencies = [
omit = [ "tests/*" ]

[tool.coverage.report]
fail_under = 65
fail_under = 68
skip_empty = true

[tool.coverage.html]
Expand All @@ -55,6 +56,7 @@ include = [ "apps" ]
[tool.hatch.build.targets.wheel]
include = [ "apps" ]


[tool.mypy]
show_error_codes = true
warn_unused_ignores = true
Expand All @@ -78,7 +80,12 @@ output-format = "grouped"

[dependency-groups]
dev = [ "ipdb>=0.13.13,<0.14", "ipython" ]
test = [ "pytest>=8.0.0,<9", "pytest-cov>=6.0.0,<7" ]
test = [
"ipdb>=0.13.13",
"ipython>=8.18.1",
"pytest>=8.0.0,<9",
"pytest-cov>=6.0.0,<7",
]

[build-system]
requires = [ "hatchling" ]
Expand Down
23 changes: 11 additions & 12 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
":dependencyDashboard",
":maintainLockFilesWeekly",
":prHourlyLimitNone"
],
"prConcurrentLimit": 0,
"lockFileMaintenance": {
"enabled": true
},
"packageRules": [
{
"matchPackagePatterns": [
"python-simple-logger",
"pylero",
"pyhelper-utils",
"pytest-mock",
"pyyaml",
"ipdb",
"ipython",
"pytest",
"pytest-cov"
],
"groupName": "poetry-deps"
"matchPackagePatterns": ["*"],
"groupName": "python-deps"
}
]
}
63 changes: 54 additions & 9 deletions tests/jira_utils/test_jira_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)

LOGGER = get_logger(name=__name__)
BASE_COMMAND = "poetry run python apps/jira_utils/jira_information.py --verbose "
BASE_COMMAND = "uv run apps/jira_utils/jira_information.py --verbose "


def test_process_jira_command_line_config_file_empty_config_token() -> None:
Expand Down Expand Up @@ -80,7 +80,16 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
# Test case 1: Issue with no jira target versions and not resolved status
("issue1", ["resolved"], [], "1.0", "file1.txt", [], "", "1.0"),
# Test case 2: Issue with no jira target versions, but resolved status
("issue2", ["open"], [], "1.0", "file2.txt", [], "issue2 current status: open is resolved.", "1.0"),
(
"issue2",
["open"],
[],
"1.0",
"file2.txt",
[],
"issue2 current status: open is resolved.",
"1.0",
),
# Test case 3: Issue with no jira target versions, default resolved status
("issue3", [], [], "", "file3.txt", [], "", "1.1"),
# Test case 4: Issue with not resolved state, but matching jira target version
Expand All @@ -99,7 +108,29 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
# Test case 6: Issue that would be skipped for version check because of skip
("issue6", ["resolved"], ["1.0"], "1.0", "file6.txt", ["issue"], "", "1.1"),
# Test case 7: Issue that would be skipped for version check but fail resolved check
("issue7", ["open"], ["1.0"], "1.0", "file6.txt", ["issue"], "issue7 current status: open is resolved.", "1.1"),
(
"issue7",
["open"],
["1.0"],
"1.0",
"file6.txt",
["issue"],
"issue7 current status: open is resolved.",
"1.1",
),
# Test case 8: Issue with unresolved state, and matching jira z target version
("issue8", [], ["1.2.z"], "1.2,z", "file4.txt", [], "", "1.2.z"),
# Test case 9: Issue with unresolved state, and jira z target version not matching expected versions
(
"issue8",
[],
["1.2.3"],
"",
"file4.txt",
[],
"issue8 target version: 1.2.z, does not match expected version ['1.2.3'].",
"1.2.z",
),
],
ids=[
"test_no_jira_versions_no_resolved_status",
Expand All @@ -109,6 +140,8 @@ def test_process_jira_command_line_config_file_valid_config(mocker):
"test_no_target_versions_not_resolved_state",
"test_skip_version_check",
"test_skip_version_check_fail_status_check",
"test_matching_target_z_version",
"test_non_matching_target_z_version",
],
)
def test_get_jira_information(
Expand All @@ -131,8 +164,8 @@ def test_get_jira_information(

if jira_target_versions:
mocker.patch(
"apps.jira_utils.jira_information.re.search",
return_value=mocker.MagicMock(group=lambda x: test_jira_version),
"apps.jira_utils.jira_information.re.findall",
return_value=[test_jira_version],
)
result = get_jira_information(
jira_object=mock_jira,
Expand Down Expand Up @@ -160,10 +193,20 @@ def test_get_jira_information(
@pytest.mark.parametrize(
"content_and_expected",
[
pytest.param({"content": "pytest.mark.jira(ABC-1111)", "expected": {"ABC-1111"}}, id="pytest_mark_jira"),
pytest.param({"content": "JIRA ID is jira_id=ABC-1111", "expected": {"ABC-1111"}}, id="jira_id="),
pytest.param(
{"content": "JIRA URL is https://example.com/browse/ABC-1111", "expected": {"ABC-1111"}}, id="jira_url="
{"content": "pytest.mark.jira(ABC-1111)", "expected": {"ABC-1111"}},
id="pytest_mark_jira",
),
pytest.param(
{"content": "JIRA ID is jira_id=ABC-1111", "expected": {"ABC-1111"}},
id="jira_id=",
),
pytest.param(
{
"content": "JIRA URL is https://example.com/browse/ABC-1111",
"expected": {"ABC-1111"},
},
id="jira_url=",
),
pytest.param(
{
Expand All @@ -184,6 +227,8 @@ def test_get_jira_information(
)
def test_get_jira_ids_from_file_content(content_and_expected):
jira_ids = get_jira_ids_from_file_content(
file_content=content_and_expected["content"], issue_pattern=r"([A-Z]+-[0-9]+)", jira_url="https://example.com"
file_content=content_and_expected["content"],
issue_pattern=r"([A-Z]+-[0-9]+)",
jira_url="https://example.com",
)
assert jira_ids == content_and_expected["expected"]
2 changes: 1 addition & 1 deletion tests/polarion/test_polarion_automated.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from pyhelper_utils.shell import run_command

BASE_COMMAND = "poetry run python apps/polarion/polarion_set_automated.py --verbose"
BASE_COMMAND = "uv run apps/polarion/polarion_set_automated.py --verbose"


def test_missing_required_params_set_automated():
Expand Down
2 changes: 1 addition & 1 deletion tests/polarion/test_verify_polarion_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from pyhelper_utils.shell import run_command

BASE_COMMAND = "poetry run python apps/polarion/polarion_verify_tc_requirements.py"
BASE_COMMAND = "uv run apps/polarion/polarion_verify_tc_requirements.py"


def test_missing_project_id():
Expand Down
7 changes: 4 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[tox]
envlist = {python3.9,python3.10,python3.11,python3.12}-{unittest},
Comment thread
myakove marked this conversation as resolved.
envlist = 3.9, 3.1{0,1,2,3}
skipsdist = True


[testenv]
setenv =
PYTHONPATH = {toxinidir}
deps =
uv
commands =
uv run pytest tests
uv python pin python{envname}
uv sync --locked --all-extras --dev --group test
uv run pytest tests
6 changes: 6 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.