We recommend CodeCharter in CI as the enforcing gate, with the pre-commit hook as a fast local pre-check. Running the hook locally is optional — but it saves iteration time because you don't have to wait for the CI build to find out whether a convention was violated.
Prerequisites: the
codecharterCLI must be installed and on yourPATH, and a valid license must be available — without one, every CLI command exits with code6, so the hook would block every commit. See Installation for download and setup instructions.
Using Git hooks directly
.git/hooks/pre-commit:
#!/bin/sh
# Analyze the staged changes; only errors block the commit.
# --diff - scopes both the findings and the --fail-on gate to changed lines.
git diff --cached --unified=0 | codecharter analyze MySolution.sln --diff - --fail-on error || {
echo ""
echo "❌ CodeCharter found blocking issues."
echo "Run 'codecharter analyze MySolution.sln' for details, or commit with --no-verify if you really want."
exit 1
}
The report still lists findings of every severity in the changed lines; --fail-on error
only decides which of them block the commit. Note that without --fail-on, analyze
exits 1 on any finding, including info — so always set --fail-on explicitly in
hooks that rely on the exit code.
On an existing codebase, record the current findings once with
codecharter analyze MySolution.sln --write-baseline codecharter.baseline.json and add
--baseline codecharter.baseline.json to the hook — then only new findings block commits.
chmod +x .git/hooks/pre-commit.
Windows note:
chmodis a no-op on NTFS. The hook file is created but may not be marked executable in the Git index. For a hook that is tracked in the repository (e.g. in ahooks/directory committed to the repo), rungit update-index --chmod=+x hooks/pre-committo set the executable bit in Git's object store. For an untracked.git/hooks/pre-commit, Git for Windows (Git Bash) will usually execute it regardless of the NTFS permission.
Using pre-commit (the Python tool)
If you already use pre-commit, add this to
.pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: codecharter-analyze
name: CodeCharter analyze
entry: codecharter analyze MySolution.sln --fail-on error
language: system
pass_filenames: false
types: ['c#']
Note on
types: ['c#']: the type tag for C# files isc#(it comes from theidentifylibrary that pre-commit uses), and it needs quotes because of the#. If you prefer not to depend on type tags, usefiles: '\.cs$'instead.
Using Husky (Node.js)
For JS/TS repos that also happen to have C#, or mixed teams:
.husky/pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
codecharter analyze MySolution.sln --fail-on error
Run the hook only on C# changes
A full analysis before every commit can take longer on large solutions. Skip the run
if the commit doesn't touch any .cs files:
#!/bin/sh
CHANGED=$(git diff --cached --name-only --diff-filter=ACMR -- '*.cs')
if [ -z "$CHANGED" ]; then
exit 0
fi
codecharter analyze MySolution.sln --fail-on error
Recommended strategy
| Step | Hook |
|---|---|
pre-commit |
diff-scoped: git diff --cached --unified=0 \| codecharter analyze MySolution.sln --diff - --fail-on error |
pre-push |
codecharter analyze MySolution.sln --fail-on error (duration depends on solution size) |
| CI | codecharter analyze MySolution.sln --fail-on error (always) |
This keeps the hook in the hot path scoped to what the commit actually changes and catches obvious mistakes without making every commit wait for a full analysis.