admin: Document my git-cliff workflow for release notes (#4319)
For the last year or so, I have been using git-cliff to generate the first draft of release notes. It gets me 90% of the way there for the monthly patch releases, perhaps more like 30% (but a very good starting point) for the big annual major releases. In this PR, I document my process and check in the toml file that provides the git cliff configuration that gets mostly what we need. Signed-off-by: Larry Gritz <lg@larrygritz.com>
This commit is contained in:
@@ -16,6 +16,10 @@ on:
|
||||
- master
|
||||
- '*analysis*'
|
||||
- '*sonar*'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.rst'
|
||||
- 'docs/**'
|
||||
# Allow manual kicking off of the workflow from github.com
|
||||
workflow_dispatch:
|
||||
# Uncomment the following line if we want to run analysis on all PRs:
|
||||
|
||||
@@ -12,10 +12,12 @@ on:
|
||||
- '**.rst'
|
||||
- '**.analysis.yml'
|
||||
- '**.properties'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.rst'
|
||||
- 'docs/**'
|
||||
schedule:
|
||||
# Full nightly build
|
||||
- cron: "0 8 * * *"
|
||||
|
||||
@@ -11,10 +11,18 @@ on:
|
||||
# Run on pushes to master, but only the main repo, not forks
|
||||
branches: [ "master" ]
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.rst'
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
# Only run on individual PRs if the workflow file itself is changed.
|
||||
paths:
|
||||
- .github/workflows/scorecard.yml
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**.rst'
|
||||
- 'docs/**'
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
@@ -147,6 +147,84 @@ leverage this [command line tool](https://github.com/coderanger/dco) for
|
||||
automatically adding the signoff message on commits.
|
||||
|
||||
|
||||
Commit messages
|
||||
---------------
|
||||
|
||||
### Summary heuristic
|
||||
|
||||
Look at the commit history of the project to get a sense of the style and
|
||||
level of detail that is expected in commit messages.
|
||||
|
||||
### General guidelines and expectations
|
||||
|
||||
The first line of the commit message should be a short (less than 80
|
||||
characters) summary of the change, prefixed with the general nature of the
|
||||
change (see below).
|
||||
|
||||
The rest of the commit message should be a more detailed explanation of the
|
||||
changes. Some commits are self-explanatory and don't need more than the
|
||||
summary line. Others may need a more detailed explanation. Hallmarks of
|
||||
a good commit message include:
|
||||
|
||||
* An explanation of why the change is necessary and what you hope to
|
||||
accomplish with it.
|
||||
* A description of any major user- or developer-facing changes that people
|
||||
should be aware of: changes in APIs or behaviors, new features, etc.
|
||||
* An explanation of any tricky implementation details or design decisions that
|
||||
might not be immediately obvious to someone reading the code.
|
||||
* Guideposts for somebody reviewing the code to understand the rationale and
|
||||
have any supporting background information to fully understanding the code
|
||||
changes.
|
||||
|
||||
Remember that at some point in the future, a developer unfamiliar with your
|
||||
change (maybe you, long after you've forgotten the details) might need to
|
||||
understand or fix your patch. Keep that person in mind as your audience and
|
||||
strive to write a commit message that explains the context in a way that saves
|
||||
them time and effort. Be the hero in the story of their future debugging
|
||||
effort!
|
||||
|
||||
### Using "conventional commits" prefixes
|
||||
|
||||
We strive to follow the recommendations known as [conventional
|
||||
commits](https://www.conventionalcommits.org/), which means that we would like
|
||||
merged commit messages to have their first line start with one of the
|
||||
following prefixes:
|
||||
|
||||
- `feat:` new features
|
||||
- `fix:` bug fixes
|
||||
- `perf:` performance improvements
|
||||
- `api:` changes to the public APIs
|
||||
- `int:` changes to code internals that don't affect the public API
|
||||
- `build:` changes to the build system
|
||||
- `test:` changes to the test suite or unit tests
|
||||
- `ci:` changes to the CI system
|
||||
- `docs:` changes to the documentation
|
||||
- `refactor:` code refactoring
|
||||
- `style:` formatting or other stylistic non-functional changes to the code
|
||||
- `admin:` project administration or policy changes
|
||||
- `revert:` reverting a previous commit
|
||||
|
||||
Obviously, some (if not most) PRs may qualify for more than one of these
|
||||
categories (for example, a new feature may also introduce a new API call, add
|
||||
tests, and include new documentation). If that is the case, use your best
|
||||
judgment to pick the category that best captures the essence or most important
|
||||
aspect of the change. When ambiguous, consider the list above to be a priority
|
||||
ranking (e.g., a change that fixes a bug and adds a new test should be listed
|
||||
under `fix:`, because that appears first in the list).
|
||||
|
||||
It is also encouraged, when it makes sense to do so, to put a subcategory in
|
||||
parenthesis after the prefix, like `fix(exr):` or `feat(IBA):`. It's ok to use
|
||||
obvious abbreviations for major classes or subsections: IB=ImageBuf,
|
||||
IBA=ImageBufAlgo, IC=ImageCace, TS=TextureSystem, etc. If there is no clear
|
||||
single format or class that is the man focus of the patch, then you can omit
|
||||
the subcategory.
|
||||
|
||||
API or ABI-breaking changes should additionally be marked with an exclamation
|
||||
point at the end of the prefix, like `feat!:` or `api!:` to make it easily
|
||||
identifiable as a breaking change from the first line of the commit message.
|
||||
|
||||
|
||||
|
||||
Pull Requests and Code Review
|
||||
-----------------------------
|
||||
|
||||
|
||||
+84
-1
@@ -241,7 +241,7 @@ The following are the steps for making the release:
|
||||
|
||||
2. Edit CHANGES.md to reflect the correct date of the release and ensure it
|
||||
includes any last-minute changes that were made during beta or release
|
||||
candidate stages.
|
||||
candidate stages. Please see the section "Generating release notes" below.
|
||||
|
||||
3. Push it to **your** GitHub, make sure it passes CI.
|
||||
|
||||
@@ -352,3 +352,86 @@ Odds and ends to do after the tag is pushed and the announcements are sent:
|
||||
(blank) heading for the next patch or release.
|
||||
|
||||
|
||||
## Generating release notes
|
||||
|
||||
We strongly encourage the use of "conventional commit" prefixes in commit
|
||||
messages. See [CONTRIBUTING.md](CONTRIBUTING.md#commit-messages) for details.
|
||||
|
||||
Many PRs are submitted (even by the author of this section you are now
|
||||
reading!) without the conventional commit prefix. The person who approves and
|
||||
merges the commit should take responsibility during the merge process to edit
|
||||
the commit message to add the appropriate prefix (and for that matter, to edit
|
||||
any part of the commit message for clarity). But at the end of the day, if we
|
||||
end up with some commits lacking a conventional commit prefix, it's no big
|
||||
deal -- we can fix it all up by hand when we make the release notes. But
|
||||
having CC prefixes in as many commit messages possible helps make the release
|
||||
notes process be simpler and more automated.
|
||||
|
||||
We have been using the [git-cliff](https://github.com/orhun/git-cliff) tool
|
||||
as the starting point for relese notes. The command we use is:
|
||||
|
||||
git cliff -c src/doc/cliff.toml -d v1.2.3.4..HEAD > cliff.out.md
|
||||
|
||||
where v1.2.3.4 in this example is the tag of the last release. You could also
|
||||
use commit hashes to denote the range of changes you want to document.
|
||||
|
||||
**For monthly patch releases**
|
||||
|
||||
We have found that the git-cliff output is most of what we need for the patch
|
||||
releases, and can be copied into the CHANGES.md file with only some minor
|
||||
editing needed. The template for the patch release notes can be found in
|
||||
[Changes-skeleton-patch.md](docs/dev/Changes-skeleton-patch.md).
|
||||
|
||||
* Get rid of the headings that git-cliff generates. We don't use the headings
|
||||
for the patch releases.
|
||||
* Add prefixes to any commits that don't have them (they will be marked as
|
||||
"uncategorized" by git-cliff), and feel free to change any of the existing
|
||||
prefixes that you think are wrong or could be improved for clarity.
|
||||
* Rearrange the order of the entries to be logical and readable. I prefer the
|
||||
order: feature enhancements, bug fixes, build system fixes that might impact
|
||||
users, internal changes, test improvements, documentation and administrative
|
||||
changes.
|
||||
* For patch releases, feel free to omit any entries that you think are not
|
||||
user-facing and are too minor to be worth mentioning in the release notes.
|
||||
|
||||
Strive to keep the release notes just long enough for users to know if the
|
||||
patch contains any fixes relevant to them, but short enough to be read at a
|
||||
glance and without extraneous detail.
|
||||
|
||||
Here is an example of well-constructed monthly patch release notes:
|
||||
https://github.com/AcademySoftwareFoundation/OpenImageIO/releases/tag/v2.5.12.0
|
||||
|
||||
**For annual major/minor releases**
|
||||
|
||||
For major releases, the git-cliff output is just a starting point and need
|
||||
significant editing to get the detail and quality level we expect for our
|
||||
major releases. A simple bullet list of commits is not sufficient -- we aim
|
||||
for a prose-based description of important changes that "tell the story" of
|
||||
the year's work and will be thoroughly understood by our stakeholders who need
|
||||
to understand what has changed.
|
||||
|
||||
* Copy all the headings from [Changes-skeleton-major.md](docs/dev/Changes-skeleton-major.md)
|
||||
or the previous year's release notes to get the skeleton of the major and
|
||||
minor headers that you fit everything into. Note that it mostly corresponds
|
||||
to sections of the git-cliff output, but with a more carefully constructed
|
||||
hierarchy of categories.
|
||||
* Add prefixes to any commits that don't have them (they will be marked as
|
||||
"uncategorized" by git-cliff), and feel free to change any of the existing
|
||||
prefixes that you think are wrong or could be improved for clarity.
|
||||
* Rearrange the git-cliff output into the hierarchy of our preferred major
|
||||
release notes organization. Within each section and subsection, group
|
||||
similar changes together. The chronological order of the commits isn't as
|
||||
important as clearly explaining what changed over the course of the year.
|
||||
* The git-cliff output is a good starting point for populating the notes with
|
||||
every PR, plus automatically adding a reference and link to the PR and the
|
||||
name of the author of the patch.
|
||||
* Look with a skeptical eye at the one-line bullet points from git-cliff (i.e,
|
||||
from the first line of each PR's commit message). Often, they are too terse
|
||||
or tend to [bury the lede](https://www.dictionary.com/e/slang/bury-the-lede/).
|
||||
Expand into as much explanation is necessary for users who need to know
|
||||
about the change or feature to know whether it is relevant and have some
|
||||
idea what to do or that they should look at the relevant PRs for more
|
||||
detail.
|
||||
|
||||
Here is an example of well-constructed annual major release notes:
|
||||
https://github.com/AcademySoftwareFoundation/OpenImageIO/releases/tag/v2.5.4.0
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
Release 3.0 (Fall 2024) -- compared to 2.5
|
||||
-------------------------------------------------
|
||||
|
||||
### New minimum dependencies and compatibility changes:
|
||||
* *Blah*: Raise minimum to 1.2...
|
||||
|
||||
### ⛰️ Major new features and public API changes:
|
||||
* *New feature*: item
|
||||
* *New image file format support:*
|
||||
- Format1 explanation
|
||||
- Format2 explanation
|
||||
* *oiiotool*:
|
||||
- item
|
||||
* *ImageBuf/ImageBufAlgo*:
|
||||
- item
|
||||
* *Other categories or subsystems as needed*:
|
||||
|
||||
### 🚀 Performance improvements:
|
||||
- *subsystem*: item
|
||||
|
||||
### 🐛 Fixes and feature enhancements:
|
||||
* *subsystem*: item
|
||||
* *oiiotool*: item
|
||||
* *ImageBuf/ImageBufAlgo*:
|
||||
- item
|
||||
* *ImageCache/TextureSystem*:
|
||||
- item
|
||||
* *format*:
|
||||
- item
|
||||
|
||||
### 🔧 Internals and developer goodies
|
||||
- *int*: item
|
||||
- *header.h*: developer item
|
||||
|
||||
### 🏗 Build/test/CI and platform ports:
|
||||
* CMake build system and scripts:
|
||||
- item
|
||||
* Testing and Continuous integration (CI) systems:
|
||||
- *test*: item
|
||||
- *ci*: item
|
||||
* Platform support:
|
||||
- *win*: item
|
||||
|
||||
### 📚 Notable documentation changes:
|
||||
- *docs*: item
|
||||
|
||||
### 🏢 Project Administration
|
||||
- *admin*: item
|
||||
@@ -0,0 +1,4 @@
|
||||
Release 1.2.3.4 (Month DD, YYYY) -- compared to 1.2.3.3
|
||||
-------------------------------------------------------
|
||||
- *category*: Description [#1234](https://github.com/AcademySoftwareFoundation/OpenImageIO/pull/1234) (by someuser)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# Copyright Contributors to the OpenImageIO project.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
# git-cliff configuration file for OpenImageIO
|
||||
#
|
||||
# Lines starting with "#" are comments.
|
||||
# Configuration options are organized into tables and keys.
|
||||
# See documentation for more information on available options.
|
||||
# https://git-cliff.org/docs/configuration
|
||||
#
|
||||
# Run like this:
|
||||
# git cliff --config-file .github/git-cliff.toml <FIRSTCOMMIT>..<LASTCOMMIT>
|
||||
#
|
||||
#
|
||||
|
||||
[changelog]
|
||||
# changelog header
|
||||
header = """
|
||||
# Changelog\n
|
||||
All notable changes to this project will be documented in this file.\n
|
||||
"""
|
||||
|
||||
|
||||
body = """
|
||||
{% if version %}\
|
||||
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
||||
{% else %}\
|
||||
## [unreleased]
|
||||
{% endif %}\
|
||||
{% for group, commits in commits | group_by(attribute="group") %}
|
||||
### {{ group | striptags | trim | upper_first }}
|
||||
{% for commit in commits
|
||||
| filter(attribute="scope")
|
||||
| sort(attribute="scope") %}
|
||||
{% raw %} {% endraw %}- *{{commit.scope}}*:{% if commit.breaking %} [**breaking**]{% endif %} \
|
||||
{{ commit.message | upper_first | split(pat="\n") | nth(n=0) }}\
|
||||
{% if commit.author.name != 'Larry Gritz' %} \
|
||||
(by {{ commit.author.name }})\
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
{% raw %}\n{% endraw %}\
|
||||
{%- for commit in commits %}
|
||||
{%- if commit.scope -%}
|
||||
{% else -%}
|
||||
{% raw %} {% endraw %}- *(uncategorized)*:{% if commit.breaking %} [**breaking**]{% endif %} \
|
||||
{{ commit.message | upper_first | split(pat="\n") | nth(n=0) }}\
|
||||
{% if commit.author.name != 'Larry Gritz' %} \
|
||||
(by {{ commit.author.name }})\
|
||||
{% endif %}
|
||||
{% endif -%}
|
||||
{% endfor -%}
|
||||
{% endfor %}\n
|
||||
"""
|
||||
|
||||
|
||||
# remove the leading and trailing whitespace from the template
|
||||
trim = true
|
||||
# changelog footer
|
||||
footer = """
|
||||
<!-- generated by git-cliff -->
|
||||
"""
|
||||
|
||||
[git]
|
||||
# parse the commits based on https://www.conventionalcommits.org
|
||||
conventional_commits = true
|
||||
# filter out the commits that are not conventional
|
||||
#orig: filter_unconventional = true
|
||||
filter_unconventional = false
|
||||
# process each line of a commit as an individual commit
|
||||
split_commits = false
|
||||
# regex for preprocessing the commit messages
|
||||
commit_preprocessors = [
|
||||
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "[#${2}](https://github.com/AcademySoftwareFoundation/OpenImageIO/pull/${2})"}, # replace issue numbers
|
||||
{ pattern = '^(BMP|DDS|FFMpeg|HDR|JPEG|IFF|PBM|PNG|PSD|RLA|Targa|TIFF):', replace = "fix(${1}):"},
|
||||
{ pattern = '^(bmp|dds|ffmpeg|jpeg|iff|pbm|png|psd|rla|targa|tiff):', replace = "fix(${1}):"},
|
||||
{ pattern = '^(ImageBuf|ImageBufAlgo|ImageCache|ImageInput|ImageOutput|ImageSpec|oiiotool|Python):', replace = "fix(${1}):"},
|
||||
{ pattern = '^(windows|Windows):', replace = "build(Windows):"},
|
||||
{ pattern = '^(Strutil|strutil):', replace = "dev(strutil.h):"},
|
||||
# { pattern = '^([A-Za-z]+\.h):', replace = "dev(${1}):"},
|
||||
]
|
||||
|
||||
|
||||
#commit_parsers = [
|
||||
# { message = '^fix\((.*)\)', group = 'Fix (${1})' },
|
||||
# { message = '^feat\((.*)\)', group = 'Feat (${1})' },
|
||||
#]
|
||||
# regex for parsing and grouping commits
|
||||
commit_parsers = [
|
||||
# { message = '^feat\((.*)\)', group = 'Features and API changes (${1})' },
|
||||
{ message = '^feat', group = '<!-- 10 -->⛰️ Features and API changes' },
|
||||
{ message = '^api', group = '<!-- 10 -->⛰️ Features and API changes' },
|
||||
{ message = '^perf', group = '<!-- 20 -->🚀 Performance Improvements' },
|
||||
{ message = '^fix', group = '<!-- 30 -->🐛 Fixes and feature enhancements' },
|
||||
{ message = '^enh', group = '<!-- 30 -->🐛 Fixes and feature enhancements' },
|
||||
{ message = '^int', group = '<!-- 40 -->🔧 Internals and developer goodies' },
|
||||
{ message = '^dev', group = '<!-- 40 -->🔧 Internals and developer goodies' },
|
||||
{ message = '^cleanup', group = '<!-- 40 -->🔧 Internals and developer goodies' },
|
||||
{ message = '^refactor', group = '<!-- 40 -->🔧 Internals and developer goodies', scope='refactor' },
|
||||
{ message = '^(build|Build)', group = '<!-- 50 -->🏗 Build/test/CI and platform ports' },
|
||||
{ message = '^(cmake|CMake)', group = '<!-- 50 -->🏗 Build/test/CI and platform ports', scope='cmake' },
|
||||
{ message = '^(test|Test|testing|Testing)', group = '<!-- 50 -->🏗 Build/test/CI and platform ports', scope='tests' },
|
||||
# { message = 'testsuite', group = '<!-- 50 -->🏗 Build/test/CI and platform ports', scope='tests' },
|
||||
{ message = '^(ci|CI)', group = '<!-- 50 -->🏗 Build/test/CI and platform ports', scope='ci' },
|
||||
{ message = '^(doc|docs|Docs|Documentation)', group = '<!-- 60 -->📚 Documentation' },
|
||||
{ message = '^(admin)', group = '<!-- 90 -->🏢 Project Administration' },
|
||||
{ message = 'RELICENSING', group = '<!-- 90 -->🏢 Project Administration' },
|
||||
{ message = '^style', group = 'Styling' },
|
||||
# { message = '^chore\\(release\\): prepare for', skip = true },
|
||||
{ message = '^chore', group = 'Miscellaneous Tasks' },
|
||||
{ body = '.*security', group = 'Security' },
|
||||
{ message = ".*", group = "Unconventional" },
|
||||
# , default_scope = "other"
|
||||
# },
|
||||
]
|
||||
|
||||
|
||||
# protect breaking changes from being skipped due to matching a skipping commit_parser
|
||||
protect_breaking_commits = false
|
||||
# filter out the commits that are not matched by commit parsers
|
||||
filter_commits = false
|
||||
# glob pattern for matching git tags
|
||||
tag_pattern = "v[0-9]*"
|
||||
# regex for skipping tags
|
||||
skip_tags = "v0.1.0-beta.1"
|
||||
# regex for ignoring tags
|
||||
ignore_tags = ""
|
||||
# sort the tags topologically
|
||||
topo_order = false
|
||||
# sort the commits inside sections by oldest/newest order
|
||||
sort_commits = "oldest"
|
||||
# limit the number of commits included in the changelog.
|
||||
# limit_commits = 42
|
||||
Reference in New Issue
Block a user