ccval 0.6.0

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

Conventional Commits Validator

CI Release Docker

Validate commit messages locally, in CI, or from stdin using Conventional Commits rules.

ccval helps you:

  • catch invalid commit messages before merge or release
  • enforce team rules for types, scopes, headers, and footers
  • validate one commit, a branch range, or a message from stdin or a file

Quickstart

Once ccval is installed, validate the last commit in the current repository:

ccval

Validate all commits on your branch:

ccval -- origin/main..HEAD

Use the built-in strict preset:

ccval -p strict

Validate a commit message from stdin:

printf 'feat: add validation\n' | ccval --stdin

Install

Choose the option that fits your workflow.

crates.io

cargo install ccval

Homebrew

brew install andrey-fomin/tap/ccval

GitHub Releases

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

Docker

Run ccval without installing it locally:

printf 'feat: add validation\n' | docker run --rm -i andreyfomin/ccval:distroless --stdin

Available image tags (moving convenience tags):

  • andreyfomin/ccval:latest - Alpine-based, includes Git support, about 11 MB

  • andreyfomin/ccval:distroless - minimal image, no Git support, about 1 MB

  • Use :distroless when you only need stdin or file validation

  • Use :latest when you need to validate commits from a Git repository

The release workflow also publishes versioned tags, including :1, :1.2, :1.2.3, and matching -distroless variants such as :1.2.3-distroless.

For Git-based validation in a mounted repository, use --trust-repo only when you control the repository and Git fails with a detected dubious ownership warning:

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

macOS note

If macOS blocks a downloaded binary, remove the quarantine attribute:

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

You can also right-click the binary, choose Open, and confirm the prompt.

Common Tasks

Validate the latest commit

ccval

Validate a commit range

ccval -- origin/main..HEAD

Validate commits in another repository

ccval -r /path/to/repo -- HEAD~10..HEAD

--repository changes where Git reads commits from. Config auto-discovery still happens in the current working directory, so use --config too when the target repository has its own config file:

ccval -r /path/to/repo -c /path/to/repo/conventional-commits.yaml -- HEAD~10..HEAD

Validate a message file

ccval --file .git/COMMIT_EDITMSG

Validate in a container or ownership-mismatch environment

ccval -T

Or with an explicit repository path:

ccval -r /repo -T -- HEAD~5..HEAD

Git Hook

Use a commit-msg hook to validate each commit message before Git creates the commit.

Create .git/hooks/commit-msg with this content:

#!/bin/sh

set -eu

exec ccval --file "$1"

Then make it executable:

chmod +x .git/hooks/commit-msg

This hook expects ccval to be installed and available on your PATH.

Configuration in 30 Seconds

By default, if no config file is found and no preset is specified, ccval only checks whether the commit message is parseable as a Conventional Commit.

When a config file is present or a preset is provided, ccval also applies the validation rules defined there so you can enforce team-specific rules.

To enforce team-specific rules, add a config file such as conventional-commits.yaml.

Minimal example:

preset: strict

type:
  values:
    - feat
    - fix
    - docs

scope:
  required: true

The same idea in TOML:

preset = "strict"

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

[scope]
required = true

With this config:

  • strict enables useful default formatting rules
  • only feat, fix, and docs are allowed
  • every commit must include a scope such as feat(api): add endpoint

When --config is not provided, ccval looks for these files in order:

  • conventional-commits.yaml
  • conventional-commits.yml
  • conventional-commits.toml
  • conventional-commits.json

Use a custom config path when needed:

ccval -c .github/conventional-commits.yaml -- origin/main..HEAD

Presets

ccval includes two built-in presets:

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

When a config file and preset are both used:

  • -p/--preset takes precedence over preset: in the config file
  • rules omitted in your config inherit from the preset
  • regexes: [] clears preset regex rules for that field

Use a preset from the command line without changing your config file:

ccval -p strict

GitHub Action

Use ccval in GitHub Actions to validate pull request commit ranges or pushed commits.

on: pull_request

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

Common options:

Use a custom config:

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

Use a 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

Note: git-args is parsed as a whitespace-separated list of arguments by the action; shell-style quoting/escaping is not supported, and arguments that contain spaces cannot be passed as a single argument. Auto-detected behavior such as adding --no-merges applies only when git-args is not set.

Limit checked commits:

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

The action supports push and pull_request events, discovers conventional-commits.yaml or .github/conventional-commits.yaml, skips merge commits in its auto-detected ranges, skips deleted-ref and zero-commit pushes, and limits validation to 100 commits by default.

For push events, it prefers the exact pushed range from local history. If the pushed before commit is not available locally, it falls back to the default-branch merge-base when possible and otherwise fails with a clear error.

If either the push event itself or the selected commit range exceeds max-commits, the action skips validation, emits a warning, and exits successfully.

Make sure your workflow fetches enough history, for example with fetch-depth: 0, so the required commit range is available.

Use @v0 to track the latest compatible v0.x.y release, or pin a specific release tag such as @v0.3.1. For a fully immutable reference, pin a commit SHA.

Parsing vs Validation

ccval checks commit messages in two steps:

  1. Parse the message structure
  2. Apply validation rules from your config

A message can be parseable and still fail validation.

For example, feat: add api may parse successfully but fail stricter formatting rules.

Read more:

  • PARSING.md for commit message structure and parse errors
  • VALIDATION.md for available fields, rules, and configuration examples

Command Reference

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

  • 0 success
  • 64 usage error
  • 65 content invalid
  • 66 input unavailable
  • 70 internal error
  • 74 I/O error
  • 77 permission error
  • 78 config error

Reference