Skip to content

feat: add max-header-length for header length check#85

Open
aj3sh wants to merge 1 commit intoopensource-nepal:mainfrom
aj3sh:max-header-length
Open

feat: add max-header-length for header length check#85
aj3sh wants to merge 1 commit intoopensource-nepal:mainfrom
aj3sh:max-header-length

Conversation

@aj3sh
Copy link
Copy Markdown
Member

@aj3sh aj3sh commented Mar 13, 2026

Description

  • add max-header-length for header length check
  • disable default header length check
  • update dependency actions version
  • add release details on contributing file

Related Issue

Fixes #67

Type of Change

Please mark the appropriate option below to describe the type of change your pull request introduces:

  • Bug fix
  • New feature
  • Enhancement
  • Documentation update
  • Refactor
  • Other (please specify)

Checklist

  • My pull request has a clear title and description.
  • I have used semantic commit messages.
    Examples: "fix: Fixed foobar bug", "feat(accounts): Added foobar feature".
  • I have added/updated the necessary documentation on README.md.
  • I have added appropriate test cases (if applicable) to ensure the changes are functioning correctly.

Additional Notes

[Add any additional notes or context that you think the reviewers should know about.]

By submitting this pull request, I confirm that I have read and complied with the contribution guidelines of this project.

@aj3sh aj3sh requested review from golesuman and sugat009 March 13, 2026 06:43
Copy link
Copy Markdown
Contributor

@sugat009 sugat009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supply chain security: please keep SHA pins for write-privileged actions. See inline comments.

Comment thread .github/workflows/release-please.yml
Comment thread .github/workflows/release-please.yml Outdated
Copy link
Copy Markdown
Contributor

@sugat009 sugat009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: --max-header-length falsy check allows invalid values. See inline comment with reproduction steps.

Comment thread src/commitlint/linter/validators.py
Copy link
Copy Markdown
Contributor

@sugat009 sugat009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question on the architectural direction for configurable validator options. See inline comment.

Comment thread src/commitlint/linter/validators.py Outdated
Copy link
Copy Markdown
Contributor

@sugat009 sugat009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional review comments using conventional comments labels.

Comment thread github_actions/action/utils.py Outdated
Comment thread src/commitlint/config.py
Comment thread src/commitlint/cli.py Outdated
Comment thread tests/test_cli.py Outdated
@aj3sh aj3sh force-pushed the max-header-length branch from be9b5b3 to 7844137 Compare March 28, 2026 16:07
@aj3sh aj3sh requested a review from sugat009 March 28, 2026 16:21
- disable default header length check
- update dependency actions version
- add release details on contributing file
@aj3sh aj3sh force-pushed the max-header-length branch from 7844137 to 3c7c482 Compare April 12, 2026 11:42
Copy link
Copy Markdown
Contributor

@sugat009 sugat009 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat: add max-header-length

Good feature addition overall. The AppParams dataclass is well-proportioned, positive_int_type handles edge cases correctly, and the validate_fns rename removes ambiguity with the module name.

Two test bugs need fixing before merge, and one product decision needs resolution. See inline comments.

Findings

# Label File Finding
1 issue (blocking) test__linter.py:29 skip_detail test passes False instead of True (copy-paste bug)
2 issue (blocking) test_run_commitlint.py:65 All 4 tests fail in isolation due to env var leakage
3 question (blocking) validators.py:77 Breaking change: header length check is now opt-in (was always 72)
4 issue utils.py:67 Docstring claims "returns None" but raises ValueError
5 suggestion utils.py:76 Add positive-int validation to match CLI path
6 suggestion cli.py:276 DRY: extract AppParams construction with dataclasses.replace
7 suggestion test_cli.py:409 Missing test coverage for --file/--hash/--from-hash + max-header-length
8 nitpick app_params.py:18 Typo: "Specially" and "Github"
9 nitpick validators.py:36 self.params public vs self._commit_message private
10 suggestion release-please.yml:22 Pin actions to commit hashes in privileged workflow

def test__lint_commit_message__skip_detail(fixture_data):
commit_message, expected_success, _ = fixture_data
success, _ = lint_commit_message(commit_message, skip_detail=True)
success, _ = lint_commit_message(commit_message, AppParams(skip_detail=False))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): Copy-paste bug. This passes skip_detail=False, making it identical to test_lint_commit_message above. Should be AppParams(skip_detail=True) to exercise the SimplePatternValidator + fail_fast code path.

