Skip to content

Commit

Permalink
feat(changelog): do not skip breaking changes if configured (#114)
Browse files Browse the repository at this point in the history
* feat(changelog): breaking changes are never skipped

Fixes #106

* test(fixture): add test fixture for skipping breaking changes

Co-authored-by: Orhun Parmaksız <[email protected]>
  • Loading branch information
sbmueller and orhun committed Oct 4, 2022
1 parent f9d4b88 commit 1c98995
Show file tree
Hide file tree
Showing 14 changed files with 178 additions and 49 deletions.
39 changes: 39 additions & 0 deletions .github/fixtures/test-skip-breaking-changes/cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[changelog]
# changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
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 | upper_first }}
{% for group, commits in commits | group_by(attribute="scope") %}
#### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\
{% endfor %}\n
"""
# remove the leading and trailing whitespaces from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""

[git]
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features", default_scope = "app", skip = true},
{ message = "^fix", group = "Bug Fixes", scope = "cli", skip = true},
]
# protect breaking changes from being skipped
protect_breaking_commits = true
9 changes: 9 additions & 0 deletions .github/fixtures/test-skip-breaking-changes/commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/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: add feature 1"
GIT_COMMITTER_DATE="2022-04-06 01:25:10" git commit --allow-empty -m "fix!: fix feature 1"
GIT_COMMITTER_DATE="2022-04-06 01:25:11" git commit --allow-empty -m "feat(gui)!: add feature 2"
GIT_COMMITTER_DATE="2022-04-06 01:25:12" git commit --allow-empty -m "fix(gui)!: fix feature 2"
git tag v0.1.0
20 changes: 20 additions & 0 deletions .github/fixtures/test-skip-breaking-changes/expected.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog

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

## [0.1.0] - 2022-04-05

### Bug Fixes

#### Cli

- Fix feature 1
- Fix feature 2

### Features

#### Gui

- Add feature 2

<!-- generated by git-cliff -->
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ commit_parsers = [
{ message = "^style", group = "Styling"},
{ message = "^test", group = "Testing"},
]
protect_breaking_commits = false
filter_commits = false
tag_pattern = "v[0-9]*"
skip_tags = "v0.1.0-beta.1"
Expand Down Expand Up @@ -593,6 +594,12 @@ 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.

#### protect_breaking_commits

If set to `true`, any breaking changes will be protected against being skipped
due to any commit parser.

#### filter_commits

If set to `true`, commits that are not matched by [commit parsers](#commit_parsers) are filtered out.
Expand Down Expand Up @@ -778,6 +785,8 @@ BREAKING CHANGE: this is a breaking change
If the `BREAKING CHANGE:` footer is present, the footer will also be included in
`commit.footers`.

Breaking changes will be skipped if [`protect_breaking_commits`](#protect_breaking_commits) is set to `true`, even when matched by a skipping [commit_parser](#commit_parsers).

##### Committer vs Author

From [Git docs](https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History):
Expand Down
2 changes: 2 additions & 0 deletions config/cliff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ commit_parsers = [
{ message = "^chore", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# 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
Expand Down
2 changes: 2 additions & 0 deletions examples/detailed.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ commit_parsers = [
{ message = "^chore", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# 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
Expand Down
2 changes: 2 additions & 0 deletions examples/keepachangelog.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ commit_parsers = [
{ message = "^.*: fix", group = "Fixed"},
{ message = "^.*", group = "Changed"},
]
# 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 = true
# glob pattern for matching git tags
Expand Down
2 changes: 2 additions & 0 deletions examples/scoped.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ commit_parsers = [
{ message = "^chore", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# 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
Expand Down
2 changes: 2 additions & 0 deletions examples/scopesorted.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ commit_parsers = [
{ message = "^chore", group = "Miscellaneous Tasks"},
{ body = ".*security", group = "Security"},
]
# 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
Expand Down
2 changes: 2 additions & 0 deletions examples/unconventional.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ commit_parsers = [
{ body = ".*security", group = "Security"},
{ body = ".*", group = "Other (unconventional)"},
]
# 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
Expand Down
36 changes: 28 additions & 8 deletions git-cliff-core/src/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,11 @@ impl Commit<'_> {
}
}
if let Some(parsers) = &config.commit_parsers {
commit =
commit.parse(parsers, config.filter_commits.unwrap_or(false))?;
commit = commit.parse(
parsers,
config.protect_breaking_commits.unwrap_or(false),
config.filter_commits.unwrap_or(false),
)?;
}
if let Some(parsers) = &config.link_parsers {
commit = commit.parse_links(parsers)?;
Expand Down Expand Up @@ -233,13 +236,29 @@ impl Commit<'_> {
Ok(self)
}

/// States if the commit is skipped in the provided `CommitParser`.
///
/// Returns `false` if `protect_breaking_commits` is enabled in the config
/// and the commit is breaking, or the parser's `skip` field is None or
/// `false`. Returns `true` otherwise.
fn skip_commit(&self, parser: &CommitParser, protect_breaking: bool) -> bool {
parser.skip.unwrap_or(false) &&
!(self.conv.as_ref().map(|c| c.breaking()).unwrap_or(false) &&
protect_breaking)
}

/// Parses the commit using [`CommitParser`]s.
///
/// Sets the [`group`] and [`scope`] of the commit.
///
/// [`group`]: Commit::group
/// [`scope`]: Commit::scope
pub fn parse(mut self, parsers: &[CommitParser], filter: bool) -> Result<Self> {
pub fn parse(
mut self,
parsers: &[CommitParser],
protect_breaking: bool,
filter: bool,
) -> Result<Self> {
for parser in parsers {
let mut regex_checks = Vec::new();
if let Some(message_regex) = parser.message.as_ref() {
Expand All @@ -253,15 +272,15 @@ impl Commit<'_> {
}
for (regex, text) in regex_checks {
if regex.is_match(&text) {
if parser.skip != Some(true) {
if self.skip_commit(parser, protect_breaking) {
return Err(AppError::GroupError(String::from(
"Skipping commit",
)));
} else {
self.group = parser.group.as_ref().cloned();
self.scope = parser.scope.as_ref().cloned();
self.default_scope = parser.default_scope.as_ref().cloned();
return Ok(self);
} else {
return Err(AppError::GroupError(String::from(
"Skipping commit",
)));
}
}
}
Expand Down Expand Up @@ -407,6 +426,7 @@ mod test {
skip: None,
}],
false,
false,
)?;
assert_eq!(Some(String::from("test_group")), commit.group);
assert_eq!(Some(String::from("test_scope")), commit.default_scope);
Expand Down
21 changes: 12 additions & 9 deletions git-cliff-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,28 @@ pub struct GitConfig {
pub split_commits: Option<bool>,

/// Git commit preprocessors.
pub commit_preprocessors: Option<Vec<CommitPreprocessor>>,
pub commit_preprocessors: Option<Vec<CommitPreprocessor>>,
/// Git commit parsers.
pub commit_parsers: Option<Vec<CommitParser>>,
pub commit_parsers: Option<Vec<CommitParser>>,
/// Whether to protect all breaking changes from being skipped by a commit
/// parser.
pub protect_breaking_commits: Option<bool>,
/// Link parsers.
pub link_parsers: Option<Vec<LinkParser>>,
pub link_parsers: Option<Vec<LinkParser>>,
/// Whether to filter out commits.
pub filter_commits: Option<bool>,
pub filter_commits: Option<bool>,
/// Blob pattern for git tags.
pub tag_pattern: Option<String>,
pub tag_pattern: Option<String>,
#[serde(with = "serde_regex", default)]
/// Regex to skip matched tags.
pub skip_tags: Option<Regex>,
pub skip_tags: Option<Regex>,
#[serde(with = "serde_regex", default)]
/// Regex to ignore matched tags.
pub ignore_tags: Option<Regex>,
pub ignore_tags: Option<Regex>,
/// Whether to sort tags chronologically.
pub date_order: Option<bool>,
pub date_order: Option<bool>,
/// Sorting of the commits inside sections.
pub sort_commits: Option<String>,
pub sort_commits: Option<String>,
}

/// Parser for grouping commits.
Expand Down
25 changes: 13 additions & 12 deletions git-cliff-core/tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ fn generate_changelog() -> Result<()> {
trim: None,
};
let git_config = GitConfig {
conventional_commits: Some(true),
filter_unconventional: Some(true),
split_commits: Some(false),
commit_preprocessors: Some(vec![CommitPreprocessor {
conventional_commits: Some(true),
filter_unconventional: Some(true),
split_commits: Some(false),
commit_preprocessors: Some(vec![CommitPreprocessor {
pattern: Regex::new(r#"\(fixes (#[1-9]+)\)"#).unwrap(),
replace: Some(String::from("[closes Issue${1}]")),
replace_command: None,
}]),
commit_parsers: Some(vec![
commit_parsers: Some(vec![
CommitParser {
message: Regex::new("^feat").ok(),
body: None,
Expand All @@ -72,13 +72,14 @@ fn generate_changelog() -> Result<()> {
skip: None,
},
]),
filter_commits: Some(true),
tag_pattern: None,
skip_tags: None,
ignore_tags: None,
date_order: None,
sort_commits: None,
link_parsers: Some(vec![
protect_breaking_commits: None,
filter_commits: Some(true),
tag_pattern: None,
skip_tags: None,
ignore_tags: None,
date_order: None,
sort_commits: None,
link_parsers: Some(vec![
LinkParser {
pattern: Regex::new("#(\\d+)").unwrap(),
href: String::from("https://github.com/$1"),
Expand Down
Loading

0 comments on commit 1c98995

Please sign in to comment.