From 04e064cc15dee2e03d3e1101785fc67e5da16a7b Mon Sep 17 00:00:00 2001 From: Khanh Duong Quoc Date: Sun, 13 Oct 2024 10:38:44 +0900 Subject: [PATCH] fix(changelog): fix missing commit fields in context (#837) * feat(commit): add `raw_message` to `Commit` * test(fixtures): add test generate all fields in conventional commits * test(fixtures): add test do not discard missing fields in conventional commits when reading from context * docs(website): add `raw_message` fields to `context.md` --- .../test-conventional-commit/cliff.toml | 35 ++++++++++ .../test-conventional-commit/commit.sh | 52 +++++++++++++++ .../test-conventional-commit/expected.md | 35 ++++++++++ .../cliff.toml | 35 ++++++++++ .../commit.sh | 52 +++++++++++++++ .../context.json | 1 + .../expected.md | 35 ++++++++++ .github/workflows/test-fixtures.yml | 3 + check.md | 64 +++++++++++++++++++ git-cliff-core/src/commit.rs | 33 +++++++++- git-cliff-core/src/release.rs | 2 + website/docs/templating/context.md | 6 +- 12 files changed, 349 insertions(+), 4 deletions(-) create mode 100644 .github/fixtures/test-conventional-commit/cliff.toml create mode 100755 .github/fixtures/test-conventional-commit/commit.sh create mode 100644 .github/fixtures/test-conventional-commit/expected.md create mode 100644 .github/fixtures/test-from-context-does-not-discard-fields/cliff.toml create mode 100755 .github/fixtures/test-from-context-does-not-discard-fields/commit.sh create mode 100644 .github/fixtures/test-from-context-does-not-discard-fields/context.json create mode 100644 .github/fixtures/test-from-context-does-not-discard-fields/expected.md create mode 100644 check.md diff --git a/.github/fixtures/test-conventional-commit/cliff.toml b/.github/fixtures/test-conventional-commit/cliff.toml new file mode 100644 index 0000000000..847a4cc86a --- /dev/null +++ b/.github/fixtures/test-conventional-commit/cliff.toml @@ -0,0 +1,35 @@ +[changelog] +# template for the changelog footer +header = """ +# Changelog\n +All notable changes to this project will be documented in this file. +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% for group, commits in commits | group_by(attribute="group") %} + ## {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**]: {{ commit.breaking_description }}{% endif %} + {{ commit.message }}: {{ commit.body }}\ + {% for footer in commit.footers %} + - {{ footer.token }}{{ footer.separator }} {{ footer.value }}\ + {% endfor %} + {% endfor %}\ +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# process each line of a commit as an individual commit +split_commits = false +commit_parsers = [ + { message = "^feat", group = "Features", default_scope = "app" }, + { message = "^fix" }, +] diff --git a/.github/fixtures/test-conventional-commit/commit.sh b/.github/fixtures/test-conventional-commit/commit.sh new file mode 100755 index 0000000000..b55c2524a3 --- /dev/null +++ b/.github/fixtures/test-conventional-commit/commit.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e + +GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit" +GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m \ + "feat(web): feature 1, breaking change in footer + +Body feature 1 + +BREAKING CHANGE: breaking change description feature 1 +Signed-off-by: user1 +Reviewed-by: user2 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m \ + "feat(web)!: feature 2, breaking chain in description + +Body feature 2 + +Signed-off-by: user3 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m \ + "feat!: feature 3, use default scope = app + +Body feature 2 + +Signed-off-by: user3 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m \ + "fix(scope): fix 1, use scope as group + +Body fix 1 + +Fix: #1 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m \ + "fix(front-end): fix 2, no footer + +Body fix 2 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:14" git commit --allow-empty -m \ + "fix(front-end): fix 3 and 4, no body but footer + +Fix: #3 +Fix: #4 +" + +git tag v0.1.0 diff --git a/.github/fixtures/test-conventional-commit/expected.md b/.github/fixtures/test-conventional-commit/expected.md new file mode 100644 index 0000000000..aefd3fa0c5 --- /dev/null +++ b/.github/fixtures/test-conventional-commit/expected.md @@ -0,0 +1,35 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## Features + +*(web)* [**breaking**]: breaking change description feature 1 +feature 1, breaking change in footer: Body feature 1 +- BREAKING CHANGE: breaking change description feature 1 +- Signed-off-by: user1 +- Reviewed-by: user2 + +*(web)* [**breaking**]: feature 2, breaking chain in description +feature 2, breaking chain in description: Body feature 2 +- Signed-off-by: user3 + +*(app)* [**breaking**]: feature 3, use default scope = app +feature 3, use default scope = app: Body feature 2 +- Signed-off-by: user3 + +## Fix + +*(scope)* +fix 1, use scope as group: Body fix 1 +- Fix: #1 + +*(front-end)* +fix 2, no footer: Body fix 2 + +*(front-end)* +fix 3 and 4, no body but footer: +- Fix: #3 +- Fix: #4 + + diff --git a/.github/fixtures/test-from-context-does-not-discard-fields/cliff.toml b/.github/fixtures/test-from-context-does-not-discard-fields/cliff.toml new file mode 100644 index 0000000000..847a4cc86a --- /dev/null +++ b/.github/fixtures/test-from-context-does-not-discard-fields/cliff.toml @@ -0,0 +1,35 @@ +[changelog] +# template for the changelog footer +header = """ +# Changelog\n +All notable changes to this project will be documented in this file. +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% for group, commits in commits | group_by(attribute="group") %} + ## {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**]: {{ commit.breaking_description }}{% endif %} + {{ commit.message }}: {{ commit.body }}\ + {% for footer in commit.footers %} + - {{ footer.token }}{{ footer.separator }} {{ footer.value }}\ + {% endfor %} + {% endfor %}\ +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# process each line of a commit as an individual commit +split_commits = false +commit_parsers = [ + { message = "^feat", group = "Features", default_scope = "app" }, + { message = "^fix" }, +] diff --git a/.github/fixtures/test-from-context-does-not-discard-fields/commit.sh b/.github/fixtures/test-from-context-does-not-discard-fields/commit.sh new file mode 100755 index 0000000000..b55c2524a3 --- /dev/null +++ b/.github/fixtures/test-from-context-does-not-discard-fields/commit.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e + +GIT_COMMITTER_DATE="2022-04-06 01:25:08" git commit --allow-empty -m "Initial commit" +GIT_COMMITTER_DATE="2022-04-06 01:25:09" git commit --allow-empty -m \ + "feat(web): feature 1, breaking change in footer + +Body feature 1 + +BREAKING CHANGE: breaking change description feature 1 +Signed-off-by: user1 +Reviewed-by: user2 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m \ + "feat(web)!: feature 2, breaking chain in description + +Body feature 2 + +Signed-off-by: user3 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m \ + "feat!: feature 3, use default scope = app + +Body feature 2 + +Signed-off-by: user3 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m \ + "fix(scope): fix 1, use scope as group + +Body fix 1 + +Fix: #1 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty -m \ + "fix(front-end): fix 2, no footer + +Body fix 2 +" + +GIT_COMMITTER_DATE="2022-04-06 01:25:14" git commit --allow-empty -m \ + "fix(front-end): fix 3 and 4, no body but footer + +Fix: #3 +Fix: #4 +" + +git tag v0.1.0 diff --git a/.github/fixtures/test-from-context-does-not-discard-fields/context.json b/.github/fixtures/test-from-context-does-not-discard-fields/context.json new file mode 100644 index 0000000000..0c20fdd909 --- /dev/null +++ b/.github/fixtures/test-from-context-does-not-discard-fields/context.json @@ -0,0 +1 @@ +[{"version":"v0.1.0","message":null,"commits":[{"id":"1f276e5b2263e7c1b5524ea245fbb4ff61330381","raw_message":"feat(web): feature 1, breaking change in footer\n\nBody feature 1\n\nBREAKING CHANGE: breaking change description feature 1\nSigned-off-by: user1 \nReviewed-by: user2","message":"feature 1, breaking change in footer","body":"Body feature 1","footers":[{"token":"BREAKING CHANGE","separator":":","value":"breaking change description feature 1","breaking":true},{"token":"Signed-off-by","separator":":","value":"user1 ","breaking":false},{"token":"Reviewed-by","separator":":","value":"user2","breaking":false}],"group":"Features","breaking_description":"breaking change description feature 1","breaking":true,"scope":"web","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175909},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}},{"id":"221fe9a8a564ffd1a9eae5fdae7a9c29b08dac83","raw_message":"feat(web)!: feature 2, breaking chain in description\n\nBody feature 2\n\nSigned-off-by: user3 ","message":"feature 2, breaking chain in description","body":"Body feature 2","footers":[{"token":"Signed-off-by","separator":":","value":"user3 ","breaking":false}],"group":"Features","breaking_description":"feature 2, breaking chain in description","breaking":true,"scope":"web","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175910},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}},{"id":"2c21dc3af7d88301d9f9009b7f952e3900d3919b","raw_message":"feat!: feature 3, use default scope = app\n\nBody feature 2\n\nSigned-off-by: user3 ","message":"feature 3, use default scope = app","body":"Body feature 2","footers":[{"token":"Signed-off-by","separator":":","value":"user3 ","breaking":false}],"group":"Features","breaking_description":"feature 3, use default scope = app","breaking":true,"scope":"app","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175911},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}},{"id":"ae3a44176f1b7491219d6cff66c05e06e464dade","raw_message":"fix(scope): fix 1, use scope as group\n\nBody fix 1\n\nFix: #1","message":"fix 1, use scope as group","body":"Body fix 1","footers":[{"token":"Fix","separator":":","value":"#1","breaking":false}],"group":"fix","breaking_description":null,"breaking":false,"scope":"scope","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175912},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}},{"id":"a9ed3d05585d4ae14bb5149c37f68a91e26175d6","raw_message":"fix(front-end): fix 2, no footer\n\nBody fix 2","message":"fix 2, no footer","body":"Body fix 2","footers":[],"group":"fix","breaking_description":null,"breaking":false,"scope":"front-end","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175913},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}},{"id":"71b8c16222328604b7e8940f4fcd3c8b07554f08","raw_message":"fix(front-end): fix 3 and 4, no body but footer\n\nFix: #3\nFix: #4","message":"fix 3 and 4, no body but footer","body":null,"footers":[{"token":"Fix","separator":":","value":"#3","breaking":false},{"token":"Fix","separator":":","value":"#4","breaking":false}],"group":"fix","breaking_description":null,"breaking":false,"scope":"front-end","links":[],"author":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1728820879},"committer":{"name":"github-actions[bot]","email":"github-actions[bot]@users.noreply.github.com","timestamp":1649175914},"conventional":true,"merge_commit":false,"extra":null,"github":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitlab":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"gitea":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false},"bitbucket":{"username":null,"pr_title":null,"pr_number":null,"pr_labels":[],"is_first_time":false}}],"commit_id":"71b8c16222328604b7e8940f4fcd3c8b07554f08","timestamp":1649175914,"previous":{"version":null,"message":null,"commits":[],"commit_id":null,"timestamp":0,"previous":null,"repository":null,"extra":null,"github":{"contributors":[]},"gitlab":{"contributors":[]},"gitea":{"contributors":[]},"bitbucket":{"contributors":[]}},"repository":"/home/dqk/workspace/rust/contrib/issue-837/test-from-context-does-not-discard-fields","extra":null,"github":{"contributors":[]},"gitlab":{"contributors":[]},"gitea":{"contributors":[]},"bitbucket":{"contributors":[]}}] diff --git a/.github/fixtures/test-from-context-does-not-discard-fields/expected.md b/.github/fixtures/test-from-context-does-not-discard-fields/expected.md new file mode 100644 index 0000000000..aefd3fa0c5 --- /dev/null +++ b/.github/fixtures/test-from-context-does-not-discard-fields/expected.md @@ -0,0 +1,35 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## Features + +*(web)* [**breaking**]: breaking change description feature 1 +feature 1, breaking change in footer: Body feature 1 +- BREAKING CHANGE: breaking change description feature 1 +- Signed-off-by: user1 +- Reviewed-by: user2 + +*(web)* [**breaking**]: feature 2, breaking chain in description +feature 2, breaking chain in description: Body feature 2 +- Signed-off-by: user3 + +*(app)* [**breaking**]: feature 3, use default scope = app +feature 3, use default scope = app: Body feature 2 +- Signed-off-by: user3 + +## Fix + +*(scope)* +fix 1, use scope as group: Body fix 1 +- Fix: #1 + +*(front-end)* +fix 2, no footer: Body fix 2 + +*(front-end)* +fix 3 and 4, no body but footer: +- Fix: #3 +- Fix: #4 + + diff --git a/.github/workflows/test-fixtures.yml b/.github/workflows/test-fixtures.yml index 6512af6280..09e7d138d8 100644 --- a/.github/workflows/test-fixtures.yml +++ b/.github/workflows/test-fixtures.yml @@ -33,6 +33,7 @@ jobs: command: --latest - fixtures-name: test-commit-footers - fixtures-name: test-commit-preprocessors + - fixtures-name: test-conventional-commit - fixtures-name: test-custom-scope - fixtures-name: test-limit-commits - fixtures-name: test-skip-breaking-changes @@ -93,6 +94,8 @@ jobs: command: --bump --unreleased --with-tag-message "Some text" - fixtures-name: test-from-context command: --from-context context.json + - fixtures-name: test-from-context-does-not-discard-fields + command: --from-context context.json - fixtures-name: test-always-render-unreleased command: --unreleased - fixtures-name: test-always-render diff --git a/check.md b/check.md new file mode 100644 index 0000000000..4d1b639692 --- /dev/null +++ b/check.md @@ -0,0 +1,64 @@ +Initialized empty Git repository in /tmp/tmp.6oNPOAnyCc/.git/ +[main (root-commit) ee8f28d] Initial commit +[main 6b565e6] feat(web): feature, breaking change in footer +[main 381ec3b] feat(web)!: feature 2, breaking chain in description +[main 0c79b49] feat!: feature 3, use default scope +[main 4cae9b0] fix(scope): fix 1, use scope as group +[main 5fc71a3] fix(front-end): fix 2, no footer + +---Run git-cliff--- +# Changelog + +All notable changes to this project will be documented in this file. + +## Features + +- *(web)* + +Feature, breaking change in footer + + +- BREAKING_CHANGE: breaking change description feature 1 + +- Signed-off-by: user1 + +- Reviewed-by: user2 + + +- *(web)* [**breaking**]: feature 2, breaking chain in description + +Feature 2, breaking chain in description + + +- Signed-off-by: user3 + + +- *(app)* [**breaking**]: feature 3, use default scope + +Feature 3, use default scope + + +- Signed-off-by: user3 + + + +## Fix + +- *(scope)* + +Fix 1, use scope as group + + +- Fix: #1 + + +- *(front-end)* + +Fix 2, no footer + + +- Fix: #2 + + + + diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index 0f349efa36..4109f9ebf4 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -28,6 +28,7 @@ use serde::ser::{ }; use serde::{ Deserialize, + Deserializer, Serialize, }; use serde_json::value::Value; @@ -143,6 +144,10 @@ pub struct Commit<'a> { #[cfg(feature = "bitbucket")] #[deprecated(note = "Use `remote` field instead")] pub bitbucket: crate::contributor::RemoteContributor, + + /// Message of the normal commit, to avoid lossly message conversion between + /// conventional commit + pub raw_message: Option, } impl<'a> From for Commit<'a> { @@ -191,6 +196,11 @@ impl Commit<'_> { } } + /// Get raw commit message uses for converting to conventional commit + pub fn raw_message(&self) -> &str { + self.raw_message.as_deref().unwrap_or(&self.message) + } + /// Processes the commit. /// /// * converts commit to a conventional commit @@ -226,7 +236,7 @@ impl Commit<'_> { /// Returns the commit with its conventional type set. pub fn into_conventional(mut self) -> Result { match ConventionalCommit::parse(Box::leak( - self.message.to_string().into_boxed_str(), + self.raw_message().to_string().into_boxed_str(), )) { Ok(conv) => { self.conv = Some(conv); @@ -423,8 +433,9 @@ impl Serialize for Commit<'_> { } } - let mut commit = serializer.serialize_struct("Commit", 9)?; + let mut commit = serializer.serialize_struct("Commit", 20)?; commit.serialize_field("id", &self.id)?; + commit.serialize_field("raw_message", &self.raw_message())?; if let Some(conv) = &self.conv { commit.serialize_field("message", conv.description())?; commit.serialize_field("body", &conv.body())?; @@ -476,6 +487,24 @@ impl Serialize for Commit<'_> { } } +/// Deserialize commits into conventional commits if they are convertible. +/// +/// Serialized commits cannot be deserialized into commits that have +/// [`Commit::conv`]. Thus, we need to manually convert them using +/// [`Commit::into_conventional`]. +/// +/// This function is only used in [`crate::release::Release::commits`]. +pub(crate) fn commits_to_conventional_commits<'de, 'a, D: Deserializer<'de>>( + deserializer: D, +) -> std::result::Result>, D::Error> { + let commits = Vec::>::deserialize(deserializer)?; + let commits = commits + .into_iter() + .map(|commit| commit.clone().into_conventional().unwrap_or(commit)) + .collect(); + Ok(commits) +} + #[cfg(test)] mod test { use super::*; diff --git a/git-cliff-core/src/release.rs b/git-cliff-core/src/release.rs index fec293f51f..dbf1d64568 100644 --- a/git-cliff-core/src/release.rs +++ b/git-cliff-core/src/release.rs @@ -1,3 +1,4 @@ +use crate::commit::commits_to_conventional_commits; use crate::error::Result; use crate::{ commit::Commit, @@ -34,6 +35,7 @@ pub struct Release<'a> { /// git tag's message. pub message: Option, /// Commits made for the release. + #[serde(deserialize_with = "commits_to_conventional_commits")] pub commits: Vec>, /// Commit ID of the tag. #[serde(rename = "commit_id")] diff --git a/website/docs/templating/context.md b/website/docs/templating/context.md index 8fe863a4ed..a2d53c7931 100644 --- a/website/docs/templating/context.md +++ b/website/docs/templating/context.md @@ -57,7 +57,8 @@ following context is generated to use for templating: "name": "User Name", "email": "user.email@example.com", "timestamp": 1660330071 - } + }, + "raw_message": "[scope]: \n[body]\n[footer(s)]" } ], "commit_id": "a440c6eb26404be4877b7e3ad592bfaa5d4eb210 (release commit)", @@ -152,7 +153,8 @@ If [`conventional_commits`](/docs/configuration/git#conventional_commits) is set "name": "User Name", "email": "user.email@example.com", "timestamp": 1660330071 - } + }, + "raw_message": "(full commit message including description, footers, etc.)" } ], "commit_id": "a440c6eb26404be4877b7e3ad592bfaa5d4eb210 (release commit)",