ccval 0.5.0

A validator for conventional commits
ccval-0.5.0 is not a library.

Conventional Commits Validator

CI Release Docker

Validate commit messages using the Conventional Commits format with YAML, TOML, or JSON local configuration files.

Installation

crates.io

cargo install ccval

Homebrew

brew install andrey-fomin/tap/ccval

GitHub Releases

Download prebuilt binaries from GitHub Releases for Linux, macOS, and Windows.

macOS

On macOS, you may see a warning: "Apple could not verify 'ccval' is free of malware."

To bypass Gatekeeper, run:

xattr -d com.apple.quarantine /path/to/ccval

Alternatively, right-click the binary > Open > Open when prompted.

Docker

Images are available on Docker Hub: andreyfomin/ccval

Tag Base Git Support Size
:latest Alpine Yes ~11 MB
:distroless Distroless No ~1 MB

Use the :distroless variant for smaller images when only using stdin or file mode.

Validate stdin:

printf 'feat: new feature\n' | docker run --rm -i andreyfomin/ccval --stdin

Validate git commits (Alpine image only):

docker run --rm -v $(pwd):/repo -w /repo andreyfomin/ccval --trust-repo

GitHub Action

Use it in your workflow:

on: pull_request

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - uses: andrey-fomin/ccval@v0

The action automatically:

  • Supports push and pull_request events
  • Validates all commits in a PR (uses --no-merges)
  • Validates pushed non-merge commits on regular pushes and full pushed histories when needed
  • Skips deleted-ref push events
  • Skips push events with zero commits
  • Discovers conventional-commits.yaml or .github/conventional-commits.yaml
  • Supports the preset input (default or strict)
  • Limits validation to 100 commits by default, warning and skipping larger auto-detected or custom ranges

On push events, merge commits are skipped and the action prefers the exact pushed range from local history. If the pushed before commit is not available locally, the action falls back to the default-branch merge-base when possible and otherwise fails with a clear error. Deleted-ref pushes are skipped. Push events with zero commits are skipped. When validating push events, make sure your workflow fetches enough history (for example actions/checkout with fetch-depth: 0) so the required commit range is available.

Use @v0 to track the latest compatible v0.x.y release, or pin to a specific release tag like @v0.3.1. For a truly immutable reference, pin the action to a commit SHA instead of a tag.

Custom config:

- uses: andrey-fomin/ccval@v0
  with:
    config: '.github/ccval.yaml'

Built-in preset:

- uses: andrey-fomin/ccval@v0
  with:
    preset: strict

Override git arguments:

- uses: andrey-fomin/ccval@v0
  with:
    git-args: 'origin/main..HEAD --no-merges'

Limit checked commits:

- uses: andrey-fomin/ccval@v0
  with:
    max-commits: '250'

Usage

Usage: ccval [-c <path>] [-p <preset>] [-r <path>] [-T] [-- <git-log-args>...]
       ccval [-c <path>] [-p <preset>] --stdin
       ccval [-c <path>] [-p <preset>] -f <path>
       ccval -h

Validates commit messages from stdin, a file, or Git.

Modes:
  (default)            Validate commit(s) from git log
                       Use -- <git-log-args>... to pass arguments to git log
                       Default: -1 (last commit)

  --stdin              Read commit message from stdin
  -f, --file <path>    Read commit message from a file
  -h, --help           Show this help message

Options:
  -c, --config <path>  Use a custom config file path
  -p, --preset <name>  Use a built-in preset (default or strict)
  -r, --repository <path>
                       Path to Git repository working tree
                       Cannot be used with --stdin or --file
  -T, --trust-repo     Trust the repository despite ownership mismatch
                       Useful when running in containers or accessing
                       repositories owned by other users
                       Requires git mode (cannot use with --stdin or --file)

Examples:
  ccval                              # validate last commit
  ccval -- origin/main..HEAD         # validate commits on branch
  ccval -p strict                    # validate last commit with strict preset
  ccval -r /path/to/repo             # validate last commit in specific repo
  ccval -T                           # validate last commit, trusting repo
  ccval -r /repo -T                  # validate in container
  printf 'feat: msg\n' | ccval --stdin
  ccval --file .git/COMMIT_EDITMSG
  ccval -c config.yaml --stdin

Exit Codes

Code Meaning
0 Success
1 Validation failed
2 Parse error
3 Config error
4 CLI usage error
5 I/O error
6 Git error

How It Works

ccval works in two steps:

  1. It parses the commit message structure.
  2. It applies validation rules from your config.

To avoid ambiguity in this document:

  • a message is parseable if its structure can be parsed
  • a message passes validation if the parsed fields satisfy the configured rules

A commit message can be parseable and still fail validation.

See PARSING.md for commit message grammar and parse errors.

See VALIDATION.md for available fields, rule types, presets, and configuration examples.

Configuration

When -c/--config is not provided, configuration is auto-discovered in this order: conventional-commits.yaml, conventional-commits.yml, conventional-commits.toml, conventional-commits.json.

Presets

  • default - formatting rules for description spacing and newline handling in body/footer values
  • strict - default plus header length limits and common type/scope restrictions

Use -p/--preset to select a built-in preset from the command line without changing your config file.

Example:

preset: strict

type:
  values:
    - feat
    - fix
    - docs

scope:
  required: true
  values:
    - api
    - core
    - ui

header:
  max-line-length: 50

YAML example:

message:
  max-line-length: 72

type:
  values:
    - feat
    - fix
    - docs

scope:
  required: true
  values:
    - api
    - core
    - ui

header:
  max-line-length: 50

TOML example:

[type]
values = ["feat", "fix", "docs"]

[scope]
required = true
values = ["api", "core", "ui"]

[header]
max-line-length = 50

Building from Source

cargo build --release

The binary will be at ./target/release/ccval.