doxygen-guard
Pre-commit hook that enforces doxygen documentation and reports the change impact of what you commit. Language- and architecture-agnostic.
Scope is deliberately narrow: enforcement and impact. Diagram generation and symbol indexing are out of scope — see Consumer Contract for the machine-readable surface that downstream tools build on.
Quick Start
1. Add the hook to .pre-commit-config.yaml
repos:
- repo: https://github.com/tvanfossen/doxygen-guard
rev: main
hooks:
- id: doxygen-guard
types_or: [c, c++, java, python]
2. Create .doxygen-guard.yaml in your repo root
output_dir: docs/generated/
validate:
exclude:
- "^tests/"
- "^\\.venv/"
tags:
req:
pattern: "^REQ-[A-Z]+-[0-9]{3}$"
version_gate:
current_version: "auto:git"
version_field: "min_version"
impact:
requirements:
file: docs/requirements.yaml
format: yaml
3. Add doxygen to your functions
/**
* @brief Read temperature from sensor hardware.
* @version 1.0
* @req REQ-SENSE-001
* @return Raw ADC value
*/
int Sensor_ReadTemperature(void) {
return hw_read_adc(TEMP_CHANNEL);
}
Run pre-commit run --all-files — violations print to stderr and the impact report
appears in <output_dir>/impact/.
What It Does
Validation (pre-commit gate)
Every function in staged files is checked for:
- Presence — must have
@brief,@version, and@return(non-void functions) - Version staleness — if function body changed (git diff),
@versionmust be updated - Tag syntax — tag values validated against configured patterns
- Requirement coverage — functions must have
@reqor an exemption tag
Exemption Tags
| Tag | Effect |
|---|---|
@internal |
Exempt from @req; excluded from the coverage report's unmapped list |
@utility |
Exempt from @req |
@callback |
Exempt from @req |
Change-Impact Reports
Cross-references git diff with parsed functions to show which requirements are affected by staged changes. Reports in markdown and JSON at <output_dir>/impact/.
Requirement Coverage
doxygen-guard coverage cross-references @req tags against the catalog and reports
covered, uncovered and orphan requirements, plus documented functions carrying no @req.
Exit code 1 when gaps exist.
Configuration Reference
validate section
| Key | Type | Default | Description |
|---|---|---|---|
languages |
dict | C, C++, Java, Python | Per-language function patterns and comment styles |
presence.require_doxygen |
bool | true |
Require doxygen on every function |
presence.require_return |
bool | true |
Require @return on non-void functions |
presence.skip_forward_declarations |
bool | true |
Skip C/C++ forward declarations |
version.require_present |
bool | true |
Require @version tag |
version.require_increment_on_change |
bool | true |
Require version bump when body changes |
exclude |
list | [] |
Regex patterns for files to skip — see doxygen-guard files |
duplicate_tags_error |
bool | true |
Flag repeated @brief/@version/@return/@file in one block |
tags.req.cross_reference |
bool | true |
Validate @req IDs exist in requirements file |
version_gate.current_version |
string | — | auto:git, auto:cmake, or explicit version |
version_gate.version_field |
string | — | Column in requirements file for version gating |
impact section
| Key | Type | Default | Description |
|---|---|---|---|
requirements.file |
string | — | Path to the requirements catalog |
requirements.format |
string | yaml |
Format: yaml, csv, or json |
requirements.id_column |
string | Req ID |
ID column — csv/json only |
requirements.name_column |
string | name |
Requirement name column/field |
These defaults are also emitted by doxygen-guard config --schema. Read them from
there rather than copying them — see Consumer Contract.
Requirements catalog
The preferred form is a mapping keyed by requirement ID. The ID is the key, so no
id_column applies:
requirements:
REQ-VAL-001:
name: Doxygen presence check # required
subsystem: Validate # optional
min_version: v0.1.0 # optional
description: >- # optional
Every function must have a doxygen comment
acceptance_criteria: >- # optional
Undocumented functions produce presence violations
The catalog is validated on load. A missing file, an unknown format, a wrong document
shape, a missing name, or an ID failing validate.tags.req.pattern each raise
RequirementsError — the run fails rather than silently proceeding with an empty
catalog. Flat csv/json row formats remain supported and use id_column.
Consumer Contract
Tools built on doxygen-guard must read the contract from the tool, not re-derive it. Every declaration the gate honours is observable in this output.
doxygen-guard config --schema # schema, defaults, catalog constants, contract_version
doxygen-guard config --effective # the merged config in force and what it resolved to
doxygen-guard files src/ # the exact post-exclude file set the gate walks
All three emit JSON carrying a contract_version. Compare it against the value your
tool was written for; when it moves, re-check your assumptions.
doxygen-guard files is the authoritative answer to "which files does the gate
consider?" — it applies validate.exclude exactly as validation does. A consumer that
walks the tree itself and diffs against this output will detect its own divergence
instead of silently reporting on files the gate never sees.
Typed errors are importable from doxygen_guard.errors: GuardError with ConfigError
and RequirementsError subclasses. load_config raises rather than exiting, so
in-process callers can handle failures; exit codes are produced only at the CLI boundary.
Passthrough config
Consumers may declare their own sections in .doxygen-guard.yaml using an x- prefix,
at any nesting level. The guard validates that they parse but never interprets them:
x-my-tool:
index_path: .cache/index
Any other unknown key is an error.
File-Level Doxygen
Each source file should have a file-level doxygen block:
/**
* @file
* @brief Sensor hardware abstraction layer.
* @version 1.0
*/
Enable validate.presence.require_file_doxygen: true to enforce file-level blocks.
For Python:
## @file
## @brief Configuration loading and validation.
## @version 1.0
Python Function Docstring Style
Python functions accept doxygen tags in two equivalent forms. Use whichever fits your codebase — they are interchangeable on a per-function basis.
Two-hash block above the def (classic doxygen-for-Python convention):
## @brief Apply a unified-diff patch to a project directory.
# @version 1.0
# @req REQ-PATCH-001
# @return 0 on success, non-zero on failure.
def apply_patch(repo_path: str, patch: str) -> int:
...
Inside the PEP 257 docstring (idiomatic Python — Sphinx/IDE-friendly):
def apply_patch(repo_path: str, patch: str) -> int:
"""Apply a unified-diff patch via `git apply`.
@brief Apply a unified-diff patch to a project directory.
@version 1.0
@req REQ-PATCH-001
@return 0 on success, non-zero on failure.
"""
...
When both styles are present on the same function, the ##-block above
takes precedence. A docstring without any recognized tag is not treated
as a doxygen block (so prose-only docstrings remain free-form).
Config Validation
The config file is validated at load time against a built-in schema. Unknown keys are rejected, with a suggestion when one is close:
doxygen-guard: Invalid config in .doxygen-guard.yaml
Unknown config key: validate.exclud — did you mean 'exclude'?
Keys prefixed x- are exempt (see Passthrough config). The schema
itself is available via doxygen-guard config --schema.
Escaping @ in Documentation Text
As in doxygen itself, @word is a command anywhere in a block — including
mid-sentence. To mention a tag name in prose, escape it as \@ or @@:
/**
* @brief Rows carry their declared \@req IDs.
* @version 1.0
*/
Without the escape, @req IDs parses as a req tag valued IDs, and the gate reports
a requirement that does not exist.
Adopting on an Existing Codebase
For repos with existing code that has no doxygen, adopt incrementally:
- Start with validation only — add
@briefand@versionto functions as you touch them. Useversion_gateto only enforce@reqon functions added after a specific version:
validate:
version_gate:
current_version: "auto:git"
version_field: "min_version"
-
Add
@returnto non-void functions — required by default. Disable withpresence.require_return: falseduring migration. -
Exclude paths you're not ready to cover:
validate:
exclude:
- "^vendor/"
- "^legacy/"
Supported Languages
| Language | Extensions | Comment Style | Body Detection |
|---|---|---|---|
| C | .c, .h |
/** ... */ |
Brace matching |
| C++ | .cpp, .hpp, .cc, .cxx |
/** ... */ |
Brace matching |
| Java | .java |
/** ... */ |
Brace matching |
| Python | .py |
## ... block above def or @tag lines inside the docstring |
Indentation |
C++ template functions (template<typename T> void func(...)) are fully supported — doxygen comments are associated via tree-sitter AST sibling detection, handling template_declaration wrappers correctly.
CLI Usage
# Pre-commit mode (default — called by pre-commit)
doxygen-guard [--config path] [files...]
# Explicit subcommands
doxygen-guard validate --no-git src/*.c
doxygen-guard impact --staged src/*.c
doxygen-guard coverage src/
# Consumer contract (JSON)
doxygen-guard config --schema
doxygen-guard config --effective
doxygen-guard files src/
# Verbose mode — logs which config sections were declared vs defaulted
doxygen-guard -v coverage src/
Scope and Direction
The tool is feature-complete for what it does: enforcement and change impact. See ROADMAP.md for what is being considered, what is explicitly out of scope, and the three tests any new feature has to pass.
License
MIT — see LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file doxygen_guard-1.3.1.tar.gz.
File metadata
- Download URL: doxygen_guard-1.3.1.tar.gz
- Upload date:
- Size: 102.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86ede66f5755a15eb805e0c07a9f35ae659b86f62c3cdae4a70eb369e7054fab
|
|
| MD5 |
2c96336682f4aebc3e036b0789d1a731
|
|
| BLAKE2b-256 |
e1d93ef34aca1b10e8924b6e66585c57892d8b0f61d3cce85aa0cce8947ee5e2
|
Provenance
The following attestation bundles were made for doxygen_guard-1.3.1.tar.gz:
Publisher:
release.yml on tvanfossen/doxygen-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
doxygen_guard-1.3.1.tar.gz -
Subject digest:
86ede66f5755a15eb805e0c07a9f35ae659b86f62c3cdae4a70eb369e7054fab - Sigstore transparency entry: 2281504532
- Sigstore integration time:
-
Permalink:
tvanfossen/doxygen-guard@7443fce1519fb4c066efae041243114175216caa -
Branch / Tag:
refs/tags/v1.3.1 - Owner: https://github.com/tvanfossen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7443fce1519fb4c066efae041243114175216caa -
Trigger Event:
push
-
Statement type:
File details
Details for the file doxygen_guard-1.3.1-py3-none-any.whl.
File metadata
- Download URL: doxygen_guard-1.3.1-py3-none-any.whl
- Upload date:
- Size: 44.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a4f3beb598d92f7ec39a1c3bf6fa5006fc580ec2df7e1d8dbeacddb03091167
|
|
| MD5 |
10b1612376f2e9fb645f45c2c0dc5da7
|
|
| BLAKE2b-256 |
9449197da6a58ed3fcb4f58528c68d266a659195cdd4141cd7938367944e2105
|
Provenance
The following attestation bundles were made for doxygen_guard-1.3.1-py3-none-any.whl:
Publisher:
release.yml on tvanfossen/doxygen-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
doxygen_guard-1.3.1-py3-none-any.whl -
Subject digest:
5a4f3beb598d92f7ec39a1c3bf6fa5006fc580ec2df7e1d8dbeacddb03091167 - Sigstore transparency entry: 2281504571
- Sigstore integration time:
-
Permalink:
tvanfossen/doxygen-guard@7443fce1519fb4c066efae041243114175216caa -
Branch / Tag:
refs/tags/v1.3.1 - Owner: https://github.com/tvanfossen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7443fce1519fb4c066efae041243114175216caa -
Trigger Event:
push
-
Statement type: