Skip to content

Changelog for Gitmoji Enthusiasts. No dependencies, (except libgit2), uv-runnable, terminal friendly.

License

Notifications You must be signed in to change notification settings

KMilhan/girokmoji

Repository files navigation

Girokmoji

Mutation Survivability

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.

Designed for General Use

Pipeline as a trigger

Pipelines, such as SCM provided ones (e.g., GitHub Actions), dedicated solutions (e.g., Jenkins) are best when you use it as a "trigger".

Do a single thing

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.

Basic use case

Semantic Version Release

with uv (recommended, especially for release pipelines)

uvx --from "girokmoji@latest" girokmoji release YOU_PROJECT_NAME --bump patch --repo-dir . > release.md

Generate Release Note

with uv (recommended, especially for release pipelines)

uvx --from "girokmoji@latest" girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.md

You 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-ancestor

Notes:

  • 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.

Go With Classic Way

Clone

git clone https://github.com/KMilhan/girokmoji.git

with isolated pip

python -m girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.md

or

girokmoji YOUR_PROJECT_NAME 2025-02-10 your_project_repo_dir v0.1.0 v0.5.2 > release_note.md

Note: Please change repository url with your repository url. Also, clone with enough depth, so libgit2 can traverse to the past tag.

Installation

with uv (recommended for development)

Install uv and create a virtual environment:

uv pip install girokmoji

The 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.

with isolated pip

If you do not use uv, you can install the project with plain pip in an already isolated environment:

pip install girokmoji

Show version

girokmoji --version

GitHub Release payload

To 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.json

Automated release helper

To 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.md

The 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 . --verbose

Tag selection and hotpatch handling

By 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.

Range Modes (commit selection)

  • auto (default):
    • Uses tail..head when 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 head only.
  • direct: Always tail..head.
  • common-base: Always merge-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-ancestor

Example

For generated release note, go EXAMPLE.md

Development and Testing

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 pytest

See multiline commit test for multi-line commit examples and how commits are grouped in release notes.

Mutation testing

We use mutmut for mutation testing. For stability, subprocess-based CLI/E2E tests are excluded during mutation runs via pytest markers:

  • Markers:
    • @pytest.mark.cli for tests that invoke the CLI in a separate Python process.
    • Module-level pytestmark = pytest.mark.e2e for 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 results

Make targets are also available:

make mutation
make mutation-results

Notes:

  • 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.toml temporarily.

Continuous Integration

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 .

Publishing to PyPI

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.

About

Changelog for Gitmoji Enthusiasts. No dependencies, (except libgit2), uv-runnable, terminal friendly.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages