Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): add field and value matchers to the commit parser #312

Merged
merged 8 commits into from
Oct 15, 2023
14 changes: 14 additions & 0 deletions git-cliff-core/src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ mod test {
default_scope: None,
scope: None,
skip: Some(true),
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("feat*").ok(),
Expand All @@ -246,6 +248,8 @@ mod test {
default_scope: Some(String::from("other")),
scope: None,
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("^fix*").ok(),
Expand All @@ -254,6 +258,8 @@ mod test {
default_scope: None,
scope: None,
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("doc:").ok(),
Expand All @@ -262,6 +268,8 @@ mod test {
default_scope: None,
scope: Some(String::from("documentation")),
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("docs:").ok(),
Expand All @@ -270,6 +278,8 @@ mod test {
default_scope: None,
scope: Some(String::from("documentation")),
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new(r"match\((.*)\):.*").ok(),
Expand All @@ -278,6 +288,8 @@ mod test {
default_scope: None,
scope: None,
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new(".*").ok(),
Expand All @@ -286,6 +298,8 @@ mod test {
default_scope: Some(String::from("other")),
scope: None,
skip: None,
field: None,
pattern: None,
},
]),
protect_breaking_commits: None,
Expand Down
61 changes: 61 additions & 0 deletions git-cliff-core/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,33 @@ impl Commit<'_> {
) {
regex_checks.push((body_regex, body.to_string()))
}
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" => self
.conv
.as_ref()
.and_then(|v| v.body())
.map(|v| v.to_string()),
"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,
}
.ok_or_else(|| {
AppError::FieldError(format!(
"field {} does not have a value",
field_name
))
})?,
));
}
for (regex, text) in regex_checks {
if regex.is_match(&text) {
if self.skip_commit(parser, protect_breaking) {
Expand Down Expand Up @@ -420,6 +447,8 @@ mod test {
default_scope: Some(String::from("test_scope")),
scope: None,
skip: None,
field: None,
pattern: None,
}],
false,
false,
Expand Down Expand Up @@ -568,4 +597,36 @@ mod test {
Commit::from(String::from("thisisinvalidsha1 style: add formatting"))
);
}

#[test]
fn parse_commit_field() -> Result<()> {
let mut commit = Commit::new(
String::from("8f55e69eba6e6ce811ace32bd84cc82215673cb6"),
String::from("feat: do something"),
);

commit.author = Signature {
name: Some("John Doe".to_string()),
email: None,
timestamp: 0x0,
};

let parsed_commit = commit.parse(
&[CommitParser {
message: None,
body: 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(())
}
}
5 changes: 5 additions & 0 deletions git-cliff-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ pub struct CommitParser {
pub scope: Option<String>,
/// Whether to skip this commit group.
pub skip: Option<bool>,
/// Field name of the commit to match the regex against.
pub field: Option<String>,
/// Regex for matching the field value.
#[serde(with = "serde_regex", default)]
pub pattern: Option<Regex>,
}

/// TextProcessor, e.g. for modifying commit messages.
Expand Down
4 changes: 4 additions & 0 deletions git-cliff-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ pub enum Error {
/// Error that may occur while parsing integers.
#[error("Failed to parse integer: `{0}`")]
IntParseError(#[from] std::num::TryFromIntError),
/// Error that may occur while processing parsers that define field and
/// value matchers
#[error("Field error: `{0}`")]
FieldError(String),
/// Error that may occur while parsing a SemVer version or version
/// requirement.
#[error("Semver error: `{0}`")]
Expand Down
36 changes: 35 additions & 1 deletion git-cliff-core/tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use git_cliff_core::commit::Commit;
use git_cliff_core::commit::{
Commit,
Signature,
};
use git_cliff_core::config::{
ChangelogConfig,
CommitParser,
Expand Down Expand Up @@ -55,6 +58,8 @@ fn generate_changelog() -> Result<()> {
default_scope: None,
scope: None,
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("^fix").ok(),
Expand All @@ -63,6 +68,8 @@ fn generate_changelog() -> Result<()> {
default_scope: None,
scope: None,
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: Regex::new("^test").ok(),
Expand All @@ -71,6 +78,18 @@ fn generate_changelog() -> Result<()> {
default_scope: None,
scope: Some(String::from("tests")),
skip: None,
field: None,
pattern: None,
},
CommitParser {
message: None,
body: None,
group: Some(String::from("docs")),
default_scope: None,
scope: None,
skip: None,
field: Some(String::from("author.name")),
pattern: Regex::new("John Doe").ok(),
},
]),
protect_breaking_commits: None,
Expand All @@ -95,6 +114,17 @@ fn generate_changelog() -> Result<()> {
limit_commits: None,
};

let mut commit_with_author = Commit::new(
String::from("hjdfas32"),
String::from("docs(cool): testing author filtering"),
);

commit_with_author.author = Signature {
name: Some("John Doe".to_string()),
email: None,
timestamp: 0x0,
};

let releases = vec![
Release {
version: Some(String::from("v2.0.0")),
Expand Down Expand Up @@ -133,6 +163,7 @@ fn generate_changelog() -> Result<()> {
String::from("1234"),
String::from("fix: support preprocessing (fixes #99)"),
),
commit_with_author
]
.iter()
.filter_map(|c| c.process(&git_config).ok())
Expand Down Expand Up @@ -181,6 +212,9 @@ fn generate_changelog() -> Result<()> {

## Release v2.0.0

### docs
- *(cool)* testing author filtering

### fix bugs
- fix abc
- support preprocessing [closes Issue#99]
Expand Down
9 changes: 9 additions & 0 deletions website/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ Examples:
- If the commit starts with "doc", group the commit as "Documentation" and set the default scope to "other". (e.g. `docs: xyz` will be processed as `docs(other): xyz`)
- `{ message = "(www)", scope = "Application"}`
- If the commit contains "(www)", override the scope with "Application". Scoping order is: scope specification, conventional commit's scope and default scope.
- `{ 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:
- `id`
- `message`
- `body`
- `author.name`
- `author.email`
- `committer.email`
- `committer.name`

### protect_breaking_commits

Expand Down
Loading