diff --git a/commitizen/changelog.py b/commitizen/changelog.py
index ead80ab775..8923dd6ff4 100644
--- a/commitizen/changelog.py
+++ b/commitizen/changelog.py
@@ -31,13 +31,21 @@
from datetime import date
from typing import Callable, Dict, Iterable, List, Optional, Tuple
-from jinja2 import Environment, PackageLoader
+from jinja2 import (
+ BaseLoader,
+ ChoiceLoader,
+ Environment,
+ FileSystemLoader,
+ PackageLoader,
+)
from commitizen import defaults
from commitizen.bump import normalize_tag
from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError
from commitizen.git import GitCommit, GitTag
+DEFAULT_TEMPLATE = "keep_a_changelog_template.j2"
+
def get_commit_tag(commit: GitCommit, tags: List[GitTag]) -> Optional[GitTag]:
return next((tag for tag in tags if tag.rev == commit.rev), None)
@@ -139,11 +147,18 @@ def order_changelog_tree(tree: Iterable, change_type_order: List[str]) -> Iterab
return sorted_tree
-def render_changelog(tree: Iterable) -> str:
- loader = PackageLoader("commitizen", "templates")
+def render_changelog(
+ tree: Iterable,
+ loader: BaseLoader | None = None,
+ template: str | None = None,
+ **kwargs,
+) -> str:
+ loader = ChoiceLoader(
+ [FileSystemLoader("."), loader or PackageLoader("commitizen", "templates")]
+ )
env = Environment(loader=loader, trim_blocks=True)
- jinja_template = env.get_template("keep_a_changelog_template.j2")
- changelog: str = jinja_template.render(tree=tree)
+ jinja_template = env.get_template(template or DEFAULT_TEMPLATE)
+ changelog: str = jinja_template.render(tree=tree, **kwargs)
return changelog
diff --git a/commitizen/cli.py b/commitizen/cli.py
index 55a04c7bb9..e489a0d6d1 100644
--- a/commitizen/cli.py
+++ b/commitizen/cli.py
@@ -190,6 +190,20 @@
"default": None,
"help": "keep major version at zero, even for breaking changes",
},
+ {
+ "name": ["--template", "-t"],
+ "help": (
+ "changelog template file name "
+ "(relative to the current working directory)"
+ ),
+ },
+ {
+ "name": ["--extra", "-e"],
+ "action": "append",
+ "dest": "extras",
+ "metavar": "EXTRA",
+ "help": "a changelog extra variable (in the form 'key=value')",
+ },
{
"name": "manual_version",
"type": str,
@@ -246,6 +260,20 @@
"If not set, it will generate changelog from the start"
),
},
+ {
+ "name": ["--template", "-t"],
+ "help": (
+ "changelog template file name "
+ "(relative to the current working directory)"
+ ),
+ },
+ {
+ "name": ["--extra", "-e"],
+ "action": "append",
+ "dest": "extras",
+ "metavar": "EXTRA",
+ "help": "a changelog extra variable (in the form 'key=value')",
+ },
],
},
{
diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py
index 14e339c669..dd6c415dac 100644
--- a/commitizen/commands/bump.py
+++ b/commitizen/commands/bump.py
@@ -46,6 +46,7 @@ def __init__(self, config: BaseConfig, arguments: dict):
"gpg_sign",
"annotated_tag",
"major_version_zero",
+ "template",
]
if arguments[key] is not None
},
@@ -58,6 +59,8 @@ def __init__(self, config: BaseConfig, arguments: dict):
self.no_verify = arguments["no_verify"]
self.check_consistency = arguments["check_consistency"]
self.retry = arguments["retry"]
+ self.template = arguments["template"] or self.config.settings.get("template")
+ self.extras = arguments["extras"]
def is_initial_tag(self, current_tag_version: str, is_yes: bool = False) -> bool:
"""Check if reading the whole git tree up to HEAD is needed."""
@@ -244,6 +247,8 @@ def __call__(self): # noqa: C901
"unreleased_version": new_tag_version,
"incremental": True,
"dry_run": dry_run,
+ "template": self.template,
+ "extras": self.extras,
},
)
changelog_cmd()
diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py
index aab613fffc..22bebf7065 100644
--- a/commitizen/commands/changelog.py
+++ b/commitizen/commands/changelog.py
@@ -49,6 +49,10 @@ def __init__(self, config: BaseConfig, args):
self.tag_format = args.get("tag_format") or self.config.settings.get(
"tag_format"
)
+ self.template = args.get("template") or self.config.settings.get("template")
+ self.extras = dict(
+ extra.split("=") for extra in args.get("extras") or [] if "=" in extra
+ )
def _find_incremental_rev(self, latest_version: str, tags: List[GitTag]) -> str:
"""Try to find the 'start_rev'.
@@ -161,7 +165,11 @@ def __call__(self):
)
if self.change_type_order:
tree = changelog.order_changelog_tree(tree, self.change_type_order)
- changelog_out = changelog.render_changelog(tree)
+ extras = self.extras | self.config.settings["extras"] | self.cz.template_extras
+ extras = self.cz.template_extras | self.config.settings["extras"] | self.extras
+ changelog_out = changelog.render_changelog(
+ tree, loader=self.cz.template_loader, template=self.template, **extras
+ )
changelog_out = changelog_out.lstrip("\n")
if self.dry_run:
diff --git a/commitizen/cz/base.py b/commitizen/cz/base.py
index a5abe35f16..22f1d25927 100644
--- a/commitizen/cz/base.py
+++ b/commitizen/cz/base.py
@@ -1,9 +1,11 @@
from abc import ABCMeta, abstractmethod
-from typing import Callable, Dict, List, Optional, Tuple
+from typing import Any, Callable, Dict, List, Optional, Tuple
+from jinja2 import BaseLoader
from prompt_toolkit.styles import Style, merge_styles
from commitizen import git
+from commitizen.changelog import DEFAULT_TEMPLATE
from commitizen.config.base_config import BaseConfig
from commitizen.defaults import Questions
@@ -40,6 +42,10 @@ class BaseCommitizen(metaclass=ABCMeta):
# Executed only at the end of the changelog generation
changelog_hook: Optional[Callable[[str, Optional[str]], str]] = None
+ template: str = DEFAULT_TEMPLATE
+ template_loader: Optional[BaseLoader] = None
+ template_extras: dict[str, Any] = {}
+
def __init__(self, config: BaseConfig):
self.config = config
if not self.config.settings.get("style"):
diff --git a/commitizen/defaults.py b/commitizen/defaults.py
index bdc853e1eb..a064af267f 100644
--- a/commitizen/defaults.py
+++ b/commitizen/defaults.py
@@ -40,6 +40,8 @@ class Settings(TypedDict, total=False):
style: Optional[List[Tuple[str, str]]]
customize: CzSettings
major_version_zero: bool
+ template: Optional[str]
+ extras: dict[str, Any]
name: str = "cz_conventional_commits"
@@ -65,6 +67,8 @@ class Settings(TypedDict, total=False):
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
+ "template": None,
+ "extras": {},
}
MAJOR = "MAJOR"
diff --git a/docs/bump.md b/docs/bump.md
index e94b96cb63..e06f14faa8 100644
--- a/docs/bump.md
+++ b/docs/bump.md
@@ -60,6 +60,7 @@ usage: cz bump [-h] [--dry-run] [--files-only] [--local-version] [--changelog]
[--devrelease DEVRELEASE] [--increment {MAJOR,MINOR,PATCH}]
[--check-consistency] [--annotated-tag] [--gpg-sign]
[--changelog-to-stdout] [--retry] [--major-version-zero]
+ [--template TEMPLATE] [--extra EXTRA]
[MANUAL_VERSION]
positional arguments:
@@ -95,6 +96,10 @@ options:
Output changelog to the stdout
--retry retry commit if it fails the 1st time
--major-version-zero keep major version at zero, even for breaking changes
+ --template TEMPLATE, -t TEMPLATE
+ changelog template file name (relative to the current working directory)
+ --extra EXTRA, -e EXTRA
+ a changelog extra variable (in the form 'key=value')
```
### `--files-only`
@@ -213,6 +218,17 @@ We recommend setting `major_version_zero = true` in your configuration file whil
is in its initial development. Remove that configuration using a breaking-change commit to bump
your project’s major version to `v1.0.0` once your project has reached maturity.
+### `--template`
+
+Provides your own changelog jinja template.
+See [the template customization section](customization.md#customizing-the-changelog-template)
+
+### `--extra`
+
+Provides your own changelog extra variables by using the `extras` settings or the `--extra` parameter.
+See [the template customization section](customization.md#customizing-the-changelog-template)
+
+
## Avoid raising errors
Some situations from commitizen rise an exit code different than 0.
diff --git a/docs/changelog.md b/docs/changelog.md
index 6f92bb21cd..9c1f3a3ce1 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -7,6 +7,7 @@ This command will generate a changelog following the committing rules establishe
```bash
$ cz changelog --help
usage: cz changelog [-h] [--dry-run] [--file-name FILE_NAME] [--unreleased-version UNRELEASED_VERSION] [--incremental] [--start-rev START_REV]
+ [--template TEMPLATE] [--extra EXTRA]
[rev_range]
positional arguments:
@@ -22,6 +23,10 @@ optional arguments:
--incremental generates changelog from last created version, useful if the changelog has been manually modified
--start-rev START_REV
start rev of the changelog.If not set, it will generate changelog from the start
+ --template TEMPLATE, -t TEMPLATE
+ changelog template file name (relative to the current working directory)
+ --extra EXTRA, -e EXTRA
+ a changelog extra variable (in the form 'key=value')
```
### Examples
@@ -161,6 +166,16 @@ cz changelog --start-rev="v0.2.0"
changelog_start_rev = "v0.2.0"
```
+### `template`
+
+Provides your own changelog jinja template by using the `template` settings or the `--template` parameter.
+See [the template customization section](customization.md#customizing-the-changelog-template)
+
+### `extras`
+
+Provides your own changelog extra variables by using the `extras` settings or the `--extra` parameter.
+See [the template customization section](customization.md#customizing-the-changelog-template)
+
## Hooks
Supported hook methods:
diff --git a/docs/config.md b/docs/config.md
index c6ee969be5..52eb287fca 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -20,6 +20,8 @@
| `customize` | `dict` | `None` | **This is only supported when config through `toml`.** Custom rules for committing and bumping. [See more][customization] |
| `use_shortcuts` | `bool` | `false` | If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [See more][shortcuts] |
| `major_version_zero` | `bool` | `false` | When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] |
+| `template` | `str` | `None` | Provide custom changelog jinja template path relative to the current working directory. [See more (template customization)][template-customization] |
+| `extras` | `dict` | `{}` | Provide extra variables to the changelog template. [See more (template customization)][template-customization] |
## pyproject.toml or .cz.toml
@@ -119,4 +121,5 @@ commitizen:
[additional-features]: https://github.com/tmbo/questionary#additional-features
[customization]: customization.md
[shortcuts]: customization.md#shortcut-keys
+[template-customization]: customization.md#customizing-the-changelog-template
[annotated-tags-vs-lightweight]: https://stackoverflow.com/a/11514139/2047185
diff --git a/docs/customization.md b/docs/customization.md
index 053dbc1230..053a77be29 100644
--- a/docs/customization.md
+++ b/docs/customization.md
@@ -381,3 +381,85 @@ from commitizen.cz.exception import CzException
class NoSubjectProvidedException(CzException):
...
```
+
+## Customizing the changelog template
+
+Commitizen gives you the possibility to provide your own changelog template, by:
+
+- either by providing one with you customization class
+- either by providing one from the current working directory and setting it:
+ - as [configuration][template-config]
+ - as `--template` parameter to both `bump` and `changelog` commands
+- either by providing a template with the same name as the default template
+
+By default, the template used is the `keep_a_changelog_template.j2` file from the commitizen repository.
+
+### Providing a template with your customization class
+
+There is 3 parameters available to hcange the template rendering from your custom `BaseCommitizen`.
+
+| Parameter | Type | Default | Description |
+| ----------------- | ------ | ------- | ----------------------------------------------------------------------------------------------------- |
+| `template` | `str` | `None` | Provide your own template name (default to `keep_a_changelog_template.j2`) |
+| `template_loader` | `str` | `None` | Override the default template loader (so you can provide template from you customization class) |
+| `template_extras` | `dict` | `None` | Provide some extra template parameters |
+
+Let's see an example.
+
+```python
+from commitizen.cz.base import BaseCommitizen
+from jinja2 import PackageLoader
+
+
+class MyPlugin(BaseCommitizen):
+ template = "CHANGELOG.md.jinja"
+ template_loader = PackageLoader("my_plugin", "templates")
+ template_extras = {"key": "value"}
+```
+
+This snippet will:
+
+- use `CHANGELOG.md.jinja` as template name
+- search for it in the `templates` directory for `my_plugin` package
+- add the `key=value` variable in the template
+
+### Providing a template from the current working directory
+
+Users can provides their own template from their current working directory (your project root) by:
+
+- providing a template with the same name (`keep_a_changelog_template.j2` unless overriden by your custom class)
+- setting your template path as `template` configuration
+- giving your template path as `--template` parameter to `bump` and `changelog` commands
+
+!!! Note
+ The path is relative to the current working directory, aka. your project root most of the time,
+
+### Template variables
+
+The default template use a single `tree` variable which is a list of entries (a release) with the following format:
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| version | `str` | The release version |
+| date | `datetime` | The release date |
+| changes | `list[tuple[str, list[Change]]]` | The release sorted changes list in the form `(type,changes)` |
+
+Each `Change` has the following fields:
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| scope | `Optional[str]` | An optionnal scope |
+| message | `str` | The commit message body |
+
+!!! Note
+ The field values depend on the customization class and/or the settings you provide
+
+When using another template (either provided by a plugin or by yourself), you can also pass extra template variables
+by:
+
+- defining them in your configuration with the [`extras` settings][extras-config]
+- providing them on the commandline with the `--extra/-e` parameter to `bump` and `changelog` commands
+
+
+[template-config]: config.md#cfg-template
+[extras-config]: config.md#cfg-extras
diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py
index b79c73e7d7..3cf4332717 100644
--- a/tests/commands/test_bump_command.py
+++ b/tests/commands/test_bump_command.py
@@ -1,12 +1,17 @@
import inspect
import sys
+from pathlib import Path
+from textwrap import dedent
from typing import Tuple
from unittest.mock import MagicMock
import pytest
+from pytest_mock import MockerFixture
import commitizen.commands.bump as bump
from commitizen import cli, cmd, git
+from commitizen.changelog import DEFAULT_TEMPLATE
+from commitizen.cz.base import BaseCommitizen
from commitizen.exceptions import (
BumpTagFailedError,
CommitizenException,
@@ -754,3 +759,97 @@ def test_bump_manual_version_disallows_major_version_zero(mocker):
"--major-version-zero cannot be combined with MANUAL_VERSION"
)
assert expected_error_message in str(excinfo.value)
+
+
+@pytest.mark.parametrize(
+ "arg,cfg,expected",
+ (
+ pytest.param("", {}, "default", id="default"),
+ pytest.param("", "changelog.cfg", "from config", id="from-config"),
+ pytest.param("--template=changelog.cmd", {}, "from cmd", id="from-command"),
+ ),
+)
+def test_bump_template_option_precedance(
+ mocker: MockerFixture,
+ tmp_commitizen_project: Path,
+ arg: str,
+ cfg: str,
+ expected: str,
+):
+ project_root = Path(tmp_commitizen_project)
+ cfg_changelog = project_root / "changelog.cfg"
+ cmd_changelog = project_root / "changelog.cmd"
+ default_changelog = project_root / DEFAULT_TEMPLATE
+ changelog = project_root / "CHANGELOG.md"
+
+ cfg_changelog.write_text("from config")
+ cmd_changelog.write_text("from cmd")
+ default_changelog.write_text("default")
+
+ create_file_and_commit("feat: new file")
+
+ if cfg:
+ pyproject = project_root / "pyproject.toml"
+ pyproject.write_text(
+ dedent(
+ f"""\
+ [tool.commitizen]
+ version = "0.1.0"
+ template = "{cfg}"
+ """
+ )
+ )
+
+ testargs = ["cz", "bump", "--yes", "--changelog"]
+ if arg:
+ testargs.append(arg)
+ mocker.patch.object(sys, "argv", testargs + ["0.1.1"])
+ cli.main()
+
+ out = changelog.read_text()
+ assert out == expected
+
+
+def test_bump_template_extras_precedance(
+ mocker: MockerFixture,
+ tmp_commitizen_project: Path,
+ mock_plugin: BaseCommitizen,
+):
+ project_root = Path(tmp_commitizen_project)
+ changelog_tpl = project_root / DEFAULT_TEMPLATE
+ changelog_tpl.write_text("{{first}} - {{second}} - {{third}}")
+
+ mock_plugin.template_extras = dict(
+ first="from-plugin", second="from-plugin", third="from-plugin"
+ )
+
+ pyproject = project_root / "pyproject.toml"
+ pyproject.write_text(
+ dedent(
+ """\
+ [tool.commitizen]
+ version = "0.1.0"
+
+ [tool.commitizen.extras]
+ first = "from-config"
+ second = "from-config"
+ """
+ )
+ )
+
+ create_file_and_commit("feat: new file")
+
+ testargs = [
+ "cz",
+ "bump",
+ "--yes",
+ "--changelog",
+ "--extra",
+ "first=from-command",
+ "0.1.1",
+ ]
+ mocker.patch.object(sys, "argv", testargs)
+ cli.main()
+
+ changelog = project_root / "CHANGELOG.md"
+ assert changelog.read_text() == "from-command - from-config - from-plugin"
diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py
index c2fafd4552..c518729fce 100644
--- a/tests/commands/test_changelog_command.py
+++ b/tests/commands/test_changelog_command.py
@@ -1,10 +1,15 @@
import sys
from datetime import datetime
+from pathlib import Path
+from textwrap import dedent
import pytest
+from pytest_mock import MockerFixture
from commitizen import cli, git
+from commitizen.changelog import DEFAULT_TEMPLATE
from commitizen.commands.changelog import Changelog
+from commitizen.cz.base import BaseCommitizen
from commitizen.exceptions import (
DryRunExit,
NoCommitsFoundError,
@@ -959,3 +964,89 @@ def test_empty_commit_list(mocker):
mocker.patch.object(sys, "argv", testargs)
with pytest.raises(NoCommitsFoundError):
cli.main()
+
+
+@pytest.mark.parametrize(
+ "arg,cfg,expected",
+ (
+ pytest.param("", {}, "default", id="default"),
+ pytest.param("", "changelog.cfg", "from config", id="from-config"),
+ pytest.param("--template=changelog.cmd", {}, "from cmd", id="from-command"),
+ ),
+)
+def test_changelog_template_option_precedance(
+ mocker: MockerFixture,
+ tmp_commitizen_project: Path,
+ arg: str,
+ cfg: str,
+ expected: str,
+):
+ project_root = Path(tmp_commitizen_project)
+ cfg_changelog = project_root / "changelog.cfg"
+ cmd_changelog = project_root / "changelog.cmd"
+ default_changelog = project_root / DEFAULT_TEMPLATE
+ changelog = project_root / "CHANGELOG.md"
+
+ cfg_changelog.write_text("from config")
+ cmd_changelog.write_text("from cmd")
+ default_changelog.write_text("default")
+
+ create_file_and_commit("feat: new file")
+
+ if cfg:
+ pyproject = project_root / "pyproject.toml"
+ pyproject.write_text(
+ dedent(
+ f"""\
+ [tool.commitizen]
+ version = "0.1.0"
+ template = "{cfg}"
+ """
+ )
+ )
+
+ testargs = ["cz", "changelog"]
+ if arg:
+ testargs.append(arg)
+ mocker.patch.object(sys, "argv", testargs)
+ cli.main()
+
+ out = changelog.read_text()
+ assert out == expected
+
+
+def test_changelog_template_extras_precedance(
+ mocker: MockerFixture,
+ tmp_commitizen_project: Path,
+ mock_plugin: BaseCommitizen,
+):
+ project_root = Path(tmp_commitizen_project)
+ changelog_tpl = project_root / DEFAULT_TEMPLATE
+ changelog_tpl.write_text("{{first}} - {{second}} - {{third}}")
+
+ mock_plugin.template_extras = dict(
+ first="from-plugin", second="from-plugin", third="from-plugin"
+ )
+
+ pyproject = project_root / "pyproject.toml"
+ pyproject.write_text(
+ dedent(
+ """\
+ [tool.commitizen]
+ version = "0.1.0"
+
+ [tool.commitizen.extras]
+ first = "from-config"
+ second = "from-config"
+ """
+ )
+ )
+
+ create_file_and_commit("feat: new file")
+
+ testargs = ["cz", "changelog", "--extra", "first=from-command"]
+ mocker.patch.object(sys, "argv", testargs)
+ cli.main()
+
+ changelog = project_root / "CHANGELOG.md"
+ assert changelog.read_text() == "from-command - from-config - from-plugin"
diff --git a/tests/conftest.py b/tests/conftest.py
index 27539e9dba..888e634ca7 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,11 +2,14 @@
import re
import tempfile
from pathlib import Path
+from typing import Iterator
import pytest
+from pytest_mock import MockerFixture
from commitizen import cmd, defaults
from commitizen.config import BaseConfig
+from commitizen.cz.base import BaseCommitizen
SIGNER = "GitHub Action"
SIGNER_MAIL = "action@github.com"
@@ -20,6 +23,14 @@ def git_sandbox(monkeypatch: pytest.MonkeyPatch, tmp_path: Path):
cmd.run(f"git config --global user.email {SIGNER_MAIL}")
+@pytest.fixture
+def chdir(tmp_path: Path) -> Iterator[Path]:
+ cwd = os.getcwd()
+ os.chdir(tmp_path)
+ yield tmp_path
+ os.chdir(cwd)
+
+
@pytest.fixture(scope="function")
def tmp_git_project(tmpdir):
with tmpdir.as_cwd():
@@ -80,3 +91,18 @@ def config():
@pytest.fixture()
def config_path() -> str:
return os.path.join(os.getcwd(), "pyproject.toml")
+
+
+class MockPlugin(BaseCommitizen):
+ def questions(self) -> defaults.Questions:
+ return []
+
+ def message(self, answers: dict) -> str:
+ return ""
+
+
+@pytest.fixture
+def mock_plugin(mocker: MockerFixture, config: BaseConfig) -> BaseCommitizen:
+ mock = MockPlugin(config)
+ mocker.patch("commitizen.factory.commiter_factory", return_value=mock)
+ return mock
diff --git a/tests/test_changelog.py b/tests/test_changelog.py
index e68a3abdcf..907dc2c11e 100644
--- a/tests/test_changelog.py
+++ b/tests/test_changelog.py
@@ -1,4 +1,7 @@
+from pathlib import Path
+
import pytest
+from jinja2 import FileSystemLoader
from commitizen import changelog, defaults, git
from commitizen.cz.conventional_commits.conventional_commits import (
@@ -853,6 +856,91 @@ def test_render_changelog(gitcommits, tags, changelog_content):
assert result == changelog_content
+def test_render_changelog_from_default_plugin_values(
+ gitcommits, tags, changelog_content
+):
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ loader = ConventionalCommitsCz.template_loader
+ template = ConventionalCommitsCz.template
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(tree, loader=loader, tempalte=template)
+ assert result == changelog_content
+
+
+def test_render_changelog_override_loader(gitcommits, tags, tmp_path: Path):
+ loader = FileSystemLoader(tmp_path)
+ tpl = "loader overridden"
+ (tmp_path / changelog.DEFAULT_TEMPLATE).write_text(tpl)
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(tree, loader=loader)
+ assert result == tpl
+
+
+def test_render_changelog_override_template_from_cwd(gitcommits, tags, chdir: Path):
+ tpl = "overridden from cwd"
+ (chdir / changelog.DEFAULT_TEMPLATE).write_text(tpl)
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(tree)
+ assert result == tpl
+
+
+def test_render_changelog_override_template_from_cwd_with_custom_name(
+ gitcommits, tags, chdir: Path
+):
+ tpl = "template overridden from cwd"
+ tpl_name = "tpl.j2"
+ (chdir / tpl_name).write_text(tpl)
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(tree, template=tpl_name)
+ assert result == tpl
+
+
+def test_render_changelog_override_loader_and_template(
+ gitcommits, tags, tmp_path: Path
+):
+ loader = FileSystemLoader(tmp_path)
+ tpl = "loader and template overridden"
+ tpl_name = "tpl.j2"
+ (tmp_path / tpl_name).write_text(tpl)
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(tree, loader=loader, template=tpl_name)
+ assert result == tpl
+
+
+def test_render_changelog_support_arbitrary_kwargs(gitcommits, tags, tmp_path: Path):
+ loader = FileSystemLoader(tmp_path)
+ tpl_name = "tpl.j2"
+ (tmp_path / tpl_name).write_text("{{ key }}")
+ parser = ConventionalCommitsCz.commit_parser
+ changelog_pattern = ConventionalCommitsCz.bump_pattern
+ tree = changelog.generate_tree_from_commits(
+ gitcommits, tags, parser, changelog_pattern
+ )
+ result = changelog.render_changelog(
+ tree, loader=loader, template=tpl_name, key="value"
+ )
+ assert result == "value"
+
+
def test_render_changelog_unreleased(gitcommits):
some_commits = gitcommits[:7]
parser = ConventionalCommitsCz.commit_parser
diff --git a/tests/test_conf.py b/tests/test_conf.py
index 746cde2401..8a707de73c 100644
--- a/tests/test_conf.py
+++ b/tests/test_conf.py
@@ -1,6 +1,7 @@
import json
import os
from pathlib import Path
+from typing import Any
import pytest
import yaml
@@ -35,7 +36,7 @@
}
-_settings = {
+_settings: dict[str, Any] = {
"name": "cz_jira",
"version": "1.0.0",
"tag_format": None,
@@ -49,9 +50,11 @@
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
+ "template": None,
+ "extras": {},
}
-_new_settings = {
+_new_settings: dict[str, Any] = {
"name": "cz_jira",
"version": "2.0.0",
"tag_format": None,
@@ -65,6 +68,8 @@
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
+ "template": None,
+ "extras": {},
}
_read_settings = {