From 1575d26edf993a7c3ec3af940c51588d5ac15bf6 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Mon, 1 Jul 2024 12:21:29 -0700 Subject: [PATCH] admin: Document my git-cliff workflow for release notes 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 --- .github/workflows/analysis.yml | 4 + .github/workflows/ci.yml | 2 + .github/workflows/scorecard.yml | 8 ++ CONTRIBUTING.md | 78 +++++++++++++++++ RELEASING.md | 85 +++++++++++++++++- docs/dev/Changes-skeleton-major.md | 48 +++++++++++ docs/dev/Changes-skeleton-patch.md | 4 + src/doc/cliff.toml | 133 +++++++++++++++++++++++++++++ 8 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 docs/dev/Changes-skeleton-major.md create mode 100644 docs/dev/Changes-skeleton-patch.md create mode 100644 src/doc/cliff.toml diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index d83aacce81..3fa62003ff 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -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: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1eee252cf4..17a0648964 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,12 @@ on: - '**.rst' - '**.analysis.yml' - '**.properties' + - 'docs/**' pull_request: paths-ignore: - '**.md' - '**.rst' + - 'docs/**' schedule: # Full nightly build - cron: "0 8 * * *" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 9d91f04417..89cccc35e6 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 478f90d6c2..d387fc76dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -134,6 +134,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 most 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 `fix!:` to make it easily +identifiable as a breaking change from the first line of the commit message. + + + Pull Requests and Code Review ----------------------------- diff --git a/RELEASING.md b/RELEASING.md index 5f073d82c9..a3d089765d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -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. @@ -353,3 +353,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 diff --git a/docs/dev/Changes-skeleton-major.md b/docs/dev/Changes-skeleton-major.md new file mode 100644 index 0000000000..c2c1a57798 --- /dev/null +++ b/docs/dev/Changes-skeleton-major.md @@ -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 diff --git a/docs/dev/Changes-skeleton-patch.md b/docs/dev/Changes-skeleton-patch.md new file mode 100644 index 0000000000..56159287e0 --- /dev/null +++ b/docs/dev/Changes-skeleton-patch.md @@ -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) + diff --git a/src/doc/cliff.toml b/src/doc/cliff.toml new file mode 100644 index 0000000000..19f13e567d --- /dev/null +++ b/src/doc/cliff.toml @@ -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 .. +# +# + +[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 = """ + +""" + +[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 = '⛰️ Features and API changes' }, + { message = '^api', group = '⛰️ Features and API changes' }, + { message = '^perf', group = '🚀 Performance Improvements' }, + { message = '^fix', group = '🐛 Fixes and feature enhancements' }, + { message = '^enh', group = '🐛 Fixes and feature enhancements' }, + { message = '^int', group = '🔧 Internals and developer goodies' }, + { message = '^dev', group = '🔧 Internals and developer goodies' }, + { message = '^cleanup', group = '🔧 Internals and developer goodies' }, + { message = '^refactor', group = '🔧 Internals and developer goodies', scope='refactor' }, + { message = '^(build|Build)', group = '🏗 Build/test/CI and platform ports' }, + { message = '^(cmake|CMake)', group = '🏗 Build/test/CI and platform ports', scope='cmake' }, + { message = '^(test|Test|testing|Testing)', group = '🏗 Build/test/CI and platform ports', scope='tests' }, +# { message = 'testsuite', group = '🏗 Build/test/CI and platform ports', scope='tests' }, + { message = '^(ci|CI)', group = '🏗 Build/test/CI and platform ports', scope='ci' }, + { message = '^(doc|docs|Docs|Documentation)', group = '📚 Documentation' }, + { message = '^(admin)', group = '🏢 Project Administration' }, + { message = 'RELICENSING', group = '🏢 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