This page documents the pre-commit hook system that enforces code quality standards at commit time. The system provides the first tier of the three-stage quality gate (local hooks → CI → deployment) that validates code before it enters the repository.
For information about CI-level quality checks, see CI Workflow. For commitizen configuration details, see Commitizen Configuration. For ruff usage in development, see Code Quality Commands.
The pre-commit framework manages git hooks that run automated checks at three stages of the git workflow: pre-commit (before commit creation), commit-msg (after commit message entry), and pre-push (before pushing to remote).
Pre-commit Hook Architecture
Sources: afterpython/.pre-commit-config.yaml1-51
The pre-commit configuration is defined in afterpython/.pre-commit-config.yaml1-51 with the template stored in src/afterpython/templates/pre-commit-config-template.yaml1-49
| Configuration Field | Purpose | Value |
|---|---|---|
default_install_hook_types | Specifies which git hook types to install automatically | pre-commit, commit-msg, pre-push |
exclude | Files/directories to skip | ^afterpython/_website/ |
repos | List of hook repositories and their configurations | Multiple repos (see below) |
The default_install_hook_types setting at afterpython/.pre-commit-config.yaml3-6 eliminates the need to explicitly pass --hook-type when running pre-commit install.
The exclude pattern at afterpython/.pre-commit-config.yaml7 prevents hooks from running on website template files in afterpython/_website/, which are managed separately.
Sources: afterpython/.pre-commit-config.yaml1-7
Pre-commit stage hooks execute before the commit is created, allowing developers to fix issues before they enter the commit history.
Hook Configuration Mapping
These hooks are provided by the pre-commit/pre-commit-hooks repository at afterpython/.pre-commit-config.yaml9-26:
| Hook ID | Purpose | Common Fixes |
|---|---|---|
trailing-whitespace | Removes trailing whitespace from lines | Cleans up extra spaces at line ends |
end-of-file-fixer | Ensures files end with a newline | Adds missing final newline |
check-yaml | Validates YAML file syntax | Reports syntax errors in .yml files |
check-toml | Validates TOML file syntax | Reports syntax errors in .toml files |
check-added-large-files | Prevents commits of large files | Blocks files exceeding default 500KB |
Sources: afterpython/.pre-commit-config.yaml9-26
Ruff Hook Pipeline
The ruff-check and ruff-format hooks at afterpython/.pre-commit-config.yaml27-41 enforce Python code quality:
ruff-check: Lints Python code for style violations, potential bugs, and code smellsruff-format: Auto-formats Python code according to style rulesBoth hooks reference the ruff configuration at ./afterpython/ruff.toml via the --config argument at afterpython/.pre-commit-config.yaml33-35 and afterpython/.pre-commit-config.yaml39-41
Sources: afterpython/.pre-commit-config.yaml27-41
The commit-msg stage validates the commit message format after it has been entered but before the commit is finalized.
The commitizen hook at afterpython/.pre-commit-config.yaml42-47 enforces conventional commit message format:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Valid commit types include:
feat: New featuresfix: Bug fixesdocs: Documentation changesstyle: Code style changes (formatting, missing semicolons, etc.)refactor: Code refactoringperf: Performance improvementstest: Test additions or modificationsbuild: Build system changesci: CI configuration changeschore: Other changes that don't modify src or test filesSources: afterpython/.pre-commit-config.yaml42-47
Pre-push hooks execute before commits are pushed to the remote repository, providing a final validation gate.
The commitizen-branch hook at afterpython/.pre-commit-config.yaml48-50 enforces branch naming conventions and validates that the branch is in a valid state for pushing.
Sources: afterpython/.pre-commit-config.yaml48-50
Commitizen behavior is configured in afterpython/cz.toml, initialized from src/afterpython/templates/cz-template.toml1-8:
| Setting | Value | Purpose |
|---|---|---|
name | cz_conventional_commits | Use conventional commits adapter |
tag_format | v$version | Version tags formatted as v1.2.3 |
version_scheme | pep440 | Use PEP 440 version scheme |
version_provider | uv | Read/write version from pyproject.toml via uv |
update_changelog_on_bump | false | Disable automatic changelog generation |
major_version_zero | true | Allow 0.x.y versions |
Sources: src/afterpython/templates/cz-template.toml1-8
Pre-commit Installation Flow
The init_pre_commit() function at src/afterpython/tools/pre_commit.py27-37 performs initial setup:
install_pre_commit() to install git hooksSimilarly, init_commitizen() at src/afterpython/tools/commitizen.py6-13 initializes commitizen configuration by copying src/afterpython/templates/cz-template.toml1-8 to afterpython/cz.toml.
Sources: src/afterpython/tools/pre_commit.py27-37 src/afterpython/tools/commitizen.py6-13
The install_pre_commit() function at src/afterpython/tools/pre_commit.py8-10 installs hooks into .git/hooks/:
ap pre-commit install --install-hooks
The --install-hooks flag ensures that hook dependencies are also installed.
Sources: src/afterpython/tools/pre_commit.py8-10
The update_pre_commit() function at src/afterpython/tools/pre_commit.py13-24 allows programmatic updates:
data_update dict into existing configurationinstall_pre_commit()Sources: src/afterpython/tools/pre_commit.py13-24
Quality Gate Architecture
The afterpython workflow implements progressive validation:
Pre-commit hooks provide immediate feedback during development:
GitHub Actions CI at .github/workflows/ci.yml runs comprehensive checks:
See CI Workflow for details.
Conditional workflows trigger based on changes:
deploy.yml rebuilds the website when content paths changerelease.yml publishes to PyPI when version tags are pushedSee Deploy Workflow and Release Workflow for details.
Sources: afterpython/.pre-commit-config.yaml1-51
While not recommended for normal development, hooks can be bypassed using git's --no-verify flag:
Valid use cases for bypassing:
Note that bypassing local hooks does not bypass CI validation—PRs will still fail CI checks if code quality issues exist.
Pre-commit Update Process
Pre-commit hook versions are managed through the ap update deps command, which uses the pcu (Python Configuration Updater) utility to update rev: fields in afterpython/.pre-commit-config.yaml1-51
Alternatively, run pre-commit autoupdate directly to update hooks to their latest versions.
After updating hook versions, reinstall hooks:
See Dependency Update System for details on the pcu utility.
Sources: afterpython/.pre-commit-config.yaml10 afterpython/.pre-commit-config.yaml28 afterpython/.pre-commit-config.yaml43
| Hook | Stage | Repository | Purpose | Auto-fix |
|---|---|---|---|---|
trailing-whitespace | pre-commit | pre-commit/pre-commit-hooks | Remove trailing spaces | Yes |
end-of-file-fixer | pre-commit | pre-commit/pre-commit-hooks | Add final newline | Yes |
check-yaml | pre-commit | pre-commit/pre-commit-hooks | Validate YAML syntax | No |
check-toml | pre-commit | pre-commit/pre-commit-hooks | Validate TOML syntax | No |
check-added-large-files | pre-commit | pre-commit/pre-commit-hooks | Prevent large files | No |
ruff-check | pre-commit | astral-sh/ruff-pre-commit | Lint Python code | Some fixes |
ruff-format | pre-commit | astral-sh/ruff-pre-commit | Format Python code | Yes |
commitizen | commit-msg | commitizen-tools/commitizen | Validate commit message | No |
commitizen-branch | pre-push | commitizen-tools/commitizen | Validate branch state | No |
Refresh this wiki