This repository moves fastest when code, docs, config surface, and tests stay in sync. If you add a feature or change behavior, update the related files in the same change.
This applies to both human contributors and any automated tooling used to generate code.
The repository ships a mise config that installs every required tool in one step.
Install mise:
# macOS
brew install mise
# macOS / Linux / WSL
curl https://mise.run | shThen activate it in your shell (one-time):
# zsh
echo 'eval "$(mise activate zsh)"' >> ~/.zshrc && source ~/.zshrc
# bash
echo 'eval "$(mise activate bash)"' >> ~/.bashrc && source ~/.bashrcThen from the repo root:
mise installThis installs Rust (stable), Node 24, Python 3.12, cargo-audit,
cargo-deny, cargo-llvm-cov, wasm-pack, pre-commit, reuse, and
bump-my-version — and automatically installs the pre-commit and pre-push
hooks as a post-install step.
When you add a user-visible feature, also update:
- the implementation
- the tests
- the user-facing docs
CHANGELOG.md— add an entry under## Unreleased- the roadmap if the project scope or milestone state changed
Do not leave “I’ll document it later” gaps behind. This repository has many interconnected components, and documentation that drifts from the implementation becomes expensive to untangle quickly.
Every user-facing change must have an entry in CHANGELOG.md under the
## Unreleased section before it is merged. Group entries under ### Added,
### Changed, ### Fixed, or ### Removed as appropriate.
Internal-only changes (refactors, CI tweaks, test-only changes) do not need a changelog entry unless they affect the user experience.
If you need to cut a release, see RELEASING.md at the
repository root. It documents the prepare-release / release-on-tag workflow
split, the required secrets and GitHub Environments, and which sibling
repositories the release pipeline writes to.
Update the CLI implementation in src/main.rs.
Then update:
README.md- usage examples
- the CLI flag list
- exit code notes if behavior changed
tests/cli.rs- add or update integration coverage for the new behavior
If the new flag is a config-backed override or exposes a formatter/config feature, also update:
src/config/file.rsdefault_config_template()
The dump-config guard test in src/main.rs expects every config-backed long
flag to appear in the dumped config template. If a new long flag is
intentionally operational-only, add it to the non-config allowlist in that
test.
Operational-only flags still need:
- docs if they are user-visible
- integration tests in
tests/cli.rs - a review of the non-config allowlist in the dump-config guard test in
src/main.rs
Update the runtime config model in src/config/mod.rs.
Then update:
src/config/file.rs- TOML deserialization structs
- config merge logic
default_config_template()
src/config/legacy.rs- add a match arm in
merge_format_sectionormerge_markup_sectionso--convert-legacy-configconverts the option instead of emitting an "unsupported" note - add the corresponding field to
OutputFormatSection/OutputMarkupSectionand itshas_any()method
- add a match arm in
docs/src/content/docs/config.md- add a subsection to the relevant Options group
- add the option to the table of contents and the Defaults block
README.md- config examples or supported option lists if applicable
- tests covering config parsing, precedence, and behavior
If the option has a CLI override flag, also update:
src/main.rstests/cli.rs
Regenerate and commit the JSON schema so that editors with SchemaStore integration pick up the new option:
cmakefmt config schema > docs/public/schemas/latest/schema.jsonThe check-docs pre-commit hook will fail if the schema is out of date.
The browser playground loads its default config from the WASM
default_config_yaml() function at runtime, so config changes are
picked up automatically after a WASM rebuild.
Update the formatter code under src/formatter/.
Then update:
tests/snapshots.rstests/idempotency.rs- any affected fixtures under
tests/fixtures/ README.mdif user-visible behavior changed
If the formatted output changes in ways that would affect real-world projects
(different indentation, wrapping behavior, or keyword grouping), review whether
docs/src/content/docs/architecture.md should also be updated.
If the change affects real-world behavior, also update:
tests/fixtures/real_world/- add, remove, or refresh corpus files intentionally
tests/fixtures/real_world/SOURCES.md- keep the fixture manifest and provenance links in sync
tests/real_world.rs- update coverage rules if the corpus shape changes
tests/snapshots/- refresh real-world snapshots when formatted output changes
Parser changes usually require coordinated updates across:
src/parser/mod.rssrc/parser/cursor.rssrc/parser/scanner.rssrc/parser/grammar.rssrc/parser/lower.rssrc/parser/ast.rs- parser fixture tests and any snapshots affected by parse-tree changes
Spec or built-in command changes usually require coordinated updates across:
src/spec/mod.rssrc/spec/registry.rssrc/spec/builtins.yaml- spec registry tests
When you update the built-in spec to a newer upstream CMake release, also:
- bump
metadata.cmake_versionandmetadata.audited_atinsrc/spec/builtins.yaml - update the audited version mention in
README.md - add or adjust registry tests for the new commands, forms, or keywords
- use the official CMake command docs and release notes as the source of truth
Do not claim support for a newer audited CMake spec version unless the built-in registry and tests were updated together.
Keep these aligned:
benches/docs/src/content/docs/performance.md.github/workflows/ci.ymlif CI should run it.pre-commit-config.yamlif local hygiene should enforce itREADME.mdif users should know about it
Release tooling has a split ownership model:
.github/workflows/prepare-release.yml- owns version bumps, lockfile refresh,
CHANGELOG.mdstamping,scripts/sync-changelog.py, schema versioning, commit creation, tagging, and push - do not run any of those steps manually — see the Releasing
section in
AGENTS.mdfor what to prepare before triggering the workflow and what stays out of your hands
- owns version bumps, lockfile refresh,
.github/workflows/release.yml- builds, verifies, and publishes from an existing tag
packaging/homebrew/- stores the Homebrew formula template and notes
- the rendered formula is generated as a release artifact, not maintained as a versioned formula file in-tree
Keep these aligned:
README.mddocs/CHANGELOG.mddocs/README.md- any relevant published docs pages under
docs/src/content/docs/
If the change affects contributor workflow or repo structure, also update:
src/README.mdtests/README.mdtests/fixtures/README.mdbenches/README.md
If you add a new primary docs page, add it to:
- the sidebar in
docs/astro.config.mjs docs/src/content/docs/scripts/check-docs.sh
The docs site is an Astro + Starlight app. Before you finish a docs-heavy change, run:
bash scripts/check-docs.shIf you add or edit a before/after formatting example in any doc page, verify
the "after" output by running the input through cmakefmt directly — either
via the CLI or the playground. Never write an "after" by hand;
the formatter is the source of truth.
# Quick way to verify a before/after snippet:
printf 'your_cmake_input_here\n' | cargo run --quiet -- /dev/stdinIf you changed the public Rust API or the meaning of exported types/functions, also update the rustdoc comments on the affected items and verify the API docs still build:
cargo doc --no-depsKeep REUSE metadata aligned with the change.
- If you add a new prose/config/documentation file that should be covered by
REUSE.toml, add an annotation for its path instead of copying SPDX headers into the file unless an inline header is required for that file type. - If you edit a file in a new calendar year and its copyright year is encoded
inline or in
REUSE.toml, update the year so the metadata still matches the file history.
Check:
REUSE.toml- any inline SPDX header on the edited file
Run:
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo doc --no-depsIf you changed CLI behavior, also check:
cargo run -- --help
cargo run -- config dumpIf you added or changed a config option, also verify the legacy conversion round-trip produces no new "unsupported" notes:
cmake-format --dump-config > /tmp/cmf_dump.py
cargo run -- config convert /tmp/cmf_dump.pyIf you changed docs, also check:
bash scripts/check-docs.shIf you add a feature and do not update tests, docs, and the config template where relevant, the change is incomplete.