From 7cca889f87a27be3e5ad73c1b5ec808302180798 Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Fri, 12 Jul 2024 14:38:14 +0200 Subject: [PATCH 1/7] fix: reorder execution to have context poplulated before parsing/pruning --- git-cliff-core/src/changelog.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index c6a84d9986..e960fc56cb 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -62,9 +62,9 @@ impl<'a> Changelog<'a> { config, additional_context: HashMap::new(), }; + changelog.add_remote_data()?; changelog.process_commits(); changelog.process_releases(); - changelog.add_remote_data()?; Ok(changelog) } @@ -104,6 +104,13 @@ impl<'a> Changelog<'a> { } } + // fn load_commits(&mut self) { + // debug!("Loading commits..."); + // self.releases.iter_mut().for_each(|release| { + + // }) + // } + /// Processes the commits and omits the ones that doesn't match the /// criteria set by configuration file. fn process_commits(&mut self) { @@ -425,16 +432,14 @@ impl<'a> Changelog<'a> { serde_json::to_value(self.config.remote.clone())?, ); #[cfg(feature = "github")] - let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() - { + let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() { self.get_github_metadata() .expect("Could not get github metadata") } else { (vec![], vec![]) }; #[cfg(feature = "gitlab")] - let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() - { + let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() { self.get_gitlab_metadata() .expect("Could not get gitlab metadata") } else { From 7db25f8ec7c052621b75fcf1892654e54bcc2a59 Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Fri, 12 Jul 2024 14:38:37 +0200 Subject: [PATCH 2/7] feat: allow field to read from context object --- git-cliff-core/src/changelog.rs | 7 ------- git-cliff-core/src/commit.rs | 31 +++++++++++++++---------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index e960fc56cb..d12d9a900d 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -104,13 +104,6 @@ impl<'a> Changelog<'a> { } } - // fn load_commits(&mut self) { - // debug!("Loading commits..."); - // self.releases.iter_mut().for_each(|release| { - - // }) - // } - /// Processes the commits and omits the ones that doesn't match the /// criteria set by configuration file. fn process_commits(&mut self) { diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index f60928492f..c71b0bb38e 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -264,6 +264,13 @@ impl Commit<'_> { protect_breaking: bool, filter: bool, ) -> Result { + let lookup_context = serde_json::to_value(&self).map_err(|e| { + AppError::FieldError(format!( + "failed to convert context into value: {}", + e + )) + })?; + for parser in parsers { let mut regex_checks = Vec::new(); if let Some(message_regex) = parser.message.as_ref() { @@ -287,25 +294,17 @@ impl Commit<'_> { if let (Some(field_name), Some(pattern_regex)) = (parser.field.as_ref(), parser.pattern.as_ref()) { - regex_checks.push(( - pattern_regex, - match field_name.as_str() { - "id" => Some(self.id.clone()), - "message" => Some(self.message.clone()), - "body" => body, - "author.name" => self.author.name.clone(), - "author.email" => self.author.email.clone(), - "committer.name" => self.committer.name.clone(), - "committer.email" => self.committer.email.clone(), - _ => None, + match tera::dotted_pointer(&lookup_context, field_name) { + Some(value) => { + regex_checks.push((pattern_regex, value.to_string())); } - .ok_or_else(|| { - AppError::FieldError(format!( + None => { + return Err(AppError::FieldError(format!( "field {} does not have a value", field_name - )) - })?, - )); + ))); + } + } } if parser.sha.clone().map(|v| v.to_lowercase()).as_deref() == Some(&self.id) From 32da6d390394c0907317b0da0b8f4d2c72ec2fa7 Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Tue, 30 Jul 2024 02:15:54 +0200 Subject: [PATCH 3/7] fix: add tests --- git-cliff-core/src/changelog.rs | 6 ++-- git-cliff-core/src/commit.rs | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/git-cliff-core/src/changelog.rs b/git-cliff-core/src/changelog.rs index d12d9a900d..3b2bc8da39 100644 --- a/git-cliff-core/src/changelog.rs +++ b/git-cliff-core/src/changelog.rs @@ -425,14 +425,16 @@ impl<'a> Changelog<'a> { serde_json::to_value(self.config.remote.clone())?, ); #[cfg(feature = "github")] - let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() { + let (github_commits, github_pull_requests) = if self.config.remote.github.is_set() + { self.get_github_metadata() .expect("Could not get github metadata") } else { (vec![], vec![]) }; #[cfg(feature = "gitlab")] - let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() { + let (gitlab_commits, gitlab_merge_request) = if self.config.remote.gitlab.is_set() + { self.get_gitlab_metadata() .expect("Could not get gitlab metadata") } else { diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index c71b0bb38e..9db12ce746 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -727,4 +727,55 @@ mod test { Ok(()) } + + #[test] + fn field_name_regex() -> Result<()> { + let commit = Commit { + message: String::from("feat: do something"), + author: Signature { + name: Some("John Doe".to_string()), + email: None, + timestamp: 0x0, + }, + ..Default::default() + }; + let parsed_commit = commit.clone().parse( + &[CommitParser { + sha: None, + message: None, + body: None, + footer: None, + group: Some(String::from("Test group")), + default_scope: None, + scope: None, + skip: None, + field: Some(String::from("author.name")), + pattern: Regex::new("Something else").ok(), + }], + false, + true, + ); + + assert!(parsed_commit.is_err()); + + let parsed_commit = commit.parse( + &[CommitParser { + sha: None, + message: None, + body: None, + footer: None, + group: Some(String::from("Test group")), + default_scope: None, + scope: None, + skip: None, + field: Some(String::from("author.name")), + pattern: Regex::new("John Doe").ok(), + }], + false, + false, + )?; + + assert_eq!(Some(String::from("Test group")), parsed_commit.group); + Ok(()) + } } From 208f0b9adfe55e343b8d5eb98136b520a3c8b751 Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Tue, 6 Aug 2024 03:20:27 +0200 Subject: [PATCH 4/7] fix: add legacy fix for body field --- git-cliff-core/src/commit.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index 9db12ce746..e93bcbe601 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -294,9 +294,15 @@ impl Commit<'_> { if let (Some(field_name), Some(pattern_regex)) = (parser.field.as_ref(), parser.pattern.as_ref()) { - match tera::dotted_pointer(&lookup_context, field_name) { + let value = if field_name == "body" { + body.clone() + } else { + tera::dotted_pointer(&lookup_context, field_name) + .map(|v| v.to_string()) + }; + match value { Some(value) => { - regex_checks.push((pattern_regex, value.to_string())); + regex_checks.push((pattern_regex, value)); } None => { return Err(AppError::FieldError(format!( From 4741ae0d470507c5f34805620591b662679ff5da Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Tue, 6 Aug 2024 03:40:00 +0200 Subject: [PATCH 5/7] test: add fixture --- .../test-regex-label-grouping/cliff.toml | 25 +++++++++++++++++++ .../test-regex-label-grouping/commit.sh | 10 ++++++++ .../test-regex-label-grouping/expected.md | 19 ++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 .github/fixtures/test-regex-label-grouping/cliff.toml create mode 100755 .github/fixtures/test-regex-label-grouping/commit.sh create mode 100644 .github/fixtures/test-regex-label-grouping/expected.md diff --git a/.github/fixtures/test-regex-label-grouping/cliff.toml b/.github/fixtures/test-regex-label-grouping/cliff.toml new file mode 100644 index 0000000000..49ce37f2a1 --- /dev/null +++ b/.github/fixtures/test-regex-label-grouping/cliff.toml @@ -0,0 +1,25 @@ +[changelog] +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +body = """ +### What's changed +{% for group, commits in commits | group_by(attribute="group") %} + #### {{ group }} + {% for commit in commits -%} + - {{ commit.message }} + {% endfor -%}\n +{% endfor %}\n +""" +footer = """ + +""" +trim = true + +[git] +commit_parsers = [ + { field = "author.name", pattern = "testa", group = "TEST A" }, + { field = "author.name", pattern = "testb", group = "TEST B" }, + { field = "author.name", pattern = "testc", group = "TEST C" }, +] diff --git a/.github/fixtures/test-regex-label-grouping/commit.sh b/.github/fixtures/test-regex-label-grouping/commit.sh new file mode 100755 index 0000000000..3a190fd5b4 --- /dev/null +++ b/.github/fixtures/test-regex-label-grouping/commit.sh @@ -0,0 +1,10 @@ +#!/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 --author="testa " -m "feat: add feature 1" +GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty --author="testa " -m "feat: add feature 2" +GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty --author="testb " -m "feat: add feature 3" +GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty --author="testb " -m "feat: add feature 4" +GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty --author="testc " -m "feat: add feature 5" +GIT_COMMITTER_DATE="2022-04-06 01:25:14" git commit --allow-empty --author="testc " -m "feat: add feature 6" diff --git a/.github/fixtures/test-regex-label-grouping/expected.md b/.github/fixtures/test-regex-label-grouping/expected.md new file mode 100644 index 0000000000..9320ce2f94 --- /dev/null +++ b/.github/fixtures/test-regex-label-grouping/expected.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +### What's changed + +#### TEST A +- add feature 1 +- add feature 2 + +#### TEST B +- add feature 3 +- add feature 4 + +#### TEST C +- add feature 5 +- add feature 6 + + \ No newline at end of file From 134a4cdfffd785065ae1ec9e3573a76860be9280 Mon Sep 17 00:00:00 2001 From: Jan Max Tiedemann Date: Tue, 6 Aug 2024 03:51:52 +0200 Subject: [PATCH 6/7] docs: update documentation --- website/docs/configuration/git.md | 7 +++++-- website/docs/tips-and-tricks.md | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/website/docs/configuration/git.md b/website/docs/configuration/git.md index 51dadd5e64..f468466d2e 100644 --- a/website/docs/configuration/git.md +++ b/website/docs/configuration/git.md @@ -165,14 +165,17 @@ Examples: - `{ sha = "f6f2472bdf0bbb5f9fcaf2d72c1fa9f98f772bb2", group = "Stuff" }` - Set the group of the commit by using its SHA1. - `{ field = "author.name", pattern = "John Doe", group = "John's stuff" }` - - If the author's name attribute of the commit matches the pattern "John Doe" (as a regex), override the scope with "John' stuff". Supported commit attributes are: + - If the author's name attribute of the commit matches the pattern "John Doe" (as a regex), override the scope with "John' stuff". + - All values that are part of the commit context can be used. Nested fields can be accessed via the [dot notation](https://keats.github.io/tera/docs/#dot-notation). Some commonly used ones are: - `id` - `message` - - `body` - `author.name` - `author.email` - `committer.email` - `committer.name` + - `body` is a special field which contains the body of a convetional commit, if applicable. + - Be aware that all fields are converted to JSON strings before they are parsed by the given regex, especially when dealing with arrays. + ### protect_breaking_commits diff --git a/website/docs/tips-and-tricks.md b/website/docs/tips-and-tricks.md index 5c135927e5..d2cfe5b52b 100644 --- a/website/docs/tips-and-tricks.md +++ b/website/docs/tips-and-tricks.md @@ -69,3 +69,18 @@ commit_parsers = [ { body = "$^", skip = true }, ] ``` + +## Use Github PR labels as groups + +```toml +[git] +commit_parsers = [ + { field = "github.pr_labels", pattern = "breaking-change", group = "๐Ÿ—๏ธ Breaking changes" }, + { field = "github.pr_labels", pattern = "type/enhancement", group = "๐Ÿš€ Features" }, + { field = "github.pr_labels", pattern = "type/bug", group = "๐Ÿ› Fixes" }, + { field = "github.pr_labels", pattern = "type/update", group = "๐Ÿงช Dependencies" }, + { field = "github.pr_labels", pattern = "type/refactor", group = "๐Ÿญ Refactor" }, + { field = "github.pr_labels", pattern = "area/documentation", group = "๐Ÿ“ Documentation" }, + { field = "github.pr_labels", pattern = ".*", group = "๐ŸŒ€ Miscellaneous" }, +] +``` From 2fd2d6a5e6e335bbd675b89079a18a4ffd8e5958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Thu, 8 Aug 2024 22:21:15 +0300 Subject: [PATCH 7/7] refactor: clean up implementation --- git-cliff-core/src/commit.rs | 7 ++----- git-cliff-core/src/error.rs | 2 +- website/docs/configuration/git.md | 2 +- website/docs/tips-and-tricks.md | 32 +++++++++++++++---------------- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/git-cliff-core/src/commit.rs b/git-cliff-core/src/commit.rs index e93bcbe601..047477250a 100644 --- a/git-cliff-core/src/commit.rs +++ b/git-cliff-core/src/commit.rs @@ -266,11 +266,9 @@ impl Commit<'_> { ) -> Result { let lookup_context = serde_json::to_value(&self).map_err(|e| { AppError::FieldError(format!( - "failed to convert context into value: {}", - e + "failed to convert context into value: {e}", )) })?; - for parser in parsers { let mut regex_checks = Vec::new(); if let Some(message_regex) = parser.message.as_ref() { @@ -306,8 +304,7 @@ impl Commit<'_> { } None => { return Err(AppError::FieldError(format!( - "field {} does not have a value", - field_name + "field {field_name} does not have a value", ))); } } diff --git a/git-cliff-core/src/error.rs b/git-cliff-core/src/error.rs index 5f96b589d3..c9342fce64 100644 --- a/git-cliff-core/src/error.rs +++ b/git-cliff-core/src/error.rs @@ -68,7 +68,7 @@ pub enum Error { #[error("Failed to parse integer: `{0}`")] IntParseError(#[from] std::num::TryFromIntError), /// Error that may occur while processing parsers that define field and - /// value matchers + /// value matchers. #[error("Field error: `{0}`")] FieldError(String), /// Error that may occur while parsing a SemVer version or version diff --git a/website/docs/configuration/git.md b/website/docs/configuration/git.md index f468466d2e..3c3722c490 100644 --- a/website/docs/configuration/git.md +++ b/website/docs/configuration/git.md @@ -165,7 +165,7 @@ Examples: - `{ sha = "f6f2472bdf0bbb5f9fcaf2d72c1fa9f98f772bb2", group = "Stuff" }` - Set the group of the commit by using its SHA1. - `{ field = "author.name", pattern = "John Doe", group = "John's stuff" }` - - If the author's name attribute of the commit matches the pattern "John Doe" (as a regex), override the scope with "John' stuff". + - If the author's name attribute of the commit matches the pattern "John Doe" (as a regex), override the scope with "John's stuff". - All values that are part of the commit context can be used. Nested fields can be accessed via the [dot notation](https://keats.github.io/tera/docs/#dot-notation). Some commonly used ones are: - `id` - `message` diff --git a/website/docs/tips-and-tricks.md b/website/docs/tips-and-tricks.md index d2cfe5b52b..693bcc2b32 100644 --- a/website/docs/tips-and-tricks.md +++ b/website/docs/tips-and-tricks.md @@ -43,14 +43,6 @@ Then strip the tags in the template with the series of filters: {% for group, commits in commits | filter(attribute="merge_commit", value=false) %} ``` -## Skip commits by PR label - -```jinja2 -{% if commit.github.pr_labels is containing("skip-release-notes") %} - {% continue %} -{% endif %} -``` - ## Remove gitmoji ```toml @@ -70,17 +62,25 @@ commit_parsers = [ ] ``` -## Use Github PR labels as groups +## Skip commits by GitHub PR label + +```jinja2 +{% if commit.github.pr_labels is containing("skip-release-notes") %} + {% continue %} +{% endif %} +``` + +## Use GitHub PR labels as groups ```toml [git] commit_parsers = [ - { field = "github.pr_labels", pattern = "breaking-change", group = "๐Ÿ—๏ธ Breaking changes" }, - { field = "github.pr_labels", pattern = "type/enhancement", group = "๐Ÿš€ Features" }, - { field = "github.pr_labels", pattern = "type/bug", group = "๐Ÿ› Fixes" }, - { field = "github.pr_labels", pattern = "type/update", group = "๐Ÿงช Dependencies" }, - { field = "github.pr_labels", pattern = "type/refactor", group = "๐Ÿญ Refactor" }, - { field = "github.pr_labels", pattern = "area/documentation", group = "๐Ÿ“ Documentation" }, - { field = "github.pr_labels", pattern = ".*", group = "๐ŸŒ€ Miscellaneous" }, + { field = "github.pr_labels", pattern = "breaking-change", group = " ๐Ÿ—๏ธ Breaking changes" }, + { field = "github.pr_labels", pattern = "type/enhancement", group = " ๐Ÿš€ Features" }, + { field = "github.pr_labels", pattern = "type/bug", group = " ๐Ÿ› Fixes" }, + { field = "github.pr_labels", pattern = "type/update", group = " ๐Ÿงช Dependencies" }, + { field = "github.pr_labels", pattern = "type/refactor", group = " ๐Ÿญ Refactor" }, + { field = "github.pr_labels", pattern = "area/documentation", group = " ๐Ÿ“ Documentation" }, + { field = "github.pr_labels", pattern = ".*", group = " ๐ŸŒ€ Miscellaneous" }, ] ```