FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: report checked value in JSON output for passing checks · commit-check/commit-check@6a38436 · GitHub

Commit 6a38436

Browse files
committed
feat: report checked value in JSON output for passing checks
validate_all_detailed now populates CheckOutcome.value on pass as well as fail, with the concrete value each validator checked (commit subject, branch name, author name/email, push refs). Structured consumers such as --format json, the Python API, and commit-check-action can therefore show what was validated even when every check passed.
1 parent 5c39e5f commit 6a38436

3 files changed

Lines changed: 83 additions & 0 deletions

File tree

‎commit_check/engine.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ class CheckOutcome:
5555

5656
check: str
5757
status: str # "pass" or "fail"
58+
# The concrete value that was checked (subject, branch, author, ...),
59+
# populated on both pass and fail so consumers can report what was
60+
# validated even when the check succeeded.
5861
value: str = ""
5962
error: str = ""
6063
suggest: str = ""
@@ -87,6 +90,11 @@ def __init__(self, rule: ValidationRule):
8790
self._compact: bool = False
8891
# Populated by _print_failure() on every failure, regardless of mode.
8992
self._last_failure: dict[str, str] | None = None
93+
# Populated by subclasses on every validation (pass or fail) with the
94+
# concrete value that was checked (subject, branch, author, ...), so
95+
# structured consumers (--format json, validate_all_detailed) can
96+
# report what was checked even when the check passed.
97+
self._checked_value: str = ""
9098

9199
@abstractmethod
92100
def validate(self, context: ValidationContext) -> ValidationResult:
@@ -244,6 +252,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
244252
if not message:
245253
return ValidationResult.PASS
246254

255+
self._checked_value = message
256+
247257
import re
248258

249259
if self.rule.regex and re.match(self.rule.regex, message):
@@ -264,6 +274,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
264274
if not subject:
265275
return ValidationResult.PASS
266276

277+
self._checked_value = subject
278+
267279
return self._validate_subject(subject)
268280

269281
def _get_subject(self, context: ValidationContext) -> str:
@@ -369,6 +381,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
369381
if not author_value:
370382
return ValidationResult.PASS
371383

384+
self._checked_value = author_value
385+
372386
return self._validate_author(author_value)
373387

374388
def _get_author_value(self, context: ValidationContext) -> str:
@@ -429,6 +443,7 @@ def validate(self, context: ValidationContext) -> ValidationResult:
429443
branch_name = (
430444
context.stdin_text.strip() if context.stdin_text else get_branch_name()
431445
)
446+
self._checked_value = branch_name
432447

433448
if not self.rule.regex:
434449
return ValidationResult.PASS
@@ -451,6 +466,7 @@ def validate(self, context: ValidationContext) -> ValidationResult:
451466

452467
current_branch = get_branch_name()
453468
target_pattern = self.rule.regex
469+
self._checked_value = current_branch
454470

455471
if not target_pattern:
456472
return ValidationResult.PASS
@@ -525,6 +541,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
525541
if not message:
526542
return ValidationResult.PASS
527543

544+
self._checked_value = message
545+
528546
import re
529547

530548
if self.rule.regex and re.search(self.rule.regex, message):
@@ -545,6 +563,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
545563
if not message:
546564
return ValidationResult.PASS
547565

566+
self._checked_value = message
567+
548568
# Split message into lines and check if there's content after the subject
549569
lines = message.strip().split("\n")
550570

@@ -598,6 +618,8 @@ def _check_current_branch_against_upstream(self) -> ValidationResult:
598618
if not upstream_ref:
599619
return ValidationResult.PASS
600620

621+
self._checked_value = f"{get_branch_name()} -> {upstream_ref}"
622+
601623
target_ref = get_upstream_remote_sha(upstream_ref) or upstream_ref
602624
returncode = git_merge_base(target_ref, "HEAD")
603625
if (
@@ -627,6 +649,7 @@ def _check_push_line(self, line: str) -> ValidationResult:
627649
parts[2],
628650
parts[3],
629651
)
652+
self._checked_value = f"{local_ref} -> {remote_ref}"
630653

631654
# Zero SHA for remote means a new branch push (not a force push)
632655
if remote_sha == self.ZERO_SHA:
@@ -677,6 +700,8 @@ def validate(self, context: ValidationContext) -> ValidationResult:
677700
if not message:
678701
return ValidationResult.PASS
679702

