Skip to content

Commit

Permalink
feat(parser): allow whole commit context to be used in commit parsers (
Browse files Browse the repository at this point in the history
…#758)

* fix: reorder execution to have context poplulated before parsing/pruning

* feat: allow field to read from context object

* fix: add tests

* fix: add legacy fix for body field

* test: add fixture

* docs: update documentation

* refactor: clean up implementation

---------

Co-authored-by: Orhun Parmaksız <[email protected]>
  • Loading branch information
DerTiedemann and orhun authored Aug 8, 2024
1 parent 85bfa95 commit ccf2ab7
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 30 deletions.
25 changes: 25 additions & 0 deletions .github/fixtures/test-regex-label-grouping/cliff.toml
Original file line number Diff line number Diff line change
@@ -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 = """
<!-- generated by git-cliff -->
"""
trim = true

[git]
commit_parsers = [
{ field = "author.name", pattern = "testa", group = "<!-- 0 -->TEST A" },
{ field = "author.name", pattern = "testb", group = "<!-- 1 -->TEST B" },
{ field = "author.name", pattern = "testc", group = "<!-- 2 -->TEST C" },
]
10 changes: 10 additions & 0 deletions .github/fixtures/test-regex-label-grouping/commit.sh
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>" -m "feat: add feature 1"
GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty --author="testa <[email protected]>" -m "feat: add feature 2"
GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty --author="testb <[email protected]>" -m "feat: add feature 3"
GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty --author="testb <[email protected]>" -m "feat: add feature 4"
GIT_COMMITTER_DATE="2022-04-06 01:25:13" git commit --allow-empty --author="testc <[email protected]>" -m "feat: add feature 5"
GIT_COMMITTER_DATE="2022-04-06 01:25:14" git commit --allow-empty --author="testc <[email protected]>" -m "feat: add feature 6"
19 changes: 19 additions & 0 deletions .github/fixtures/test-regex-label-grouping/expected.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Changelog

All notable changes to this project will be documented in this file.

### What's changed

#### <!-- 0 -->TEST A
- add feature 1
- add feature 2

#### <!-- 1 -->TEST B
- add feature 3
- add feature 4

#### <!-- 2 -->TEST C
- add feature 5
- add feature 6

<!-- generated by git-cliff -->
2 changes: 1 addition & 1 deletion git-cliff-core/src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
89 changes: 71 additions & 18 deletions git-cliff-core/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ impl Commit<'_> {
protect_breaking: bool,
filter: bool,
) -> Result<Self> {
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() {
Expand All @@ -287,25 +292,22 @@ 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,
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));
}
.ok_or_else(|| {
AppError::FieldError(format!(
"field {} does not have a value",
field_name
))
})?,
));
None => {
return Err(AppError::FieldError(format!(
"field {field_name} does not have a value",
)));
}
}
}
if parser.sha.clone().map(|v| v.to_lowercase()).as_deref() ==
Some(&self.id)
Expand Down Expand Up @@ -728,4 +730,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(())
}
}
2 changes: 1 addition & 1 deletion git-cliff-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 5 additions & 2 deletions website/docs/configuration/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'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`
- `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

Expand Down
31 changes: 23 additions & 8 deletions website/docs/tips-and-tricks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -69,3 +61,26 @@ commit_parsers = [
{ body = "$^", skip = true },
]
```

## 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 = "<!-- 0 --> 🏗️ Breaking changes" },
{ field = "github.pr_labels", pattern = "type/enhancement", group = "<!-- 1 --> 🚀 Features" },
{ field = "github.pr_labels", pattern = "type/bug", group = "<!-- 2 --> 🐛 Fixes" },
{ field = "github.pr_labels", pattern = "type/update", group = "<!-- 3 --> 🧪 Dependencies" },
{ field = "github.pr_labels", pattern = "type/refactor", group = "<!-- 4 --> 🏭 Refactor" },
{ field = "github.pr_labels", pattern = "area/documentation", group = "<!-- 5 --> 📝 Documentation" },
{ field = "github.pr_labels", pattern = ".*", group = "<!-- 6 --> 🌀 Miscellaneous" },
]
```

0 comments on commit ccf2ab7

Please sign in to comment.