Automatic changelog generator with built-in SemVer 2.0 tagging.
Turn your Gitmoji commits into release notes and version bumps. Girokmoji relies solely on pygit2 and runs anywhere
libgit2 is available.
We use minimum dependencies. Currently, only pygit2, which requires no runtime
git executable, good enough binary distributions, is used.
Pipelines, such as SCM provided ones (e.g., GitHub Actions), dedicated solutions (e.g., Jenkins) are best when you use it as a "trigger".
Release notes and tagging with Gitmoji flair.
Powered by a SemVer 2.0 parser, the release command tags your repo and outputs the changelog.
Run it with uvx in CI for reproducible builds.
uvx --from "girokmoji@latest" girokmoji release YOU_PROJECT_NAME --bump patch --repo-dir . > release.mduvx --from "girokmoji@latest" girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.mdYou can control how the commit range is determined with --range:
# Auto (default): direct when linear, common-base when diverged
girokmoji YOUR_PROJECT_NAME 2025-02-10 . v1.2.3 v1.3.0 --range auto
# Force raw tail..head range
girokmoji YOUR_PROJECT_NAME 2025-02-10 . v1.2.3 v1.3.0 --range direct
# Force merge-base..head (recommended for hotfix vs latest on different lines)
girokmoji YOUR_PROJECT_NAME 2025-02-10 . v1.2.3-hotfix v1.3.0 --range common-base
# Require linear history and fail if not
girokmoji YOUR_PROJECT_NAME 2025-02-10 . v1.2.3 v1.3.0 --strict-ancestorNotes:
- Informational notices about range auto-detection (e.g., switching to common-base, or head-only fallback) are printed to stderr. The generated changelog is written to stdout, so you can safely redirect stdout to a file without capturing notices.
Clone
git clone https://github.com/KMilhan/girokmoji.gitpython -m girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.mdor
girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.mdNote: Please change repository url with your repository url. Also, clone with enough depth, so libgit2 can
traverse to the past tag.
Install uv and create a virtual
environment:
uv pip install girokmojiThe last command installs this package together with development tools such as
pytest. Use uv pip inside the environment whenever you need to add more
packages.
If you do not use uv, you can install the project with plain pip in an
already isolated environment:
pip install girokmojigirokmoji --versionTo create JSON payload for GitHub Release:
girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 --github-payload > release.jsonTo bump the project version using the built-in versioning (fully compliant with Semantic Versioning 2.0) and output the changelog generated by girokmoji in one go:
girokmoji release YOUR_PROJECT_NAME --bump patch --repo-dir . > release.mdThe release command also accepts range-related flags which apply to the generated diff between the last tag and the new tag:
# Default auto behavior (linear histories use direct)
girokmoji release YOUR_PROJECT_NAME --bump patch --repo-dir . --range auto
# Enforce linear history if desired
girokmoji release YOUR_PROJECT_NAME --bump minor --repo-dir . --strict-ancestor
# Quiet or verbose notices
girokmoji release YOUR_PROJECT_NAME --bump patch --repo-dir . --quiet
girokmoji release YOUR_PROJECT_NAME --bump patch --repo-dir . --verboseBy default, girokmoji determines the previous tag (tail) for generating release notes and bumping versions using only tags that are reachable from the current HEAD.
- Tail tag selection:
- Only tags whose target commit is an ancestor of HEAD are considered.
- Among those, the largest SemVer tag is chosen.
- Tag formats:
- Both with or without a leading "v" are accepted (e.g., v1.2.3 or 1.2.3).
- Both annotated and lightweight tags are supported.
Hotpatch/global floor:
- To avoid version regressions when a larger SemVer tag exists on another branch (e.g., a hotpatch), the bump baseline uses the maximum of:
- The last reachable SemVer from HEAD, and
- The global maximum SemVer tag across the repository.
- Example: If HEAD’s reachable max is v1.2.3 but there is a v2.0.0 tag on a different branch, a patch bump will produce v2.0.1 by default.
- Library usage (Python API) can opt out of this behavior with:
- auto_release(..., version_floor_scope="reachable") to base the bump only on the reachable tag.
This change affects how the previous tag is chosen and how the new version is computed, while the commit range selection (auto/direct/common-base) continues to work as documented below.
auto(default):- Uses
tail..headwhen the tail is an ancestor of head (linear history). - Otherwise uses
merge-base(tail, head)..head. - If there is no common base, shows commits reachable from
headonly.
- Uses
direct: Alwaystail..head.common-base: Alwaysmerge-base(tail, head)..head(falls back to head-only when no base).
Flags and behavior:
--range/--range-mode {auto,direct,common-base}--strict-ancestor: fail if refs diverge (tail is not ancestor of head)--quiet: suppress informational notices on stderr--verbose: print chosen mode and resolved SHAs to stderr
Examples:
# Notes for a mainline release since the previous mainline tag
girokmoji MyProj 2025-08-17 . v1.2.3 v1.3.0 --range auto
# Notes comparing a hotfix tag and the latest tag on main
girokmoji MyProj 2025-08-17 . v1.2.3-hotfix v1.3.0 --range auto
# Enforce linear history in CI
girokmoji MyProj 2025-08-17 . v1.2.3 v1.3.0 --strict-ancestorFor generated release note, go EXAMPLE.md
The recommended workflow for local development uses uv for dependency
management. uv creates an isolated virtual environment and installs the
required packages using its own resolver.
# create a virtual environment in `.venv`
uv venv
# install main and development dependencies
uv sync
# run the test suite
uv run pytestSee multiline commit test for multi-line commit examples and how commits are grouped in release notes.
We use mutmut for mutation testing. For stability, subprocess-based CLI/E2E tests are excluded during mutation runs via pytest markers:
- Markers:
@pytest.mark.clifor tests that invoke the CLI in a separate Python process.- Module-level
pytestmark = pytest.mark.e2efor end-to-end tests.
- Runner filter (configured in
pyproject.toml):pytest -q -m 'not e2e and not cli'tests_dir = ["tests"]so all tests are discovered but filtered by markers.
Commands:
# run the full test suite normally (includes cli/e2e)
uv run pytest -q
# run mutation tests (excludes cli/e2e via marker filter)
uv run mutmut run
# show surviving mutants
uv run mutmut resultsMake targets are also available:
make mutation
make mutation-resultsNotes:
- Regular pytest remains unchanged and continues to run the full suite, including CLI/E2E.
- Mutation runs intentionally skip CLI/E2E to avoid subprocess-related instability during mutmut’s stats/listing phase.
- If you need to experiment with including CLI/E2E in mutation, adjust the runner filter in
pyproject.tomltemporarily.
A typical pipeline installs uv, syncs dependencies and runs girokmoji via uvx:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: astral-sh/setup-uv@v5
- run: uv pip install .
- run: pytest
- run: uvx --from "girokmoji@latest" girokmoji release MyProject --bump patch --repo-dir .The "Publish Python Package to PyPI" workflow can be dispatched manually. Provide the
tag input to select which release tag should be built and uploaded. The workflow
checks out the chosen tag, runs tests, builds the package and then deploys it to PyPI.