diff --git a/docs/assets/extra.css b/docs/assets/extra.css index a5fdfc1..7b2b300 100644 --- a/docs/assets/extra.css +++ b/docs/assets/extra.css @@ -48,6 +48,15 @@ vertical-align: top; } +/* In the rule index tables only, keep rule names and CLI flags on one line so + the tables stay scannable — without this, names like `subject-capitalized` + break across two lines even on a wide screen. Other tables hold long regexes + and allow-lists that must stay wrappable, hence the wrapper class rather + than a rule for every table. */ +.md-typeset .rules-index td code { + white-space: nowrap; +} + /* Sidebar section headings ("Getting started", "Reference", ...) */ .md-nav__item--section > .md-nav__link { font-weight: 700; diff --git a/docs/configuration.md b/docs/configuration.md index d67a25d..fe891bf 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,195 +1,173 @@ # Configuration -`commit-check` reads its settings from four places, in this priority order -(highest to lowest): +Commit Check reads its settings from four places. When the same option is set +in more than one, the first one listed wins: -1. **Command-line arguments** (`--subject-imperative=true`) -2. **Environment variables** (`CCHK_SUBJECT_IMPERATIVE=true`) -3. **Configuration files** (`cchk.toml` or `commit-check.toml`) +1. **Command-line arguments** — `--subject-imperative=true` +2. **Environment variables** — `CCHK_SUBJECT_IMPERATIVE=true` +3. **A configuration file** — `cchk.toml` or `commit-check.toml` 4. **Built-in defaults** -This flexibility allows you to: +That ordering is what makes the layering useful: the file carries the policy +the project agreed on, the environment overrides it for a single CI job, and a +flag overrides both for a single run. -* Use configuration files for project-wide settings -* Override with environment variables in CI/CD pipelines -* Override specific settings via CLI for one-off checks -* Use without any configuration files (relies on defaults) +!!! tip "Defaults are not "nothing"" -## Configuration Files + Two different things decide whether a rule fires: whether you asked for + that check at all, and what the option defaults to. A check only runs when + its flag is passed — `--message` never evaluates branch rules — but once a + check is running, these apply with no configuration file present: -`commit-check` configuration files support the TOML format. See `cchk.toml` for an example configuration. + | Check | Enforced by default | + |---|---| + | `--message` | Conventional Commits ([CC001](rules.md#cc001)), subject lengths of 5–80 characters ([CC004](rules.md#cc004), [CC005](rules.md#cc005)), and an allow-list of ten commit types | + | `--branch` | Conventional Branch ([CC201](rules.md#cc201)) and an allow-list of twenty-one branch types | + | `--author-name` / `--author-email` | The built-in name and email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)) | -!!! tip "Default Behavior" + Off until you turn them on: subject capitalization, imperative mood, + required body and signoff, and rebase requirements. - * When no configuration file exists, commit-check uses sensible defaults with minimal restrictions. - * Enforced by default: the Conventional Commits format ([CC001](rules.md#cc001)), the Conventional Branch format ([CC201](rules.md#cc201)), the subject length limits of 5–80 characters ([CC004](rules.md#cc004), [CC005](rules.md#cc005)), and the author name and email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)). - * **Off** by default: subject capitalization, imperative mood, body and signoff requirements, rebase requirements, and every `allow_*` restriction. + The `allow_*` options are a mix, so read them individually rather than + assuming: `allow_commit_types` and `allow_branch_types` are allow-lists + that restrict from the start, while `allow_merge_commits`, + `allow_revert_commits`, `allow_empty_commits`, `allow_fixup_commits`, + `allow_wip_commits` and `allow_force_push` all default to permitting + everything. The *Default* column in the + [rules reference](rules.md#rule-index) is the full picture. - See [rules](rules.md) for the default state of every rule. +## Where the config file lives -commit-check can be configured via a `cchk.toml` or `commit-check.toml` file. +The file is TOML, and may be called `cchk.toml` or `commit-check.toml`. Commit +Check searches four locations and uses the first that exists: -The file should be placed in the root of your repository or in the `.github` folder. - -## Configuration File Locations - -commit-check searches for configuration files in the following order (first found is used): - -1. `cchk.toml` (root directory) -2. `commit-check.toml` (root directory) +1. `cchk.toml` +2. `commit-check.toml` 3. `.github/cchk.toml` 4. `.github/commit-check.toml` -!!! tip "GitHub Best Practice" - - Placing configuration files in the `.github` folder helps keep your repository root clean and follows GitHub conventions used by tools like Dependabot and Renovate. +Pass `--config` to point at one directly and skip the search: -!!! tip "IDE Autocompletion" +```console +$ commit-check --config path/to/cchk.toml --message +``` - commit-check's TOML schema is published on [SchemaStore](https://www.schemastore.org/), - so editors like VS Code (via [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)), - PyCharm, and IntelliJ provide autocompletion, validation, and documentation - tooltips for `cchk.toml` out of the box — no manual schema path configuration needed. +!!! tip "Why `.github/`" -## Organization-Level Configuration (inherit_from) + Putting the file in `.github/` keeps the repository root uncluttered and + matches where Dependabot and Renovate already keep theirs. Commit Check + treats both locations identically. -For organizations that want to share a common base configuration across many repositories, commit-check supports an `inherit_from` directive at the top level of your TOML config file. +!!! tip "Editor autocompletion" -**How it works:** + The TOML schema is published on [SchemaStore](https://www.schemastore.org/), + so VS Code (via + [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)), + PyCharm and IntelliJ offer completion, validation and inline documentation + for `cchk.toml` with nothing to configure. -1. The `inherit_from` value can be a `github:` shorthand, a local file path, or an HTTPS URL. -2. The parent (inherited) configuration is loaded first. -3. Local settings in the current config file **override** the parent values. -4. The `inherit_from` key itself is not passed to the validation engine. +## Inheriting a shared config -**Example — inherit from a GitHub repository (recommended):** +An organization can keep one base policy and have every repository build on it. +Point `inherit_from` at the shared file; the parent loads first, and anything +set locally overrides it. The key itself is never passed to the validation +engine. -```toml -# .github/cchk.toml +```toml title=".github/cchk.toml" inherit_from = "github:my-org/.github:cchk.toml" [commit] -subject_max_length = 72 # Overrides parent value +subject_max_length = 72 # overrides whatever the parent set ``` -**GitHub shorthand format:** - -* `github:owner/repo:path/to/cchk.toml` — uses `HEAD` (default branch) -* `github:owner/repo@main:path/to/cchk.toml` — pins to the `main` branch - -**Example — inherit from a local file:** +The value can be a GitHub shorthand, a local path or an HTTPS URL: -```toml -# repo/.github/cchk.toml -inherit_from = "../../shared/org-cchk.toml" +| Form | Example | +|---|---| +| GitHub, default branch | `github:owner/repo:path/to/cchk.toml` | +| GitHub, pinned branch | `github:owner/repo@main:path/to/cchk.toml` | +| Local file | `../../shared/org-cchk.toml` | +| HTTPS URL | `https://example.com/shared/cchk.toml` | -[commit] -allow_wip_commits = true # Override for this project only -``` +!!! note "Inheritance fails quietly" -**Example — inherit from an HTTPS URL:** - -```toml -# .github/cchk.toml -inherit_from = "https://example.com/shared/cchk.toml" -``` + If the target is unreachable or the format is not recognized, Commit Check + ignores the inheritance and uses the local configuration alone. Plain HTTP + URLs are rejected outright. A repository that silently stops inheriting + still passes its own checks, so pin the branch when the policy matters. -!!! note +## A worked example - If the `inherit_from` target is unreachable or the format is unrecognized, commit-check silently ignores the inheritance and uses only the local configuration. HTTP (non-TLS) URLs are rejected for security. +Every line below that differs from the built-in default is marked, so it is +clear what this file is actually changing: -## Example Configuration - -```toml +```toml title="cchk.toml" [commit] # https://www.conventionalcommits.org conventional_commits = true -# message_pattern = "" # Optional - custom regex (overrides conventional_commits) +# message_pattern = "" # optional: a custom regex, replacing the above subject_capitalized = false -subject_imperative = false -subject_max_length = 80 # Default - set to your own limit -subject_min_length = 5 # Default - set to your own minimum +subject_imperative = true # changed: off by default +subject_max_length = 80 +subject_min_length = 5 +# changed: a subset of the default list, which also has perf, build and ci allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore"] allow_merge_commits = true allow_revert_commits = true -allow_empty_commits = false +allow_empty_commits = false # changed: allowed by default allow_fixup_commits = true -allow_wip_commits = false +allow_wip_commits = false # changed: allowed by default require_body = false -# ignore_authors = [] # Optional - bypass checks for these commit/co-authors require_signed_off_by = false -ai_attribution = "forbid" # "ignore" (default) or "forbid" — rejects AI tool trailers +ai_attribution = "forbid" # changed: "ignore" by default +# ignore_authors = [] # optional: bypass all commit checks for these authors [push] -# Block force pushes when used as a pre-push hook or with --no-force-push -allow_force_push = true # Set to false to block force pushes +allow_force_push = true # set false to block force pushes [branch] # https://conventionalbranch.org conventional_branch = true -# Optional: defaults are a superset of the Conventional Branch spec — the -# spec types plus Conventional Commit types, AI agent prefixes and bot -# prefixes (see the Options table below for the full list). Omit this -# option to use the defaults, or set your own list for a strict subset. -allow_branch_types = [ - "feature", - "bugfix", - "hotfix", - "release", - "chore", - "feat", - "fix", - "build", - "ci", - "docs", - "perf", - "refactor", - "style", - "test", -] -# allow_branch_names = [] # Optional - additional standalone branch names (e.g., ["develop", "staging"]) -# require_rebase_target = "main" # Optional - no rebase requirement by default -# ignore_authors = [] # Optional - no authors ignored by default +# changed: spec types only. The default is a superset — these plus the +# Conventional Commit types, AI agent prefixes and bot prefixes — so setting +# this at all narrows it. Omit the line to accept all of them. +allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore"] +# allow_branch_names = [] # optional: extra standalone names, e.g. ["develop"] +# require_rebase_target = "main" # optional: no rebase requirement by default +# ignore_authors = [] # optional: as above, for branch checks ``` -## Command-Line Arguments - -All configuration options can be specified via command-line arguments, which take precedence over environment variables and configuration files. - -**Syntax:** +!!! warning "`allow_*` options describe what is permitted" -* Boolean options: `--option-name=true` or `--option-name=false` -* Integer options: `--option-name=80` -* List options: `--option-name=value1,value2,value3` (comma-separated) -* String options: `--option-name=value` + They read backwards from most linters. `allow_wip_commits = false` is the + setting that *rejects* WIP commits; leaving it at its default of `true` + lets them through. -**Examples:** +## Command-line arguments -```bash -# Disable imperative mood check -commit-check --message --subject-imperative=false +Every option can be set as a flag, which is what makes a TOML file optional +entirely — useful when the policy lives in `.pre-commit-config.yaml` instead. -# Set custom subject length limit -commit-check --message --subject-max-length=72 +| Type | Form | +|---|---| +| Boolean | `--option-name=true` / `--option-name=false` | +| Integer | `--option-name=80` | +| List | `--option-name=value1,value2,value3` | +| String | `--option-name=value` | -# Restrict allowed commit types -commit-check --message --allow-commit-types=feat,fix,docs - -# Combine multiple options -commit-check --message --subject-imperative=true --subject-max-length=50 --allow-commit-types=feat,fix - -# Branch configuration via CLI -commit-check --branch --allow-branch-types=feature,bugfix,hotfix +```console +$ commit-check --message --subject-imperative=false +$ commit-check --message --subject-max-length=72 +$ commit-check --message --allow-commit-types=feat,fix,docs +$ commit-check --branch --allow-branch-types=feature,bugfix,hotfix ``` -**Pre-commit Hook Usage:** +Used from a hook definition, with no config file anywhere in the repository: -The primary use case for CLI arguments is configuring commit-check in `.pre-commit-config.yaml` without requiring a TOML file: - -```yaml +```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message args: @@ -198,39 +176,19 @@ repos: - --allow-merge-commits=false ``` -## Environment Variables - -Configuration can also be set via environment variables with the `CCHK_` prefix. This is useful for CI/CD pipelines and temporary overrides. - -**Naming Convention:** - -* Convert option name to uppercase -* Replace hyphens with underscores -* Add `CCHK_` prefix - -**Examples:** +## Environment variables -```bash -# Set boolean options -export CCHK_SUBJECT_IMPERATIVE=true -export CCHK_SUBJECT_CAPITALIZED=false +Any option can also be set through the environment, which is the practical way +to vary policy per CI job without editing the file. Uppercase the option name, +replace hyphens with underscores, and prefix `CCHK_`: -# Set integer options -export CCHK_SUBJECT_MAX_LENGTH=72 -export CCHK_SUBJECT_MIN_LENGTH=10 - -# Set list options (comma-separated) -export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore -export CCHK_ALLOW_BRANCH_TYPES=feature,bugfix,hotfix - -# Set string options -export CCHK_REQUIRE_REBASE_TARGET=main - -# Use in CI/CD -CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message +```console +$ export CCHK_SUBJECT_MAX_LENGTH=72 +$ export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore +$ CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message ``` -**Complete Mapping:** +The full mapping between the three forms: | TOML Config | Environment Variable | CLI Argument | |---|---|---| @@ -259,29 +217,41 @@ CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message | `ai_attribution = "forbid"` | `CCHK_AI_ATTRIBUTION=forbid` | `--ai-attribution=forbid` | | `ignore_authors = ["bot"]` (in branch section) | `CCHK_BRANCH_IGNORE_AUTHORS=bot,user` | `--branch-ignore-authors=bot,user` | -## Configuration Priority Example - -When the same option is specified in multiple places, the priority determines which value is used: +## Which value wins -```bash -# In cchk.toml: -# subject_max_length = 100 +The four sources layer, so the same option can be set in several at once. Only +the highest-priority one takes effect: -# Set via environment: -export CCHK_SUBJECT_MAX_LENGTH=80 +```console +$ grep subject_max_length cchk.toml +subject_max_length = 100 -# Override via CLI: -commit-check --message --subject-max-length=50 +$ export CCHK_SUBJECT_MAX_LENGTH=80 -# Result: subject_max_length = 50 (CLI wins) +$ commit-check --message --subject-max-length=50 ``` -## Options Table Description +The limit applied is 50 — the flag beats the environment, which beats the file. +Nothing warns about the values that lost, which is worth remembering when a +setting in the file appears to have no effect. + +## Every option + +Types are as TOML understands them. A default shown as `""` means the option is +unset, which is never the same as the check being off — but it does not mean +the same thing twice, so read the description rather than the cell: + +- `message_pattern` unset leaves `conventional_commits` to generate the + pattern. [CC001](rules.md#cc001) still runs. +- `author_name_pattern` unset falls back to the built-in name pattern. + [CC101](rules.md#cc101) still runs. +- `require_rebase_target` unset is the one case where the check really does not + run — there is no branch to compare against. | Section | Option | Type | Default | Description | |---|---|---|---|---| | commit | conventional_commits | bool | true | Enforce Conventional Commits specification. | -| commit | message_pattern | str | "" (disabled) | Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., `"^PROJ-\\d+: .+"`). When `message_pattern` is set (non-empty) it takes precedence over `conventional_commits`. | +| commit | message_pattern | str | "" (no custom pattern) | Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., `"^PROJ-\\d+: .+"`). When `message_pattern` is set (non-empty) it takes precedence over `conventional_commits`. | | commit | subject_capitalized | bool | false | Subject must start with a capital letter. | | commit | subject_imperative | bool | false | Subject must be in imperative mood. Forms of verbs can be found at [imperatives.py](https://github.com/commit-check/commit-check/blob/main/commit_check/imperatives.py) | | commit | subject_max_length | int | 80 | Maximum length of the subject line. | diff --git a/docs/example.md b/docs/example.md index 1c38170..41a412b 100644 --- a/docs/example.md +++ b/docs/example.md @@ -104,7 +104,7 @@ pushed: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-no-force-push stages: [pre-push] diff --git a/docs/guides/integrations.md b/docs/guides/integrations.md index e108a11..f15a237 100644 --- a/docs/guides/integrations.md +++ b/docs/guides/integrations.md @@ -23,7 +23,7 @@ Add Commit Check to `.pre-commit-config.yaml`: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message - id: check-branch @@ -70,7 +70,7 @@ Options can be passed as hook arguments, which keeps everything in one file: ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message args: diff --git a/docs/index.md b/docs/index.md index fa4d36e..60710d0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -36,7 +36,7 @@ whatever your AI agent is committing on your behalf. ```yaml title=".pre-commit-config.yaml" repos: - repo: https://github.com/commit-check/commit-check - rev: v2.13.0 + rev: v2.13.1 hooks: - id: check-message - id: check-branch @@ -270,75 +270,75 @@ graph TB
- Apache + Apache Apache
- Discovery Unicamp + Discovery Unicamp Discovery Unicamp
- Texas Instruments + Texas Instruments Texas Instruments
- OpenCADC + OpenCADC OpenCADC
- Extrawest + Extrawest Extrawest
- Chainlift + Chainlift Chainlift
- Mila + Mila Mila
- RLinf + RLinf RLinf
- Istio Ecosystem + Istio Ecosystem Istio Ecosystem
- Juniper Networks + Juniper Networks Juniper Networks
- French National Parks + French National Parks French National Parks
- OpenDriveLab + OpenDriveLab OpenDriveLab
- UT Austin RobIn + UT Austin RobIn UT Austin RobIn
- WorldArena2 + WorldArena2 WorldArena2
- moniqo + moniqo moniqo
- elu mobility + elu mobility elu mobility
- Open Energy Platform + Open Energy Platform Open Energy Platform
- Collective + Collective Collective
diff --git a/docs/migration.md b/docs/migration.md index 45d57ff..9d23ac3 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -1,53 +1,22 @@ -# Migration Guide +# Migrating from v1 -This guide helps you migrate from commit-check v1.x (YAML configuration) to v2.0+ (TOML configuration). +Version 2.0 replaced the YAML configuration with TOML. The change is mechanical +— nothing about what Commit Check validates went away — but the two formats +express policy very differently, and there is no automatic conversion. -## Overview +In v1 a config file was a list of checks, each carrying its own regex, error +message and suggestion. You wrote the pattern; Commit Check ran it. In v2 the +patterns are built in and the file selects and tunes them by name. A v1 file +was mostly regex; a v2 file is mostly booleans. -Version 2.0 introduces significant changes to commit-check: +The practical consequence: you do not translate a v1 file line by line. You +decide which rules you want and write those down, which is usually far shorter. -* **Configuration format**: `.commit-check.yml` → `cchk.toml` or `commit-check.toml` -* **Simplified architecture**: New validation engine with cleaner design -* **Enhanced functionality**: Better error messages and more flexible configuration options +## Converting the file -## Quick Migration Steps +Take a representative v1 config: -1. **Backup your existing configuration**: - -```bash -cp .commit-check.yml .commit-check.yml.backup -``` - -2. **Create new TOML configuration**: - -```bash -touch cchk.toml # or commit-check.toml -``` - -3. **Convert YAML to TOML format** (see examples below) - -4. **Test the new configuration**: - -```bash -commit-check --help -commit-check --message --branch --author-name --author-email --dry-run -``` - -5. **Remove old YAML file**: - -```bash -rm .commit-check.yml.backup # after confirming everything works -``` - -## Configuration Format Changes - -The configuration structure has changed from YAML to TOML format - -### YAML (v1.x) vs TOML (v2.0+) - -**Old YAML format** (`.commit-check.yml`): - -```yaml +```yaml title=".commit-check.yml (v1)" checks: - check: message regex: '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)' @@ -64,7 +33,7 @@ checks: suggest: run command `git checkout -b type/branch_name` - check: author_name - regex: ^[A-Za-zÀ-ÖØ-öø-ÿ\u0100-\u017F\u0180-\u024F ,.\'-]+$|.*(\[bot]) + regex: ^[A-Za-zÀ-ÖØ-öø-ÿĀ-ſƀ-ɏ ,.\'-]+$|.*(\[bot]) error: The committer name seems invalid suggest: run command `git config user.name "Your Name"` @@ -82,108 +51,141 @@ checks: regex: main # it can be master, develop, devel etc based on your project. error: Current branch is not rebased onto target branch suggest: Please ensure your branch is rebased with the target branch - -- check: imperative - regex: '' # Not used for imperative mood check - error: 'Commit message should use imperative mood (e.g., "Add feature" not "Added feature")' - suggest: 'Use imperative mood in commit message like "Add", "Fix", "Update", "Remove"' ``` -**New TOML format** (`cchk.toml` or `commit-check.toml`): +Every one of those checks still exists. Named rather than spelled out, the +whole file becomes: -```toml +```toml title="cchk.toml (v2)" [commit] -# https://www.conventionalcommits.org conventional_commits = true -subject_capitalized = false -subject_imperative = true -subject_max_length = 80 -subject_min_length = 5 -allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore", "ci"] -allow_merge_commits = true -allow_revert_commits = true -allow_empty_commits = false -allow_fixup_commits = true -allow_wip_commits = false -require_body = false -require_signed_off_by = false -ignore_authors = ["dependabot[bot]", "copilot[bot]"] +require_signed_off_by = true [branch] -# https://conventionalbranch.org conventional_branch = true -allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix"] require_rebase_target = "main" ``` -### CLI Changes +The regexes are gone because they were restating the built-in behaviour. So are +`error` and `suggest`: Commit Check now supplies both, along with a stable rule +ID and a link to the rule's documentation. -The command-line interface has been simplified: +!!! warning "Check your type lists before deleting the old file" -**Old CLI** (v1.x): + "Restating the built-in behaviour" is nearly true, not exactly true, and + the gap is silent — the shorter file above accepts slightly less than the + v1 one it replaces. Two types in that v1 example are missing from the v2 + defaults: -```bash -commit-check --config .commit-check.yml -``` + - `revert` as a commit type. `revert: drop the cache` passed under v1 and + is rejected by the default `allow_commit_types`. (Unrelated to + `allow_revert_commits`, which governs Git's own `Revert "..."` commits + and is on by default.) + - `task` as a branch type. `task/CC-42` passed under v1 and is rejected by + the default `allow_branch_types`. -**New CLI** (v2.0+): + Keep them by naming the list you want, remembering that setting either + option replaces the default rather than adding to it: -```bash -commit-check --config cchk.toml # or commit-check.toml -# Or use defaults (no config file needed) -commit-check --message --branch -``` + ```toml + [commit] + allow_commit_types = ["build", "chore", "ci", "docs", "feat", "fix", + "perf", "refactor", "revert", "style", "test"] + + [branch] + allow_branch_types = ["bugfix", "chore", "feature", "hotfix", "release", "task"] + ``` + + Compare your own v1 regex against + [every option](configuration.md#every-option) before deleting it — this is + the one part of the migration that fails quietly, months later, on a commit + that used to be fine. + +### What each v1 check became + +| v1 `check:` | v2 option | Rule | +|---|---|---| +| `message` | `[commit] conventional_commits` | [CC001](rules.md#cc001) | +| `branch` | `[branch] conventional_branch` | [CC201](rules.md#cc201) | +| `author_name` | `[commit] author_name_pattern` | [CC101](rules.md#cc101) | +| `author_email` | `[commit] author_email_pattern` | [CC102](rules.md#cc102) | +| `commit_signoff` | `[commit] require_signed_off_by` | [CC012](rules.md#cc012) | +| `merge_base` | `[branch] require_rebase_target` | [CC202](rules.md#cc202) | +| `imperative` | `[commit] subject_imperative` | [CC003](rules.md#cc003) | -### Custom Regex (`message_pattern`) +The two `*_pattern` options are the exception to "no more regexes": they exist +so an author policy stricter than the default stays expressible. -If you relied on the custom `regex` field in v1.x to enforce a non-Conventional-Commits -format (e.g. JIRA smart commits `PROJ-123: description`), use the `message_pattern` -option in the `[commit]` section: +### Keeping a custom message format -```toml +If the v1 `regex` enforced something that is not Conventional Commits — JIRA +smart commits, say — that is what `message_pattern` is for. Set it and it +replaces the generated Conventional Commits pattern entirely: + +```toml title="cchk.toml" [commit] message_pattern = "^PROJ-\\d+: .+" ``` -When `message_pattern` is set (non-empty), it replaces the auto-generated Conventional -Commits regex entirely, giving you full control over the accepted message format. +## Converting the command line -## Troubleshooting +Only the config flag changed, and only because the file did: -### Common Issues +```console +$ commit-check --config .commit-check.yml # v1 +$ commit-check --config cchk.toml # v2 +``` -**Issue**: "Configuration file not found" +A config file is now optional. With none, the defaults apply immediately: -**Solution**: Ensure your file is named `cchk.toml` or `commit-check.toml` and placed in the repository root or in the `.github` folder. +```console +$ commit-check --message --branch +``` -**Issue**: "Invalid TOML syntax" +## Doing the migration -**Solution**: Use a TOML validator or check the syntax. Common issues include: +1. Keep the old file until you are done: -* Missing quotes around strings -* Incorrect boolean values (use `true`/`false`, not `True`/`False`) -* Invalid array syntax + ```console + $ cp .commit-check.yml .commit-check.yml.backup + ``` -**Issue**: "Validation rules not working as expected" +2. Write `cchk.toml` — in the repository root or in `.github/`. Use the table + above rather than translating regexes. -**Solution**: Check the [Configuration Documentation](configuration.md) for the correct option names and formats. +3. Check it against real commits without failing anything, which is what + `--dry-run` is for: -### Validation and Testing + ```console + $ commit-check --message --branch --author-name --author-email --dry-run + ``` -After migration, test your configuration: +4. Try a message that should fail, so you know the policy is doing something: -```bash -# Test commit message validation -echo "feat: test commit message" | commit-check --message + ```console + $ echo "nonsense" | commit-check --message + ``` -# Test branch validation -commit-check --branch + A passing run on a message that ought to fail usually means the config file + was not found — see + [where the config file lives](configuration.md#where-the-config-file-lives). -# Test with dry-run flag -commit-check --message --branch --author-name --author-email --dry-run -``` +5. Delete `.commit-check.yml` and the backup. + +## If something does not work + +**The config file is not found.** It has to be named `cchk.toml` or +`commit-check.toml`, in the repository root or `.github/`. Any other name needs +`--config`. + +**TOML fails to parse.** Usually unquoted strings, `True` instead of `true`, or +a trailing comma in an array. Editors validate the file against the published +schema — see +[where the config file lives](configuration.md#where-the-config-file-lives). -## Getting Help +**A rule does not behave as expected.** Check the option name and its default in +[every option](configuration.md#every-option); the `allow_*` options in +particular describe what is *permitted*, so `false` is the strict setting. -* **Documentation**: Check the [Configuration Guide](configuration.md) -* **Issues**: Report problems on [GitHub Issues](https://github.com/commit-check/commit-check/issues) +**Something else.** [Open an issue](https://github.com/commit-check/commit-check/issues) +— include the output of `commit-check --message --format json`. diff --git a/docs/rules.md b/docs/rules.md index cf19ec4..ddafe1e 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -62,6 +62,8 @@ actively harmful for one that does not. Run with `-m` / `--message`. +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC001](#cc001) | `message` | The commit message should follow Conventional Commits | `-m` | ✅ On | @@ -78,28 +80,42 @@ Run with `-m` / `--message`. | [CC012](#cc012) | `require-signed-off-by` | Signed-off-by not found in latest commit | `-m` | ⚪ Off | | [CC013](#cc013) | `ai-attribution` | AI attribution policy violation | `-m` | ⚪ Off | +
+ ### Author rules (`CC1xx`) { #author-rules } +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC101](#cc101) | `author-name` | The committer name seems invalid | `-n` | ✅ On | | [CC102](#cc102) | `author-email` | The committer's email seems invalid | `-e` | ✅ On | +
+ ### Branch rules (`CC2xx`) { #branch-rules } Run with `-b` / `--branch`. +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC201](#cc201) | `branch` | The branch should follow Conventional Branch | `-b` | ✅ On | | [CC202](#cc202) | `merge-base` | Current branch is not rebased onto target branch | `-b` | ⚪ Off | +
+ ### Push rules (`CC3xx`) { #push-rules } +
+ | Code | Name | Message | Check | Default | |---|---|---|---|---| | [CC301](#cc301) | `no-force-push` | Force push is not allowed | `--no-force-push` | ⚪ Off | +
+ ## Commit message rules ### message (CC001) { #cc001 } diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index fcabb68..bee1857 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -185,6 +185,22 @@ th { } } +/* Both tinted bands above bleed past the content column with a negative + margin. Material sets the root font to 125%, so -1.5rem is 30px — but below + its 76.25em breakpoint the column only has a 16px gutter to give back. The + bands hung 14px off the right edge, which made the whole page scroll + sideways on every phone and tablet. Match the gutter there instead. + + (76.25em is Material's own breakpoint. Media-query em units resolve against + the initial 16px font size rather than the 125% root, so this is 1220px.) */ +@media screen and (max-width: 76.25em) { + .trusted-by, + .community-section { + margin-left: -0.8rem; + margin-right: -0.8rem; + } +} + /* Mobile navigation drawer ------------------------------------------------- Material leaves this at its own default indigo, which clashes with the header right above it. */ diff --git a/docs/troubleshoot.md b/docs/troubleshoot.md index 6eacc8d..5143083 100644 --- a/docs/troubleshoot.md +++ b/docs/troubleshoot.md @@ -1,8 +1,14 @@ # Troubleshooting -## How to Skip Author Name Check +## A check fails and you need the commit through anyway -In some cases, Commit Check may fail due to an invalid `author_name`, as shown below: +Every check can be bypassed. Doing so is sometimes the right call — you are +mid-rebase, or the rule is wrong and fixing it properly can wait — but the +bypass is the second thing to reach for. The first is reading what failed: +every diagnostic names the rule, quotes the value that failed it, and links to +the page explaining it. + +Take an author name check that rejects a one-character name: ```shell check committer name.....................................................Failed @@ -17,25 +23,90 @@ Suggest: git config user.name 'Your Name' Docs: https://commit-check.com/rules/#cc101 ``` -To fix it, you can either update your Git config or temporarily skip the check using one of the following methods. +`12` is the value Git actually recorded as the author — usually a sign that +`user.name` was never set on this machine, or was set by a script. The fix is +the one the `Suggest:` line gives: + +```shell +git config user.name "Your Name" +git commit --amend --reset-author --no-edit +``` + +### Skipping one hook + +When the check is genuinely wrong for a single commit, skip that hook by ID and +leave the rest running. `SKIP` is a +[pre-commit](https://pre-commit.com/#temporarily-disabling-hooks) feature, so it +takes the hook's `id`, not the rule ID: + +```shell +SKIP=check-author-name git commit --amend --no-edit +``` + +The IDs are `check-message`, `check-branch`, `check-author-name`, +`check-author-email` and `check-no-force-push`. + +### Skipping every hook + +`--no-verify` bypasses the whole pre-commit run — Commit Check and everything +else you have configured: + +```shell +git commit --amend --no-edit --no-verify +``` + +!!! warning "A local bypass is not a CI bypass" + + `SKIP` and `--no-verify` only affect the hooks on your machine. If the same + policy runs in CI — through the + [GitHub Action](guides/integrations.md#in-github-actions), say — it will + check the commit again when you push, and reject it there. To exempt a + commit everywhere, change the policy rather than the invocation: turn the + rule off in `cchk.toml`, or add the author to + [`ignore_authors`](configuration.md#every-option) if the exemption is + permanent. + +## A rule fires that you never turned on -### Bypass All Hooks +Commit Check is not silent by default. Whichever checks you asked for run with +their defaults already applied, even with no config file present: `--message` +enforces Conventional Commits, the 5–80 character subject limits and an +allow-list of ten commit types; `--branch` enforces Conventional Branch and an +allow-list of twenty-one branch types; `--author-name` and `--author-email` +apply the built-in patterns. The *Default* column in the +[rules reference](rules.md#rule-index) shows every rule's starting state, and +the [configuration page](configuration.md) spells the split out. -Use the `--no-verify` flag to skip the pre-commit hook: +To see what a given repository is actually enforcing, check which config file +it picked up — the search order is in +[where the config file lives](configuration.md#where-the-config-file-lives), and +`--config` overrides it: ```shell -# Amend the commit without running hooks -git commit --amend --author="Xianpeng Shen " --no-edit --no-verify +commit-check --config cchk.toml --message ``` -### Bypass A Specific Hook +## Nothing is checked at all -Alternatively, use the `SKIP=your-hook-name` environment variable, like below: +A check only runs when its own flag is passed. `commit-check --message` never +evaluates branch rules, and `commit-check --branch` never reads the commit +message — so a hook wired up with the wrong flag passes silently, forever. ```shell -# Set the correct Git author name -git config user.name "Xianpeng Shen" +commit-check --message --branch --author-name --author-email +``` + +Each rule in the [rules reference](rules.md) lists the flag that activates it. + +## Something else -# Force amend while skipping the specified hook -SKIP=check-author-name git commit --amend --author="Xianpeng Shen " --no-edit +If the failure does not match anything above, the JSON output shows exactly +what was evaluated and why, which is usually enough to see where the config +disagrees with the expectation: + +```shell +commit-check --message --format json ``` + +Failing that, [open an issue](https://github.com/commit-check/commit-check/issues) +with that output attached. diff --git a/tests/docs_sync_test.py b/tests/docs_sync_test.py index 80d174d..4eac633 100644 --- a/tests/docs_sync_test.py +++ b/tests/docs_sync_test.py @@ -14,6 +14,7 @@ from __future__ import annotations import re +from importlib.metadata import version from pathlib import Path from typing import Any @@ -93,6 +94,39 @@ def test_every_rule_explains_itself(self): ) +#: A pre-commit revision pin, e.g. ``rev: v2.13.1``. +_REV_PIN = re.compile(r"^\s*rev:\s*v(\d+\.\d+\.\d+)\s*$", re.M) + + +class TestDocumentedRevisions: + """The revisions the install snippets pin are the released version. + + Copy-pasteable snippets are the most-used thing on the site, and a pin is + invisible once it goes stale: the snippet keeps working, it just installs + an older release than the page around it describes. Nothing else here + reads these — the other guards compare reference tables against the + package — so a release would leave five pages pinned to the version + before it. + """ + + def test_pinned_revisions_match_the_released_version(self): + """Every ``rev:`` outside the blog names the installed version.""" + installed = version("commit-check") + stale = [] + for page in DOCS.rglob("*.md"): + # Blog posts are dated: they record what was current when they + # were written, and moving their pins forward would falsify them. + if "blog" in page.relative_to(DOCS).parts: + continue + for pinned in _REV_PIN.findall(page.read_text("utf-8")): + if pinned != installed: + stale.append( + f"{page.relative_to(DOCS)}: pins v{pinned}, " + f"the released version is {installed}" + ) + assert not stale, "install snippets are out of date:\n " + "\n ".join(stale) + + _OPTIONS_ROW = re.compile( r"^\|\s*(commit|branch|push)\s*" # section r"\|\s*(\w+)\s*" # option name