Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions style/change_log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Charm Tech change log style guide

This is the change log style we inherit from [canonical/operator](https://github.com/canonical/operator).

We will use this style going forward in all repos that we own.

## File format

The change log is written in Markdown. The file is located in the root directory of a repo, with the name `CHANGES.md`. See [operator's change log](https://github.com/canonical/operator/blob/main/CHANGES.md) and [jubilant's change log](https://github.com/canonical/jubilant/blob/main/CHANGES.md).

## Specification

Each `CHANGES.md` contains one or many sections.

Each section in the change log represents a release version, and the included commits.

The section title has the version number and its released date (in Github Release), separated by a dash `-`.

```
# {{ version_number }} - {{ date }}
```

For example, Jubilant [v1.9.0](https://github.com/canonical/jubilant/releases/tag/v1.9.0) has the following section title:

```
# 1.9.0 - 29 April 2026
```

We use [conventional commits](https://www.conventionalcommits.org). Only these types of commit are recorded in `CHANGES.md`: `feat`, `fix`, `docs`, `test`, `refactor`, `ci`. Commit types not in these types are ignored.

Each commit type has its own Sub-section. The table below maps each commit type to a Sub-section. **All** breaking change commits belongs to their own Sub-section `Breaking changes`:

| Commit type | Sub-section |
| :--- | :--- |
| `<type>!:` | `## Breaking changes` |
| `feat:` | `## Features` |
| `fix:` | `## Fixes` |
| `docs:` | `## Documentation` |
| `test:` | `## Tests` |
| `refactor:` | `## Refactoring` |
| `ci:` | `## CI` |

The order of Sub-sections follow the table from top to bottom. For example:

```
# 1.9.0 - 29 April 2026

## Breaking Changes

* Commit message (#1)
* Commit message (#2)

## Features

* Commit message (#1)
* Commit message (#2)

## Fixes

* Commit message (#1)
* Commit message (#2)

## Documentation

* Commit message (#1)
* Commit message (#2)

## Tests

* Commit message (#1)
* Commit message (#2)

## Refactoring

* Commit message (#1)
* Commit message (#2)

## CI

* Commit message (#1)
* Commit message (#2)
```

If a Sub-section doesn't have a commit, it is not included.

Spacing: There is exactly one empty lines before and after each Section and Sub-section. There is no empty line between commits.

Each commit follows this format:

```
* {{ capitalized_first_letter_message }} #{{ Pull request number }}
```

If a commit doesn't follow conventional commit, discuss with the commit author or the team to decide. You must put all code symbols (for example: method names) in backticks.

## Toolings

You can use [cliff](https://git-cliff.org/) to generate the change log. See [cliff.toml](./cliff.toml) for an example configuration.

Remember to double check the tool's result before checking it into the repo.
42 changes: 42 additions & 0 deletions style/cliff.toml

@tromai tromai May 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not too sure about putting this in the style directory. May be we can have another directory for such files?

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration

[changelog]
header = "# Changelog\n\n"

body = """
{% if version %}\
# {{ version | trim_start_matches(pat="v") }} - {{ timestamp | date(format="%d %B %Y") }}
{% for group, commits in commits | group_by(attribute="group") %}
## {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
* {{ commit.message | upper_first }}{% if commit.metadata.issue %} (#{{ commit.metadata.issue }}){% endif %}\
{% endfor %}
{% endfor %}
{% endif %}
"""

trim = true

render_always = false

[git]
conventional_commits = true

filter_unconventional = false

commit_parsers = [
# https://git-cliff.org/docs/tips-and-tricks#changing-the-group-order
{ message = "^[a-z]*!:", group = "<!-- 0 -->Breaking Changes" },
{ message = "^feat", group = "<!-- 1 -->Features" },
{ message = "^fix", group = "<!-- 2 -->Fixes" },
{ message = "^docs", group = "<!-- 3 -->Documentation" },
{ message = "^test", group = "<!-- 4 -->Tests" },
{ message = "^refactor", group = "<!-- 5 -->Refactoring" },
{ message = "^ci", group = "<!-- 6 -->CI" },
{ message = "^chore", skip = true },
]

filter_commits = false

fail_on_unmatched_commit = false