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
5 changes: 3 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ jobs:
uv sync --locked
- name: Lint
run: |
uv run black --check src test
uv run isort --check src test
uv run ruff format --check
uv run ruff check
uv run ty check
- name: Test with pytest
run: |
uv run pytest --cov
21 changes: 13 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ classifiers = [
]
dependencies = [
"typer>=0.21.1,<0.22",
"typing-extensions>=4.15.0,<5",
"gitpython>=3.1.46,<4",
]

Expand All @@ -26,12 +25,10 @@ jira-commit-msg = "jira_commit_msg:app"
[dependency-groups]
dev = [
"pytest>=8.4.2,<9",
"mypy>=1.19.1,<2",
"ruff>=0.14.13,<0.15",
"black>=25.11.0,<26",
"pytest-coverage>=0.0,<0.1",
"pre-commit>=4.3.0,<5",
"isort>=6.1.0,<7",
"ty>=0.0.13",
]


Expand All @@ -57,9 +54,17 @@ fail_under = 100
skip_empty = true
skip_covered = true

[tool.black]
[tool.ruff]
line-length = 100

[tool.isort]
line_length = 100
profile = "black"
[tool.ruff.lint]
select = ["ALL"]
ignore = ["D", "FBT", "COM812"]

[tool.ruff.lint.per-file-ignores]
"test/**" = [
"S101", # use of assert
"ANN201", # missing return type annotations,

"INP001",
]
11 changes: 5 additions & 6 deletions src/jira_commit_msg/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
from pathlib import Path
from re import compile as regex
from typing import Optional
from typing import Annotated

from git import Repo
from typer import Argument, Exit, Option, Typer, echo
from typing_extensions import Annotated

app = Typer()

JIRA_ISSUE_REGEX = regex(r"(?P<issue_id>[A-Z]+-\d+)")


def branch_name(repo: Optional[Repo] = None):
def branch_name(repo: Repo | None = None) -> str:
"""Get branch name from git repo"""
if repo is None:
repo = Repo(Path.cwd())
Expand All @@ -22,7 +21,7 @@ def branch_name(repo: Optional[Repo] = None):


