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(release): make the bump version rules configurable #530

Merged
merged 2 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/fixtures/test-bump-version-keep-zerover/cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[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://keats.github.io/tera/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}]
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# template for the changelog footer
footer = """
<!-- generated by git-cliff -->
"""
# remove the leading and trailing whitespace from the templates
trim = true

[bump]
features_always_bump_minor = false
breaking_always_bump_major = false
9 changes: 9 additions & 0 deletions .github/fixtures/test-bump-version-keep-zerover/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="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 1"
GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 2"
git tag v0.1.0

GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "feat!: add breaking feature"
GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 2"
22 changes: 22 additions & 0 deletions .github/fixtures/test-bump-version-keep-zerover/expected.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

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

## [0.2.0]

### Feat

- [**breaking**] Add breaking feature

### Fix

- Fix feature 2

## [0.1.0]

### Feat

- Add feature 1
- Add feature 2

<!-- generated by git-cliff -->
2 changes: 1 addition & 1 deletion .github/fixtures/test-bump-version/commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add
GIT_COMMITTER_DATE="2021-01-23 01:23:45" git commit --allow-empty -m "feat: add feature 2"
git tag v0.1.0

GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 1"
GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "feat!: add breaking feature"
GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 2"
7 changes: 5 additions & 2 deletions .github/fixtures/test-bump-version/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

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

## [0.1.1]
## [1.0.0]

### Feat

- [**breaking**] Add breaking feature

### Fix

- Fix feature 1
- Fix feature 2

## [0.1.0]
Expand Down
5 changes: 4 additions & 1 deletion git-cliff-core/src/changelog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ impl<'a> Changelog<'a> {
pub fn bump_version(&mut self) -> Result<Option<String>> {
if let Some(ref mut last_release) = self.releases.iter_mut().next() {
if last_release.version.is_none() {
let next_version = last_release.calculate_next_version()?;
let next_version = last_release
.calculate_next_version_with_config(&self.config.bump)?;
debug!("Bumping the version to {next_version}");
last_release.version = Some(next_version.to_string());
last_release.timestamp = SystemTime::now()
Expand Down Expand Up @@ -307,6 +308,7 @@ impl<'a> Changelog<'a> {
mod test {
use super::*;
use crate::config::{
Bump,
ChangelogConfig,
CommitParser,
GitConfig,
Expand Down Expand Up @@ -485,6 +487,7 @@ mod test {
token: None,
},
},
bump: Bump::default(),
};
let test_release = Release {
version: Some(String::from("v1.0.0")),
Expand Down
26 changes: 26 additions & 0 deletions git-cliff-core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub struct Config {
/// Configuration values about remote.
#[serde(default)]
pub remote: RemoteConfig,
/// Configuration values about bump version.
#[serde(default)]
pub bump: Bump,
}

/// Changelog configuration.
Expand Down Expand Up @@ -136,6 +139,29 @@ impl Remote {
}
}

/// Bump version configuration.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Bump {
/// Configures automatic minor version increments for feature changes.
///
/// When `true`, a feature will always trigger a minor version update.
/// When `false`, a feature will trigger:
///
/// - A patch version update if the major version is 0.
/// - A minor version update otherwise.
pub features_always_bump_minor: Option<bool>,

/// Configures 0 -> 1 major version increments for breaking changes.
///
/// When `true`, a breaking change commit will always trigger a major
/// version update (including the transition from version 0 to 1)
/// When `false`, a breaking change commit will trigger:
///
/// - A minor version update if the major version is 0.
/// - A major version update otherwise.
pub breaking_always_bump_major: Option<bool>,
}

/// Parser for grouping commits.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct CommitParser {
Expand Down
157 changes: 133 additions & 24 deletions git-cliff-core/src/release.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commit::Commit;
use crate::config::Bump;
use crate::error::Result;
#[cfg(feature = "github")]
use crate::github::{
Expand Down Expand Up @@ -98,7 +99,21 @@ impl<'a> Release<'a> {
}

/// Calculates the next version based on the commits.
///
/// It uses the default bump version configuration to calculate the next
/// version.
pub fn calculate_next_version(&self) -> Result<String> {
self.calculate_next_version_with_config(&Bump::default())
}

/// Calculates the next version based on the commits.
///
/// It uses the given bump version configuration to calculate the next
/// version.
pub(super) fn calculate_next_version_with_config(
&self,
config: &Bump,
) -> Result<String> {
match self
.previous
.as_ref()
Expand Down Expand Up @@ -126,8 +141,12 @@ impl<'a> Release<'a> {
}
}
let next_version = VersionUpdater::new()
.with_features_always_increment_minor(true)
.with_breaking_always_increment_major(true)
.with_features_always_increment_minor(
config.features_always_bump_minor.unwrap_or(true),
)
.with_breaking_always_increment_major(
config.breaking_always_bump_major.unwrap_or(true),
)
.increment(
&semver?,
self.commits
Expand Down Expand Up @@ -169,7 +188,27 @@ mod test {
use super::*;
#[test]
fn bump_version() -> Result<()> {
for (version, expected_version, commits) in [
fn build_release<'a>(version: &str, commits: &'a [&str]) -> Release<'a> {
Release {
version: None,
commits: commits
.iter()
.map(|v| Commit::from(v.to_string()))
.collect(),
commit_id: None,
timestamp: 0,
previous: Some(Box::new(Release {
version: Some(String::from(version)),
..Default::default()
})),
#[cfg(feature = "github")]
github: crate::github::GitHubReleaseMetadata {
contributors: vec![],
},
}
}

let test_shared = [
("1.0.0", "1.1.0", vec!["feat: add xyz", "fix: fix xyz"]),
("1.0.0", "1.0.1", vec!["fix: add xyz", "fix: aaaaaa"]),
("1.0.0", "2.0.0", vec!["feat!: add xyz", "feat: zzz"]),
Expand Down Expand Up @@ -202,36 +241,106 @@ mod test {
"aaa#/@#$@9384!#%^#@#@!#!239432413-idk-9999.2200.5932-alpha.420",
vec!["feat: damn this is working"],
),
] {
let release = Release {
version: None,
commits: commits
.into_iter()
.map(|v| Commit::from(v.to_string()))
.collect(),
commit_id: None,
timestamp: 0,
previous: Some(Box::new(Release {
version: Some(String::from(version)),
..Default::default()
})),
#[cfg(feature = "github")]
github: crate::github::GitHubReleaseMetadata {
contributors: vec![],
},
};
];

for (version, expected_version, commits) in test_shared.iter().chain(
[
("0.0.1", "0.0.2", vec!["fix: fix xyz"]),
("0.0.1", "0.1.0", vec!["feat: add xyz", "fix: fix xyz"]),
("0.0.1", "1.0.0", vec!["feat!: add xyz", "feat: zzz"]),
("0.1.0", "0.1.1", vec!["fix: fix xyz"]),
("0.1.0", "0.2.0", vec!["feat: add xyz", "fix: fix xyz"]),
("0.1.0", "1.0.0", vec!["feat!: add xyz", "feat: zzz"]),
]
.iter(),
) {
let release = build_release(version, commits);
let next_version = release.calculate_next_version()?;
assert_eq!(expected_version, next_version);
assert_eq!(expected_version, &next_version);
let next_version =
release.calculate_next_version_with_config(&Bump::default())?;
assert_eq!(expected_version, &next_version);
}

for (version, expected_version, commits) in test_shared.iter().chain(
[
("0.0.1", "0.0.2", vec!["fix: fix xyz"]),
("0.0.1", "0.0.2", vec!["feat: add xyz", "fix: fix xyz"]),
("0.0.1", "0.0.2", vec!["feat!: add xyz", "feat: zzz"]),
("0.1.0", "0.1.1", vec!["fix: fix xyz"]),
("0.1.0", "0.1.1", vec!["feat: add xyz", "fix: fix xyz"]),
("0.1.0", "0.2.0", vec!["feat!: add xyz", "feat: zzz"]),
]
.iter(),
) {
let release = build_release(version, commits);
let next_version =
release.calculate_next_version_with_config(&Bump {
features_always_bump_minor: Some(false),
breaking_always_bump_major: Some(false),
})?;
assert_eq!(expected_version, &next_version);
}

for (version, expected_version, commits) in test_shared.iter().chain(
[
("0.0.1", "0.0.2", vec!["fix: fix xyz"]),
("0.0.1", "0.1.0", vec!["feat: add xyz", "fix: fix xyz"]),
("0.0.1", "0.1.0", vec!["feat!: add xyz", "feat: zzz"]),
("0.1.0", "0.1.1", vec!["fix: fix xyz"]),
("0.1.0", "0.2.0", vec!["feat: add xyz", "fix: fix xyz"]),
("0.1.0", "0.2.0", vec!["feat!: add xyz", "feat: zzz"]),
]
.iter(),
) {
let release = build_release(version, commits);
let next_version =
release.calculate_next_version_with_config(&Bump {
features_always_bump_minor: Some(true),
breaking_always_bump_major: Some(false),
})?;
assert_eq!(expected_version, &next_version);
}

for (version, expected_version, commits) in test_shared.iter().chain(
[
("0.0.1", "0.0.2", vec!["fix: fix xyz"]),
("0.0.1", "0.0.2", vec!["feat: add xyz", "fix: fix xyz"]),
("0.0.1", "1.0.0", vec!["feat!: add xyz", "feat: zzz"]),
("0.1.0", "0.1.1", vec!["fix: fix xyz"]),
("0.1.0", "0.1.1", vec!["feat: add xyz", "fix: fix xyz"]),
("0.1.0", "1.0.0", vec!["feat!: add xyz", "feat: zzz"]),
]
.iter(),
) {
let release = build_release(version, commits);
let next_version =
release.calculate_next_version_with_config(&Bump {
features_always_bump_minor: Some(false),
breaking_always_bump_major: Some(true),
})?;
assert_eq!(expected_version, &next_version);
}

let empty_release = Release {
previous: Some(Box::new(Release {
version: None,
..Default::default()
})),
..Default::default()
};
let next_version = empty_release.calculate_next_version()?;
assert_eq!("0.1.0", next_version);
assert_eq!("0.1.0", empty_release.calculate_next_version()?);
for (features_always_bump_minor, breaking_always_bump_major) in
[(true, true), (true, false), (false, true), (false, false)]
{
assert_eq!(
"0.1.0",
empty_release.calculate_next_version_with_config(&Bump {
features_always_bump_minor: Some(features_always_bump_minor),
breaking_always_bump_major: Some(breaking_always_bump_major),
})?
);
}
Ok(())
}

Expand Down
28 changes: 28 additions & 0 deletions website/docs/configuration/bump.md
orhun marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# `bump`

This section contains the bump version related configuration options.

```toml
[bump]
features_always_bump_minor = true
breaking_always_bump_major = true
```

### features_always_bump_minor

Configures automatic minor version increments for feature changes.
When `true`, a feature will always trigger a minor version update.
When `false`, a feature will trigger:

- A patch version update if the major version is 0.
- A minor version update otherwise.

### breaking_always_bump_major

Configures 0 -> 1 major version increments for breaking changes.
When `true`, a breaking change commit will always trigger a major version update
(including the transition from version 0 to 1)
When `false`, a breaking change commit will trigger:

- A minor version update if the major version is 0.
- A major version update otherwise.
9 changes: 9 additions & 0 deletions website/docs/usage/bump-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,12 @@ Tip: you can also get the bumped version [from the context](/docs/usage/print-co
```bash
git cliff --unreleased --bump --context | jq -r .[0].version
```

## Zero-based versioning scheme

When working with a zero-based versioning scheme (i.e., `0.x.y` or `0.0.x`),
it is often desirable to preserve the leading zero even when introducing a breaking change.
A switch from `0` to `1` should indicate a higher API stability level.

You can modify the bumping rules to preserve the zero-based versioning scheme in the
[configuration file](/docs/configuration/bump).
Loading