Skip to content

Commit

Permalink
feat(template): allow to override the template from cli, configuratio…
Browse files Browse the repository at this point in the history
…n and plugins

Fixes #132
Fixes #384
Fixes #433
Closes #376
  • Loading branch information
noirbizarre committed Jan 27, 2023
1 parent c7a836d commit d83704a
Show file tree
Hide file tree
Showing 15 changed files with 594 additions and 9 deletions.
25 changes: 20 additions & 5 deletions commitizen/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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: Optional[BaseLoader] = None,
template: Optional[str] = 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


Expand Down
48 changes: 48 additions & 0 deletions commitizen/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import argparse
import logging
import sys
from copy import deepcopy
from functools import partial
from types import TracebackType
from typing import List
Expand All @@ -17,6 +18,51 @@
)

logger = logging.getLogger(__name__)


class ParseKwargs(argparse.Action):
"""
Parse arguments in the for `key=value`.
Quoted strings are automatically unquoted.
Can be submitted multiple times:
ex:
-k key=value -k double-quotes="value" -k single-quotes='value'
will result in
namespace["opt"] == {
"key": "value",
"double-quotes": "value",
"single-quotes": "value",
}
"""

def __call__(self, parser, namespace, kwarg, option_string=None):
kwargs = getattr(namespace, self.dest, None) or {}
key, value = kwarg.split("=", 1)
kwargs[key] = value.strip("'\"")
setattr(namespace, self.dest, kwargs)


tpl_arguments = (
{
"name": ["--template", "-t"],
"help": (
"changelog template file name "
"(relative to the current working directory)"
),
},
{
"name": ["--extra", "-e"],
"action": ParseKwargs,
"dest": "extras",
"metavar": "EXTRA",
"help": "a changelog extra variable (in the form 'key=value')",
},
)

data = {
"prog": "cz",
"description": (
Expand Down Expand Up @@ -190,6 +236,7 @@
"default": None,
"help": "keep major version at zero, even for breaking changes",
},
*deepcopy(tpl_arguments),
{
"name": "manual_version",
"type": str,
Expand Down Expand Up @@ -246,6 +293,7 @@
"If not set, it will generate changelog from the start"
),
},
*deepcopy(tpl_arguments),
],
},
{
Expand Down
5 changes: 5 additions & 0 deletions commitizen/commands/bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand All @@ -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."""
Expand Down Expand Up @@ -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()
Expand Down
10 changes: 9 additions & 1 deletion commitizen/commands/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ 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 = args.get("extras") or {}

def _find_incremental_rev(self, latest_version: str, tags: List[GitTag]) -> str:
"""Try to find the 'start_rev'.
Expand Down Expand Up @@ -161,7 +163,13 @@ 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.cz.template_extras.copy()
extras.update(self.config.settings["extras"])
extras.update(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:
Expand Down
10 changes: 9 additions & 1 deletion commitizen/cz/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
from __future__ import annotations

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

Expand Down Expand Up @@ -40,6 +44,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"):
Expand Down
6 changes: 6 additions & 0 deletions commitizen/defaults.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import pathlib
from collections import OrderedDict
from typing import Any, Dict, Iterable, List, MutableMapping, Optional, Tuple, Union
Expand Down Expand Up @@ -40,6 +42,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"
Expand All @@ -65,6 +69,8 @@ class Settings(TypedDict, total=False):
"update_changelog_on_bump": False,
"use_shortcuts": False,
"major_version_zero": False,
"template": None,
"extras": {},
}

MAJOR = "MAJOR"
Expand Down
20 changes: 20 additions & 0 deletions docs/bump.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -213,6 +218,21 @@ 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/-e` parameter.
```bash
cz bump --changelog --extra key=value -e short="quoted value"
```
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.
Expand Down
20 changes: 20 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -161,6 +166,21 @@ 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/-e` parameter.
```bash
cz changelog --extra key=value -e short="quoted value"
```
See [the template customization section](customization.md#customizing-the-changelog-template)
## Hooks
Supported hook methods:
Expand Down
3 changes: 3 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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] |
| <a name="cfg-template"></a>`template` | `str` | `None` | Provide custom changelog jinja template path relative to the current working directory. [See more (template customization)][template-customization] |
| <a name="cfg-extras"></a>`extras` | `dict` | `{}` | Provide extra variables to the changelog template. [See more (template customization)][template-customization] |

## pyproject.toml or .cz.toml

Expand Down Expand Up @@ -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
Loading

0 comments on commit d83704a

Please sign in to comment.