From 45d3ceb146662638b8f7f7f1ed5c2ec4367987ad Mon Sep 17 00:00:00 2001 From: Axel H Date: Wed, 14 Dec 2022 19:05:54 +0100 Subject: [PATCH] feat(template): allow to override the template from cli, configuration and plugins Fixes #132 Fixes #384 Fixes #433 Closes #376 --- commitizen/changelog.py | 25 ++++-- commitizen/cli.py | 28 +++++++ commitizen/commands/bump.py | 5 ++ commitizen/commands/changelog.py | 10 ++- commitizen/cz/base.py | 8 +- commitizen/defaults.py | 4 + docs/bump.md | 16 ++++ docs/changelog.md | 15 ++++ docs/config.md | 3 + docs/customization.md | 82 ++++++++++++++++++++ tests/commands/test_bump_command.py | 99 ++++++++++++++++++++++++ tests/commands/test_changelog_command.py | 91 ++++++++++++++++++++++ tests/conftest.py | 26 +++++++ tests/test_changelog.py | 88 +++++++++++++++++++++ tests/test_conf.py | 9 ++- 15 files changed, 500 insertions(+), 9 deletions(-) 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 = {