703+
self._checked_value = message
704+
680705
# Check if this commit type is allowed based on rule configuration
681706
is_allowed = self._is_commit_type_allowed(message)
682707

@@ -886,6 +911,7 @@ def validate_all_detailed(self, context: ValidationContext) -> list[CheckOutcome
886911
CheckOutcome(
887912
check=rule.check,
888913
status="pass",
914+
value=validator._checked_value or "",
889915
rule_id=rule.rule_id or "",
890916
docs_url=rule.docs_url or "",
891917
)

‎tests/engine_test.py‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,46 @@ def test_validation_engine_validate_all_pass(self):
11581158
result = engine.validate_all(context)
11591159
assert result == ValidationResult.PASS
11601160

1161+
@pytest.mark.benchmark
1162+
def test_validate_all_detailed_reports_value_on_pass(self):
1163+
"""Passed checks still report the concrete value that was checked."""
1164+
rules = [
1165+
ValidationRule(check="message", regex=r"^feat:"),
1166+
ValidationRule(check="subject_imperative", regex=r""),
1167+
]
1168+
engine = ValidationEngine(rules)
1169+
context = ValidationContext(stdin_text="feat: add feature")
1170+
1171+
outcomes = engine.validate_all_detailed(context)
1172+
assert len(outcomes) == 2
1173+
assert all(o.status == "pass" for o in outcomes)
1174+
by_check = {o.check: o for o in outcomes}
1175+
assert by_check["message"].value == "feat: add feature"
1176+
assert by_check["subject_imperative"].value == "feat: add feature"
1177+
1178+
@pytest.mark.benchmark
1179+
def test_validate_all_detailed_author_reports_author_name(self):
1180+
"""Author check reports the checked identity even when it passes."""
1181+
rules = [ValidationRule(check="author_name", regex=r"^Jane")]
1182+
engine = ValidationEngine(rules)
1183+
1184+
with patch(GIT_CONFIG_VALUE, return_value="Jane Doe"):
1185+
outcomes = engine.validate_all_detailed(ValidationContext())
1186+
1187+
assert outcomes[0].status == "pass"
1188+
assert outcomes[0].value == "Jane Doe"
1189+
1190+
@pytest.mark.benchmark
1191+
def test_validate_all_detailed_branch_reports_branch_name(self):
1192+
"""Branch check reports the branch name even when it passes."""
1193+
rules = [ValidationRule(check="branch", regex=r"^feature/")]
1194+
engine = ValidationEngine(rules)
1195+
context = ValidationContext(stdin_text="feature/add-login")
1196+
1197+
outcomes = engine.validate_all_detailed(context)
1198+
assert outcomes[0].status == "pass"
1199+
assert outcomes[0].value == "feature/add-login"
1200+
11611201
@pytest.mark.benchmark
11621202
def test_validation_engine_validate_all_fail(self):
11631203
"""Test ValidationEngine with some validations failing."""

‎tests/main_test.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,23 @@ def test_json_format_valid_message_returns_pass(self, mocker, capsys, monkeypatc
555555
assert isinstance(data["checks"], list)
556556
assert all("check" in c and "status" in c for c in data["checks"])
557557

558+
@pytest.mark.benchmark
559+
def test_json_format_pass_reports_checked_value(self, mocker, capsys, monkeypatch):
560+
"""JSON output reports the checked value even when the check passed."""
561+
mocker.patch("sys.stdin.isatty", return_value=False)
562+
mocker.patch("sys.stdin.read", return_value="feat: add new feature\n")
563+
564+
monkeypatch.setattr("sys.argv", [CMD, "-m", "--format", "json"])
565+
main()
566+
567+
out, _ = capsys.readouterr()
568+
data = json.loads(out)
569+
passed_with_value = [
570+
c for c in data["checks"] if c["status"] == "pass" and c["value"]
571+
]
572+
assert passed_with_value
573+
assert all(c["value"] == "feat: add new feature" for c in passed_with_value)
574+
558575
@pytest.mark.benchmark
559576
def test_json_format_invalid_message_returns_fail(
560577
self, mocker, capsys, monkeypatch

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL