Using TopMark with pre-commit¶
Pre-commit integration allows TopMark to participate in:
- local Git workflows
- CI validation
- staged-file checks
- repository-wide mutation workflows
- runtime policy enforcement during commits and pushes
Note
The canonical vocabulary used throughout the documentation is defined in Terminology and Canonical Vocabulary.
TopMark ships a hook manifest so you can run header checks in Git workflows and CI. This page covers setup, recommended patterns, and troubleshooting.
Hook execution uses the same runtime-resolution, filtering, policy-evaluation, and runtime configuration semantics as normal CLI execution.
Quick start (consumer repos)¶
For canonical file type identity semantics and runtime configuration behavior, see Configuration discovery, precedence, and policy.
Add TopMark to a project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/shutterfreak/topmark
rev: v1.0.0 # pin to a released tag
hooks:
- id: topmark-check
# Optional: limit scope to supported text types
# files: '\.(py|md|toml|ya?ml|sh|Makefile)$'
args: ["--report", "actionable", "--summary"]
Install and run:
Hooks provided by TopMark¶
TopMark provides three pre-commit hooks to help manage, mutate, and diagnose file headers:
topmark-check- non-destructive validation. Fails if headers need changes.- Entry:
topmark check topmark-apply- destructive fix; requires--apply. Markedmanualso it only runs when explicitly invoked.- Entry:
topmark check --apply topmark-probe- read-only runtime-resolution diagnostics. Explains which file type and processor TopMark selects for each input. Markedmanualbecause it is intended for troubleshooting and investigation rather than routine commit validation.- Entry:
topmark probe
Hook policy¶
By default:
topmark-checkruns automatically atpre-commitandpre-push.\ It validates headers and fails if changes are needed.topmark-applyis restricted to the manual stage.\ It may modify files and should only be invoked explicitly by developers.topmark-probeis restricted to the manual stage.\ It is diagnostic-only and read-only, but its output is primarily useful when investigating file runtime discovery, filtering, file-type selection, or processor resolution.
This policy ensures safety in CI and everyday workflows. Teams that want formatter-like behavior
(similar to Black or Prettier) may choose to enable topmark-apply at pre-commit once the
repository is clean.
TopMark intentionally defaults to non-destructive behavior unless --apply is explicitly enabled.
The hook manifest intentionally exposes minimal runtime defaults. All runtime behavioral flags (such
as --summary, --report, policy options, probe verbosity, file-type filters, or output modes)
should be supplied by consuming repositories via the hook's args: configuration.
TopMark performs staged whole-source TOML validation during hook execution; TOML-source diagnostics are included in the reported runtime configuration diagnostics.
Consumers can control configuration-loading validation strictness using --strict / --no-strict.
This overrides the effective strict setting resolved from TOML sources for the duration of the
hook run.
Note
[config].strict is a TOML-source-local strictness preference controlling staged
configuration-loading validation for the current TOML source.
Effective strictness is evaluated across:
- TOML-source diagnostics;
- merged-config diagnostics;
- runtime applicability diagnostics.
strict is resolved during TOML loading and does not become a layered configuration field.
In the current implementation, this strictness is applied across staged configuration-loading validation (TOML-source, merged-config, and runtime applicability diagnostics), while the reported diagnostics remain the flattened compatibility view derived from staged validation logs. For the stable 1.x line, this boundary is intentional: staged validation remains primarily internal, while hook output exposes only the flattened compatibility view.
For the topmark-check hook (which runs topmark check), consumers may also
pass policy options such as --header-mutation-mode, --allow-header-in-empty-files, or
--empty-insert-mode when they need command-specific behavior on top of the resolved config.
These options follow the same runtime policy-resolution and file type identity semantics as normal CLI execution.
Invoke the manual hook locally:
# Apply headers on the whole repo
pre-commit run topmark-apply --all-files --hook-stage manual
# Or apply headers to specific files
pre-commit run topmark-apply --files path/to/file1 path/to/file2 --hook-stage manual
# Explain file-type and processor resolution for selected files
pre-commit run topmark-probe --files README.md pyproject.toml --hook-stage manual
Pre-commit and files¶
Pre-commit batches filenames to avoid OS argument-length limits (ARG_MAX). Your hook may run multiple times per invocation (for different batches). This is expected.
TopMark applies the same layered runtime filtering pipeline during hook execution:
- path filtering
- file-type filtering
- runtime-resolution and probe evaluation
- runtime policy evaluation
Note
- Verbosity (
-v/--verbose) affects only TEXT rendering. - Quiet mode (
-q/--quiet) suppresses TEXT rendering for commands that support it. - Markdown and machine-readable output are not affected by TEXT verbosity controls.
Run once per repository by setting pass_filenames: false in the hook manifest and letting
TopMark perform its own file discovery from config:
About args¶
Arguments passed through args: behave exactly like normal CLI arguments.
Pre-commit supports an args: list in consumer repos (in .pre-commit-config.yaml). Because
TopMark's hook manifest uses minimal runtime defaults, consumer args: are the primary mechanism
for configuring TopMark runtime behavior when run under pre-commit.
Example (consumer repo):
repos:
- repo: https://github.com/shutterfreak/topmark
rev: v0.10.1
hooks:
- id: topmark-check
args: ["--report", "noncompliant", "--output-format=ndjson"]
For the manual hook:
For the diagnostic probe hook:
Examples using canonical qualified identifiers:
Notes:
-
args:is appended to the hook'sentry. -
Prefer
args:over copying a fullentry:in the consumer config; it stays compatible when the hook entry changes. -
If you need TopMark to run once per repo (self-discovery), combine
pass_filenames: falsewithargs:as needed. -
TEXT-oriented human-readable controls such as
-v/--verboseand-q/--quietaffect only human TEXT output; Markdown and machine-readable JSON/NDJSON output ignore these flags.
File-type identifier behavior¶
TopMark accepts file type identifiers in local form, such as python, or qualified form, such as
topmark:python.
Local identifiers are accepted only when unambiguous. Internally, TopMark normalizes identifiers to canonical qualified file type identities before filtering, runtime resolution, policy evaluation, diagnostics, and registry lookup.
See file-type filtering for the full identifier contract.
Pre-commit hook arguments, TOML configuration, and runtime policy evaluation all share the same canonical file type identity semantics.
Recommended patterns¶
CI-friendly checks¶
These patterns are especially useful for repository-wide validation workflows in CI.
During these runs, staged configuration-loading validation includes per-source TOML validation before layered runtime configuration merging, so schema issues are surfaced alongside normal check runtime diagnostics.
# Enforce strict config validation in CI
# (warnings are treated as errors)
topmark check --report actionable --strict
You can also pass --summary to receive only a summary instead of per-file diagnostics.
Narrow file scope in consuming repos¶
Exit codes in pre-commit hooks¶
TopMark hooks rely on the stable CLI exit-code contract to signal success or failure to pre-commit and CI.
-
topmark-check(non-destructive): -
Exits with
SUCCESS (0)when all headers are up-to-date. - Exits with
WOULD_CHANGE (2)when headers would be added/updated in dry-run mode (this causes the hook to fail as intended). -
May exit with other non-zero codes for errors, for example:
FILE_NOT_FOUND (66)for explicit missing input pathsIO_ERROR (74)orPERMISSION_DENIED (77)for filesystem issuesCONFIG_ERROR (78)for configuration problems
-
topmark-apply(manual, destructive): -
Exits with
SUCCESS (0)on successful application of changes. -
May exit with the same error codes as above if issues occur during processing.
-
topmark-probe(manual, read-only diagnostic): -
Exits with
SUCCESS (0)when all explicit inputs resolve successfully. - Exits with
UNSUPPORTED_FILE_TYPE (69)when one or more inputs produce unsupported, filtered, unavailable, or unresolved semantic runtime outcomes. - May exit with other non-zero codes for hard input, filesystem, usage, or configuration errors.
Notes:
- Pre-commit treats any non-zero exit code as a failure; this is expected for
topmark-checkwhen changes are needed (WOULD_CHANGE (2)). - Use
--quietin CI to suppress human-readable TEXT output and rely solely on exit status. - Use
topmark-probewhen a hook appears to skip, include, or classify files with unexpected semantic runtime outcomes. - For full details and edge cases (mixed-result runs, precedence), see:
Exit codescheckcommandstripcommand
hooks:
- id: topmark-check
files: '\.(py|md|toml|ya?ml|sh|Makefile)$'
args: ["--report", "actionable"]
Runtime hook model¶
TopMark intentionally separates:
- staged configuration-loading validation
- layered runtime configuration resolution
- runtime applicability evaluation
- runtime probing and processor resolution
- runtime policy evaluation
- runtime mutation planning and execution
Machine-readable diagnostics and runtime behavior expose a flattened compatibility view derived from these internal runtime stages while preserving deterministic stable 1.x runtime behavior.
Troubleshooting¶
"uses deprecated stage names (commit, push)"¶
Use modern names in the manifest: pre-commit and pre-push.
FileNotFoundError when loading topmark-example.toml¶
Ensure the bundled example TOML resource is loaded through package resources (TopMark already does)
and that the file is included as package data. In pyproject.toml:
Test your hook locally¶
# Uses the committed manifest from the current repo
topmark version
pre-commit clean
pre-commit try-repo . topmark-check --all-files --verbose