@app.command()
def prepare_commit_msg(
def prepare_commit_msg( # noqa: PLR0913
commit_msg_file: Annotated[
Path,
Argument(
Expand All @@ -42,14 +41,14 @@ def prepare_commit_msg(
] = False,
skip_merge_commit: Annotated[bool, Option(help="Skip Merge Commit")] = True,
msg_source: Annotated[
Optional[str],
str | None,
Option(
"--commit-msg-source",
help="Message Source (second argument passed to prepare-commit-msg)",
envvar="PRE_COMMIT_COMMIT_MSG_SOURCE",
),
] = None,
):
) -> None:
"""Prepare commit message for Jira"""

if skip_merge_commit and msg_source == "merge":
Expand Down
125 changes: 57 additions & 68 deletions test/test_commit_jira_issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from subprocess import check_output
from tempfile import NamedTemporaryFile

from pytest import mark, param, raises
import pytest
from typer import Exit

from jira_commit_msg import branch_name, prepare_commit_msg
Expand All @@ -17,18 +17,17 @@ def commit_msg_file(source: Path):
yield Path(tmp.name)


@mark.parametrize(
"force_issue_id,skip_merge_commit",
@pytest.mark.parametrize(
("force_issue_id", "skip_merge_commit"),
[
param(False, False, id="no force no skip"),
param(True, False, id="force no skip"),
param(False, True, id="no force skip"),
param(True, True, id="force skip"),
pytest.param(False, False, id="no force no skip"),
pytest.param(True, False, id="force no skip"),
pytest.param(False, True, id="no force skip"),
pytest.param(True, True, id="force skip"),
],
)
def test_prepare_commit_msg_without_issue_no_cc(force_issue_id, skip_merge_commit):
"""
Test that the hook adds the issue id to the commit message
def test_prepare_commit_msg_without_issue_no_cc(force_issue_id: bool, skip_merge_commit: bool):
"""Test that the hook adds the issue id to the commit message

force_issue_id and skip_merge_commit should not have any effect on the result
"""
Expand All @@ -38,18 +37,17 @@ def test_prepare_commit_msg_without_issue_no_cc(force_issue_id, skip_merge_commi
assert tmp.read_text() == Path("test/commit-files/commit-msg-with-issue.txt").read_text()


@mark.parametrize(
"force_issue_id,skip_merge_commit",
@pytest.mark.parametrize(
("force_issue_id", "skip_merge_commit"),
[
param(False, False, id="no force no skip"),
param(True, False, id="force no skip"),
param(False, True, id="no force skip"),
param(True, True, id="force skip"),
pytest.param(False, False, id="no force no skip"),
pytest.param(True, False, id="force no skip"),
pytest.param(False, True, id="no force skip"),
pytest.param(True, True, id="force skip"),
],
)
def test_prepare_commit_msg_with_issue_no_cc(force_issue_id, skip_merge_commit):
"""
Test idempotency of the hook in case the issue is already written in commit
def test_prepare_commit_msg_with_issue_no_cc(force_issue_id: bool, skip_merge_commit: bool):
"""Test idempotency of the hook in case the issue is already written in commit

force_issue_id and skip_merge_commit should not have any effect on the result
"""
Expand All @@ -59,54 +57,49 @@ def test_prepare_commit_msg_with_issue_no_cc(force_issue_id, skip_merge_commit):
assert tmp.read_text() == Path("test/commit-files/commit-msg-with-issue.txt").read_text()


@mark.parametrize(
"force_issue_id,skip_merge_commit",
@pytest.mark.parametrize(
("force_issue_id", "skip_merge_commit"),
[
param(False, False, id="no force no skip"),
param(True, False, id="force no skip"),
param(False, True, id="no force skip"),
param(True, True, id="force skip"),
pytest.param(False, False, id="no force no skip"),
pytest.param(True, False, id="force no skip"),
pytest.param(False, True, id="no force skip"),
pytest.param(True, True, id="force skip"),
],
)
def test_prepare_commit_msg_without_issue_cc(force_issue_id, skip_merge_commit):
"""
Test that the hook adds the issue id to the commit message
"""
def test_prepare_commit_msg_without_issue_cc(force_issue_id: bool, skip_merge_commit: bool):
"""Test that the hook adds the issue id to the commit message"""
with commit_msg_file(Path("test/commit-files/commit-msg-without-issue-cc.txt")) as tmp:
prepare_commit_msg(tmp, "AA-123", True, force_issue_id, skip_merge_commit, "message")

assert tmp.read_text() == Path("test/commit-files/commit-msg-with-issue-cc.txt").read_text()


@mark.parametrize(
"force_issue_id,skip_merge_commit",
@pytest.mark.parametrize(
("force_issue_id", "skip_merge_commit"),
[
param(False, False, id="no force no skip"),
param(True, False, id="force no skip"),
param(False, True, id="no force skip"),
param(True, True, id="force skip"),
pytest.param(False, False, id="no force no skip"),
pytest.param(True, False, id="force no skip"),
pytest.param(False, True, id="no force skip"),
pytest.param(True, True, id="force skip"),
],
)
def test_prepare_commit_msg_with_issue_cc(force_issue_id, skip_merge_commit):
"""
Test idempotency of the hook in case the issue is already written in commit
"""
def test_prepare_commit_msg_with_issue_cc(force_issue_id: bool, skip_merge_commit: bool):
"""Test idempotency of the hook in case the issue is already written in commit"""
with commit_msg_file(Path("test/commit-files/commit-msg-with-issue-cc.txt")) as tmp:
prepare_commit_msg(tmp, "AA-123", True, force_issue_id, skip_merge_commit, "message")

assert tmp.read_text() == Path("test/commit-files/commit-msg-with-issue-cc.txt").read_text()


@mark.parametrize(
"conventional_commit,filename",
@pytest.mark.parametrize(
("conventional_commit", "filename"),
[
param(True, "test/commit-files/commit-msg-with-issue-cc.txt", id="cc"),
param(False, "test/commit-files/commit-msg-with-issue.txt", id="no cc"),
pytest.param(True, "test/commit-files/commit-msg-with-issue-cc.txt", id="cc"),
pytest.param(False, "test/commit-files/commit-msg-with-issue.txt", id="no cc"),
],
)
def test_prepare_commit_msg_with_issue_force_issue_id(conventional_commit, filename):
"""
Test that an issue id in the pattern will pass the issue id enforcement
def test_prepare_commit_msg_with_issue_force_issue_id(conventional_commit: bool, filename: str):
"""Test that an issue id in the pattern will pass the issue id enforcement
Commit msg file will be left unchanged
"""
with commit_msg_file(Path(filename)) as tmp:
Expand All @@ -115,47 +108,43 @@ def test_prepare_commit_msg_with_issue_force_issue_id(conventional_commit, filen
assert tmp.read_text() == Path(filename).read_text()


@mark.parametrize(
"conventional_commit,filename",
@pytest.mark.parametrize(
("conventional_commit", "filename"),
[
param(True, "test/commit-files/commit-msg-without-issue-cc.txt", id="cc"),
param(False, "test/commit-files/commit-msg-without-issue.txt", id="no cc"),
pytest.param(True, "test/commit-files/commit-msg-without-issue-cc.txt", id="cc"),
pytest.param(False, "test/commit-files/commit-msg-without-issue.txt", id="no cc"),
],
)
def test_prepare_commit_msg_without_issue_force_issue_id_no_issue(conventional_commit, filename):
"""
Test that the hook adds the issue id to the commit message
"""
def test_prepare_commit_msg_without_issue_force_issue_id_no_issue(
conventional_commit: bool, filename: str
):
"""Test that the hook adds the issue id to the commit message"""
with commit_msg_file(Path(filename)) as tmp:
with raises(Exit) as exit:
with pytest.raises(Exit) as exit_exc:
prepare_commit_msg(tmp, "master", conventional_commit, True, True, "message")

assert exit.value.exit_code == 1
assert exit_exc.value.exit_code == 1
# file should be left unchanged
assert tmp.read_text() == Path(filename).read_text()


@mark.parametrize(
"conventional_commit,filename",
@pytest.mark.parametrize(
("conventional_commit", "filename"),
[
param(True, "test/commit-files/commit-msg-without-issue-cc.txt", id="cc"),
param(False, "test/commit-files/commit-msg-without-issue.txt", id="no cc"),
pytest.param(True, "test/commit-files/commit-msg-without-issue-cc.txt", id="cc"),
pytest.param(False, "test/commit-files/commit-msg-without-issue.txt", id="no cc"),
],
)
def test_prepare_commit_msg_skip_merge_commits(conventional_commit, filename):
"""
Test that merge commits are bypassed, even when trying to force an issue id.
"""
def test_prepare_commit_msg_skip_merge_commits(conventional_commit: bool, filename: str):
"""Test that merge commits are bypassed, even when trying to force an issue id."""
with commit_msg_file(Path(filename)) as tmp:
prepare_commit_msg(tmp, "AA-123", conventional_commit, True, True, "merge")

assert tmp.read_text() == Path(filename).read_text()


def test_get_branch_name():
"""
Test that the branch name is correctly extracted from the repo
"""
from_git = check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode().strip()
"""Test that the branch name is correctly extracted from the repo."""
from_git = check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode().strip() # noqa: S607
from_repo = branch_name()
assert from_repo == from_git
Loading