Increment version number found in source code without regex
Project description
jiggle_version
Deterministic CLI to discover, check, and bump a project version without importing user code or writing
regex. Optional autogit (stage/commit/push). Supports PEP 440 and SemVer. Includes an auto mode that
infers bump size from public API changes (__all__).
Why this exists
Version values drift across pyproject.toml, setup.cfg, setup.py, and module files. Many tools import your package
or ask you to hand-write regex. This one does neither.
Features
-
Discovery across common sources:
pyproject.toml→[project].version(PEP 621) or[tool.setuptools].versionsetup.cfg→[metadata] versionsetup.py→ static AST ofsetup(version="...")- Python modules → top-level
__version__ = "...", plus_version.py,__version__.py,__about__.py, package__init__.py
-
Agreement check (CI-friendly, no writes)
-
Bump:
major | minor | patch | autowith--scheme pep440|semver -
Auto mode: diffs the union of
__all__symbols to infer major/minor/patch; persists digest in.jiggle_version.config -
Autogit:
--autogit off|stage|commit|pushwith templated commit message -
Git-aware discovery: honors
.gitignore, repo excludes, and global gitignore; supports extra ignore paths -
Zero-import of target project; AST + safe text updates only
-
Deterministic exit codes for automation
Install
pipx install jiggle_version
# or
python -m pip install --user jiggle_version
Python: >=3.8
Runtime deps (runtime or conditional): packaging, tomlkit, pathspec, rich-argparse (help styling), tomli
on Python <3.11.
Quick start
# From your project root
jiggle_version check
jiggle_version print
jiggle_version bump --increment patch --scheme pep440 --dry-run
jiggle_version bump --increment auto --autogit commit
Initialize default config:
jiggle_version init
Configuration (pyproject.toml)
[tool.jiggle_version]
scheme = "pep440" # "pep440" | "semver"
default_increment = "patch" # "major" | "minor" | "patch" | "auto"
project_root = "."
ignore = ["docs/_build", "dist", ".venv"] # optional
# Optional autogit defaults
autogit = "off" # "off" | "stage" | "commit" | "push"
commit_message = "Release: {version}"
allow_dirty = false
Notes:
- CLI overrides config. Missing CLI args are filled from config.
ignoreis normalized to a list of relative paths.
Commands
check
Discover versions across sources and verify agreement. No writes.
jiggle_version check [--project-root .] [--ignore path ...]
print
Print the normalized version if all sources agree.
jiggle_version print
inspect
List all candidate files and run check.
jiggle_version inspect
bump
Compute next version and update all writable sources.
jiggle_version bump \
[--increment major|minor|patch|auto] \
[--scheme pep440|semver] \
[--set X.Y.Z] \
[--force-write] \
[--dry-run] \
[--autogit off|stage|commit|push] \
[--commit-message "Release: {version}"] \
[--allow-dirty]
Behavior:
- If sources disagree, operation fails unless
--force-writeor--setis provided. --setskips bump logic and writes the explicit version everywhere.
hash-all
Compute and persist API digest used by auto mode.
jiggle_version hash-all
# writes .jiggle_version.config (TOML)
init
Append a default [tool.jiggle_version] section to pyproject.toml.
Auto mode: how it decides
-
Walk project for
__all__in Python modules (respecting.gitignore+ignore). -
Build the set union of exported symbols; compare to last stored set in
.jiggle_version.config. -
Decide:
- major if any previously exported symbol was removed
- minor if new symbols were added (and nothing removed)
- patch if identical or no
__all__anywhere
-
After a successful, non–
--dry-runbump, the digest is updated.
You can pre-seed the digest with jiggle_version hash-all.
Git behavior
-
No shelling out to
gitfor ignore logic; usespathspecwith:<root>/.gitignore<root>/.git/info/exclude~/.config/git/ignoreor~/.gitignore
-
Autogit uses
subprocess.run(..., check=True):stage→git add <changed files>commit→ stage +git commit -m "<message>"push→ commit +git push origin <current-branch>
-
Refuses to proceed if repo is dirty and autogit is requested, unless
--allow-dirty.
Exit codes (stable for CI)
User / project issues (treated as “expected” for tests):
100— no version declarations found102— discovered versions disagree103— git repo dirty and--allow-dirtynot set104— config not found where required
Tool / unexpected failures:
1— unexpected error2— discovery error (I/O, traversal)3— auto-increment analysis error4— bump calculation error (invalid version/scheme)5— failed to update a file6— autogit failed7— hash/digest generation failed8— argparse error (invalid CLI)
Contract for test runners:
- Treat >=100 as user error (assertable, not a tool crash).
- Treat <100 as application failure (potential bug).
Safety model
- Never imports or executes target project code.
- Python parsed via
ast; setup parsing limited to literalversion="...". - TOML via
tomllib/tomli, INI viaconfigparser,tomlkitused to preserve formatting on write.
Semantics (bumping)
- PEP 440 (default): increments numeric release segments; drops pre/dev/post markers on standard bump.
- SemVer: enforces
MAJOR.MINOR.PATCH; strips pre-release/build on standard bump. (Flags to preserve/annotate can be added later.)
CLI quality-of-life
- Typo suggestions for choice arguments (e.g., wrong subcommand/value).
- Verbose logging:
-v→ INFO,-vv→ DEBUG; or--log-level DEBUG. - Rich help text when
rich-argparseis available.
CI usage examples (GitHub Actions)
Drift check (no writes):
- run: pipx install jiggle_version
- run: jiggle_version check
Release bump (auto + autogit):
- run: pipx install jiggle_version
- run: jiggle_version hash-all
- run: jiggle_version bump --increment auto --autogit push
Known limitations / non-goals
- Won’t evaluate dynamic
setup.pylogic (files, env, computed constants). - Only updates known patterns; exotic version locations aren’t modified.
- Single, project-wide version policy (per-module versioning is out-of-scope for now).
Troubleshooting
- “No version found”: ensure one of the supported sources exists and is literal.
- “Versions disagree”: run
jiggle_version inspectto see sources; reconcile or use--force-writeonce. - Auto mode always “patch”: ensure you actually export a public API via
__all__. - Ignored paths not respected: confirm entries in
pyproject.tomlunder[tool.jiggle_version].ignore(list or string), and that.gitignorecovers generated trees.
Contributing
- Add/adjust unit tests (no tests for logging needed).
- Keep exit codes and CLI surfaces stable.
- Prefer AST/TOML/INI approaches over regex.
- Windows paths: avoid
shell=True, preferPathAPIs.
License
MIT. See LICENSE.
Minimal API surface (for importers)
This is a CLI-first tool. Internal modules may change. If you import, prefer:
jiggle_version.__about__.__version__- Running via
python -m jiggle_version
Project details
Release history Release notifications | RSS feed
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 jiggle_version-2.1.0.tar.gz.
File metadata
- Download URL: jiggle_version-2.1.0.tar.gz
- Upload date:
- Size: 11.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24358dfffe6125fc805bb167dd49a7b458e1854b3c43f33c4cff31d4a11183a7
|
|
| MD5 |
01193f832fe4060edbe30e56907e77e7
|
|
| BLAKE2b-256 |
aec315181ba45f3517023ae667fd9ff2668b5912c24b8a0c09a951525e53311c
|
Provenance
The following attestation bundles were made for jiggle_version-2.1.0.tar.gz:
Publisher:
publish_to_pypi.yml on matthewdeanmartin/jiggle_version
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jiggle_version-2.1.0.tar.gz -
Subject digest:
24358dfffe6125fc805bb167dd49a7b458e1854b3c43f33c4cff31d4a11183a7 - Sigstore transparency entry: 624973035
- Sigstore integration time:
-
Permalink:
matthewdeanmartin/jiggle_version@43c923c4d446a4af6c1fe0b099cfc658b52412ed -
Branch / Tag:
refs/heads/main - Owner: https://github.com/matthewdeanmartin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@43c923c4d446a4af6c1fe0b099cfc658b52412ed -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file jiggle_version-2.1.0-py3-none-any.whl.
File metadata
- Download URL: jiggle_version-2.1.0-py3-none-any.whl
- Upload date:
- Size: 30.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5afbb2d3521c131b5753124f4007abce7ec4b0200c24d28ab49fdcc3b9727de
|
|
| MD5 |
9528d05f8c28f9db9f05539f62995528
|
|
| BLAKE2b-256 |
c195e8c2b77b0445c16f66f754ea5912b1b0390ae1f6ebdbb2350fd57be36a0d
|
Provenance
The following attestation bundles were made for jiggle_version-2.1.0-py3-none-any.whl:
Publisher:
publish_to_pypi.yml on matthewdeanmartin/jiggle_version
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jiggle_version-2.1.0-py3-none-any.whl -
Subject digest:
a5afbb2d3521c131b5753124f4007abce7ec4b0200c24d28ab49fdcc3b9727de - Sigstore transparency entry: 624973128
- Sigstore integration time:
-
Permalink:
matthewdeanmartin/jiggle_version@43c923c4d446a4af6c1fe0b099cfc658b52412ed -
Branch / Tag:
refs/heads/main - Owner: https://github.com/matthewdeanmartin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yml@43c923c4d446a4af6c1fe0b099cfc658b52412ed -
Trigger Event:
workflow_dispatch
-
Statement type: