Zum Inhalt springen

Configure CodeCharter per repository with config.yml

Configure CodeCharter per repository with .codecharter/config.yml — profiles, parameters, severities, rule activation, and path scopes.

CodeCharter reads a single configuration file, .codecharter/config.yml, from your repository. It controls which rule profiles apply, the values of tunable rule parameters, per-rule severities, which findings are switched on or off, and which paths are analysed. One file covers the whole repository, and path scopes let you vary the configuration for individual folders.

Location and discovery

Place the file at .codecharter/config.yml in your repository. When CodeCharter analyses a file, it looks for .codecharter/config.yml starting at the file's directory and walking up to the repository root, so a config at the root applies to everything beneath it.

config.yml itself is a checked-in file, not a local one. Only two paths under .codecharter/ are meant to be ignored:

.codecharter/cache/
.codecharter/config.local.yml

Do not .gitignore the whole .codecharter/ folder. A blanket ignore quietly drops config.yml from version control, so profiles, overrides, and exclusions never make it into a commit and every teammate — and CI — silently falls back to no configuration at all. Only cache/ (downloaded rule bundles) and config.local.yml (below) are machine-local; config.yml is the shared, committed source of truth.

An optional .codecharter/config.local.yml sits next to it for personal, machine-local overrides. It is never committed (it is covered by the .gitignore entry above) and takes precedence over config.yml. Use it to float your local rule versions or to quiet a rule on your machine without changing the shared configuration. CI, which never has this file, stays reproducible.

A complete example

version: 1

# Rule profiles to apply, newest-first. A bare slug or @latest floats to the
# newest published version; an exact version pins it. Platform profiles use the
# org/slug form. Use { path: ... } to load a local bundle instead of the portal.
profiles:
  - dotnet-base@1.4.2
  - security@latest
  - acme/house-style@^2.1.0
  - { path: ./bundles/internal-rules.cgbundle }

# Values for tunable rule parameters, keyed by rule-slug.paramName.
params:
  cyclomatic-complexity.max: "15"
  method-length.maxLines: "60"

# Per-rule severity. Only severity can be overridden here.
overrides:
  magic-number:
    severity: warning
  empty-catch-block:
    severity: error

# Switch findings off. Optionally limit to a namespace (in:) or to entities
# whose reported name matches a glob (match:) — fully qualified for types and
# members, plain for events and occurrence-level entities.
ignore:
  - rule: namespace-distance
    in: Acme.Generated
  - rule: todo-comment
  - rule: const-naming
    match: "*InstanceMemberBindingFlags"

# Switch a rule back on after it was ignored more broadly (e.g. in a scope).
include:
  - rule: todo-comment

# Paths excluded from analysis entirely (glob patterns).
exclude:
  - "**/tests/**"
  - "**/obj/**"
  - "**/Migrations/**"

# The test-coverage gate: threshold, report snippet size, test parallelism, the
# files left out of the coverage number and the directories never searched for
# test projects. Repository-wide, never per scope.
coverage:
  minimum-percent: 99.5
  snippet-context-lines: 3
  max-parallel-test-projects: 4
  exclude:
    - "**/*.Designer.cs"
  excluded-directories:
    - "vendor/**"

# Folder-specific overrides. Each scope matches one or more path globs and may
# set any of the sections above; settings apply only to files the scope matches.
scopes:
  - match:
      - "src/Legacy/**"
    overrides:
      magic-number:
        severity: info
    ignore:
      - rule: cyclomatic-complexity

Sections

version

Required. The configuration format version. Use version: 1.

org

Optional. Names the organization the CLI acts as, needed only when your license covers more than one. In the common single-organization case, leave it out entirely — the CLI resolves the acting organization automatically and org adds nothing.

org: acme
profiles:
  - dotnet-base@1.4.2

org is resolved hierarchically like every other key in this file: the value closest to the analyzed file wins. It takes precedence over the CODECHARTER_ORG environment variable. Naming an organization your license does not cover exits with code 6; if your license covers more than one organization and neither org nor CODECHARTER_ORG names one, the CLI exits with code 64. See Choosing an Organization in the CLI.

profiles

