Skip to content

feat: configurable commit formats beyond conventional commits #247

@BryanFRD

Description

@BryanFRD

Problem

FerrFlow currently only recognizes strict conventional commits (feat:, fix:, refactor:, etc.) when determining version bumps. The regex patterns are hardcoded in conventional_commits.rs and don't account for alternative commit conventions that teams might use, such as:

  • Branch-style prefixes: Feat/, Fix/, Refactor/ (capitalized, with slash)
  • Capitalized conventional: Feat:, Fix:
  • Other separators or custom patterns

Teams migrating to FerrFlow or working in mixed environments can't use it without rewriting their entire commit history or enforcing a strict format upfront.

Proposed solution

Add a commit_formats (or similar) configuration option in ferrflow.json that lets users define which patterns map to which bump types.

Glob patterns

Each bump level accepts glob patterns to match against the full commit message. This keeps the syntax familiar (FerrFlow already uses globs for branch matching):

{
  "commit_formats": {
    "minor": ["feat:*", "feat(*):*", "Feat:*", "Feat(*):*", "Feat/*", "feature:*", "feature(*):*"],
    "patch": ["fix:*", "fix(*):*", "Fix:*", "Fix(*):*", "Fix/*", "perf:*", "perf(*):*", "refactor:*", "refactor(*):*", "Refactor:*", "Refactor(*):*", "Refactor/*"],
    "major": ["BREAKING CHANGE*", "*!:*"],
    "case_sensitive": true
  }
}

With case_sensitive: true (the default), patterns match exactly as written. The default config lists both feat:* and Feat:* explicitly to cover common variants.

This matches commits like:

  • feat: add login → minor
  • feat(auth): add login → minor
  • Feat: add login → minor
  • Feat/add-login → minor
  • feature: add login → minor
  • Fix: resolve crash → patch
  • Fix/resolve-crash → patch
  • fix(db): connection leak → patch
  • refactor: split module → patch
  • Refactor/cleanup → patch
  • feat!: remove old API → major

Each value can be:

  • A string — single pattern: "minor": "feat:*"
  • A list — multiple patterns: "minor": ["feat:*", "Feat/*"]
  • "all" — catch-all, matches any commit: "minor": "all"

Grouping and all option

Use "all" to treat every commit as a bump trigger — useful for projects that don't follow conventional commits at all:

{
  "commit_formats": {
    "minor": "all"
  }
}

More targeted grouping with a catch-all baseline:

{
  "commit_formats": {
    "patch": "all",
    "minor": ["feat:*", "feat(*):*", "Feat/*"],
    "major": ["BREAKING CHANGE*", "*!:*"]
  }
}

Here, "patch": "all" is the baseline — every commit triggers at least a patch bump, but commits matching minor or major patterns get bumped higher. Priority: major > minor > patch > none.

Case sensitivity

case_sensitive defaults to true — patterns match exactly as written. Set to false for case insensitive matching, which simplifies the config since you don't need to list each casing variant:

{
  "commit_formats": {
    "minor": ["feat:*", "feat(*):*", "feat/*", "feature:*", "feature(*):*"],
    "patch": ["fix:*", "fix(*):*", "fix/*", "perf:*", "perf(*):*", "refactor:*", "refactor(*):*", "refactor/*"],
    "major": ["BREAKING CHANGE*", "*!:*"],
    "case_sensitive": false
  }
}

With false, feat:* matches feat:, Feat:, FEAT:, etc.

Default behavior

The default config is permissive — it includes capitalized variants and slash separators out of the box. This is a breaking change: commits like Feat/something or Fix: something that were previously ignored will now trigger version bumps.

Users who want strict conventional commits only can trim the patterns:

{
  "commit_formats": {
    "minor": ["feat:*", "feat(*):*"],
    "patch": ["fix:*", "fix(*):*", "perf:*", "perf(*):*", "refactor:*", "refactor(*):*"],
    "major": ["BREAKING CHANGE*", "*!:*"]
  }
}

Implementation notes

  • The regex patterns in conventional_commits.rs (feat_re(), patch_re(), breaking_re()) would be replaced by glob matching from config
  • Reuse glob_match crate already used for branch matching in prerelease.rs
  • For case insensitive matching (case_sensitive: false): lowercase both the pattern and commit message before glob matching
  • The determine_bump() function takes config as input, tries each level in priority order (major → minor → patch), returns first match
  • "all" skips pattern matching and always matches that level
  • This is a breaking change — must use feat!: or feat(commits)!: for the commit introducing it
  • Schema update needed in ferrflow.json and Application/packages/site/public/schema/ferrflow.json
  • Changelog generation (changelog.rs) also relies on commit type parsing — needs to stay in sync

Acceptance criteria

  • New config option with glob pattern support for each bump level
  • case_sensitive option, defaults to true
  • Default config includes permissive patterns (capitalized variants, slash separators)
  • Each level accepts a string, a list of strings, or "all"
  • Glob matching via glob_match crate (consistent with branch matching)
  • Support "all" as a catch-all for any bump level
  • Priority resolution when multiple levels match (major > minor > patch)
  • Users can override to strict conventional commits via config
  • Unit tests for glob patterns, case sensitivity, single string, list, and "all" mode
  • Schema and docs updated

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low priority / somedayenhancementImprovement to existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions