0001 — Isolate bump-my-version behind an adapter¶
- Status: Accepted
- Deciders: rhiza-tools maintainers
Context¶
The bump command drives bump-my-version
to rewrite version strings, commit, and tag. bump-my-version exposes internals
we depend on directly: bumpversion.bump.do_bump, bumpversion.config.get_configuration,
bumpversion.ui.setup_logging, the Config model (its files_to_modify and
pre_commit_hooks fields), and the BumpVersionError exception hierarchy.
These are not a stable public API. If they were referenced from many places, an upstream change would force edits scattered across the codebase, and we would likely discover the break only when a real release failed.
Decision¶
All contact with bump-my-version internals lives in a single module,
rhiza_tools.commands.bump.engine. Every other module goes through the helpers
it exposes (_build_configuration, _preflight_bump, _execute_bump, …). The
module is typed against bump-my-version's concrete Config (aliased
BumpConfig) rather than Any, because do_bump requires that exact type, so
the adapter must hold a real Config anyway — using it keeps the surface fully
checked under strict ty.
A contract test, tests/test_bumpversion_contract.py, pins the exact upstream
surface the adapter relies on (the pre_commit_hooks and files_to_modify
fields, and the importable BumpVersionError base). It inspects model fields
only and never runs a full bump, so it is cheap and fails loudly at CI time if
upstream renames or retypes anything.
Consequences¶
- An upstream change is reconciled in exactly one module, and the contract test flags it before it can corrupt a release.
- Exception handling in the adapter is narrowed to the domains upstream actually
raises (
BumpVersionError, plusValueError/OSErrorfor config loading) rather than a blanketexcept Exception— see the adapter's inline notes. - The rest of the codebase never imports from
bumpversion.*.