The rule profiles to apply. Each entry is either a slug reference or a local bundle path:

  • dotnet-base@1.4.2 — an exact published version.
  • security@latest or a bare security — floats to the newest published version, re-resolved when you run codecharter update.
  • security@^2.1.0 / security@2.1.* — a version range.
  • acme/house-style@1.0.0 — a platform profile, addressed as org/slug.
  • { path: ./bundles/rules.cgbundle } — a local .cgbundle, not fetched from the portal.

params

Values for rule parameters that a rule exposes, keyed by rule-slug.paramName. Values are written as strings; CodeCharter validates them against each parameter's declared type and range. A rule that exposes no parameters, or a parameter name that does not exist, is reported by codecharter config validate.

overrides

Changes a rule's severity. The only field is severity, one of error, warning (or warn), or info. To switch a rule off entirely, use ignore rather than overrides.

ignore and include

ignore switches findings off; include switches them back on. Each entry targets a rule by slug and may narrow the target:

  • in: Acme.Core — applies only to findings in that namespace or a namespace beneath it.
  • match: "*Dto" — applies only to entities whose reported name matches the glob. For a type, method, property, or field that name is fully qualified (namespace plus simple name, e.g. Acme.Core.OrderDto), so a pattern without a leading * must start at the namespace root — a bare simple name such as match: "OrderDto" matches nothing unless the type is declared at the global namespace. Events and occurrence-level entities (parameters, member accesses, literals, and the like) are reported under their plain, unqualified name instead, so a bare simple name matches those directly.

ignore and include share one activation signal that is evaluated in order: the base layer first, then each matching scope top to bottom. The last matching entry wins, so you can ignore a rule broadly and re-include it for a narrower path or namespace.

exclude

Glob patterns for paths that are dropped from analysis before any rule runs. exclude is additive: the base patterns and the patterns of every matching scope all apply.

coverage

Settings for the test-coverage gate run by codecharter coverage:

  • minimum-percent — the required line coverage, 0 to 100. Default: 100.
  • snippet-context-lines — how many lines of context the report prints around each uncovered region.
  • max-parallel-test-projects — how many test projects run at once after the shared build, 1 or more. Default: the processor count, capped at 4.
  • exclude — globs for source files that are left out of the coverage number.
  • excluded-directories — globs for directory trees that test-project discovery skips entirely.

The last two are easy to confuse. exclude works at measurement time: the tests still run and the matching files simply stop counting. excluded-directories works at discovery time: the tree is never walked, so a test project inside it is never found and never run. Reach for the first when generated code drags the number down, and for the second when a vendored tree brings tests you do not own.

This section is repository-wide and cannot be set inside a scope. Its exclude list is independent of the top-level exclude above: a folder you drop from rule analysis still counts towards coverage unless you exclude it here as well.

A coverage key in config.local.yml replaces the committed value rather than merging with it, which for both glob lists means the local list replaces the whole committed list. --min-coverage and --max-parallel on the command line beat both layers.

scopes

Folder-specific configuration. Each scope has a match list of path globs and may contain any of params, overrides, ignore, include, and exclude. A scope's settings apply only to files it matches. Resolution is last-match-wins for params, overrides, and rule activation, anchored on the source file of the entity being evaluated; exclude is additive across all matching scopes.

Inspecting and validating the configuration

Three commands help you work with the file:

  • codecharter config explain <path> shows the effective configuration for a given source file, including which layer (base or which scope) each value came from. Add --json for machine-readable output.
  • codecharter config validate checks the file for structural errors, unknown rule ids, invalid parameter values, and entries that never match anything.
  • codecharter config schema emits a JSON Schema for .codecharter/config.yml that editors can use for completion and validation. Pass --out <file> to write it to disk.

To edit the file from the command line instead of by hand — set parameters and severities, manage profiles and exclusions, scaffold the file, and sync the local overlay — use the config write commands. Every edit preserves your comments and formatting and is validated before it is written. See codecharter config.

Relationship to suppressions

ignore/exclude operate at the repository or folder level. To silence a single finding at the point in the code where it occurs, use an inline suppression comment instead — see Suppressions.