"subprocess.check_output",
return_value="feat: valid commit message",
)
@patch.dict(os.environ, {**os.environ, "INPUT_MAX_HEADER_LENGTH": "72"})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (blocking): All four tests in this file fail when run in isolation:

pytest tests/test_github_actions/test_run/test_run_commitlint.py
# 4 failed — KeyError: 'INPUT_VERBOSE' or 'INPUT_MAX_HEADER_LENGTH'

This test patches INPUT_MAX_HEADER_LENGTH but not INPUT_VERBOSE. The three existing tests above patch INPUT_VERBOSE but not INPUT_MAX_HEADER_LENGTH. Since run_commitlint() now reads both env vars, each test needs both.

They only pass in the full suite because set_github_env_vars() from other test files leaks env vars without cleanup.

Fix: add both env vars to every @patch.dict:

@patch.dict(os.environ, {**os.environ, "INPUT_VERBOSE": "false", "INPUT_MAX_HEADER_LENGTH": ""})

"""
max_header_length = self.params.max_header_length

if max_header_length is None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (blocking): This changes the default behavior from "always check 72 chars" to "never check unless opt-in." Users upgrading will silently lose header length enforcement.

The Conventional Commits spec doesn't mandate a header length, so opt-in is defensible, but this is a breaking change for anyone relying on the implicit 72-char guard.

Two options:

  1. Keep as opt-in (current), but document as a breaking change and bump the major version.
  2. Default to 72 (--max-header-length defaults to 72, add --max-header-length 0 to disable).

Which is intended?

key: Input key.

Returns:
The integer value of the input. If not integer, returns None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (non-blocking): Docstring says "If not integer, returns None" but the function raises ValueError for non-integer, non-empty strings (line 78). Only empty string returns None.

Returns:
    The integer value of the input, or None if the value is an empty string.

Raises:
    ValueError: If the value is a non-empty, non-integer string.

return None

try:
return int(val)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): The CLI validates > 0 via positive_int_type(), but this function accepts negative and zero values. A user setting max_header_length: 0 in their workflow YAML would get a confusing subprocess error instead of a clear message.

Consider adding a positive-integer check:

result = int(val)
if result <= 0:
    raise ValueError(f"Input '{key}' must be a positive integer (> 0).")
return result

Comment thread src/commitlint/cli.py
_handle_commit_message(
commit_message, skip_detail=args.skip_detail, hide_input=args.hide_input
commit_message,
AppParams(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): AppParams is constructed four times in main(), and three are identical (only the --file branch adds strip_comments=True). Adding a new field to AppParams requires four edits.

Consider:

from dataclasses import replace

params = AppParams(
    skip_detail=args.skip_detail,
    hide_input=args.hide_input,
    max_header_length=args.max_header_length,
)
# file branch only:
_handle_commit_message(commit_message, replace(params, strip_comments=True))
# all other branches:
_handle_commit_message(commit_message, params)

Comment thread tests/test_cli.py
main()
assert config.verbose is True

# main : max-header-length
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): max_header_length is tested only for the direct commit message path. The --file, --hash, and --from-hash branches also construct AppParams with max_header_length but have no corresponding tests. At minimum, a test for the --file path (which uniquely adds strip_comments=True) would catch branch-specific bugs.

# Skips the detailed error check (fails immediately without detail error message).
skip_detail: bool = False

# Hide input from stdout/stderr. Specially used by Github Actions.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (non-blocking): "Specially" → "Specifically" and "Github" → "GitHub" (capital H, matching the project's naming elsewhere).

def __init__(self, commit_message: str) -> None:
def __init__(self, commit_message: str, params: AppParams) -> None:
self._commit_message = commit_message
self.params = params
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (non-blocking): self.params is public while self._commit_message and self._errors use the private convention. Consider self._params for consistency.

uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0

- uses: actions/checkout@v4
- uses: actions/checkout@v6
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): Lines 11-12 say "always pin actions to a specific commit hash," but this line and actions/setup-python@v6 (line 37) use mutable version tags in a workflow with id-token: write, contents: write, and pull-requests: write permissions. Consider pinning to commit hashes, like you did for release-please-action and gh-action-pypi-publish in the same file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Commitlint fails after squash merge

2 participants