1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-11-29 15:42:15 +03:00

feat(changelog): support bumping the semantic version via --bump (#309)

This commit is contained in:
Orhun Parmaksız 2023-10-13 14:43:55 +02:00 committed by GitHub
parent a6a890ac4f
commit bcfcd1fd59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 112 additions and 2 deletions

22
Cargo.lock generated
View File

@ -301,6 +301,16 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "conventional_commit_parser"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58660f9e1d5eeeeec9c33d1473ea8bba000c673a2189edaeedb4523ec7d6f7cb"
dependencies = [
"pest",
"pest_derive",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
@ -604,9 +614,11 @@ dependencies = [
"indexmap 2.0.2",
"lazy-regex",
"log",
"next_version",
"pretty_assertions",
"regex",
"rust-embed",
"semver",
"serde",
"serde_json",
"serde_regex",
@ -1032,6 +1044,16 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "next_version"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70bb5535b0e53d062c92ad0ad600a29b5fd4ea84e40dc42faecff21218239593"
dependencies = [
"conventional_commit_parser",
"semver",
]
[[package]]
name = "nom"
version = "7.1.3"

View File

@ -26,6 +26,8 @@ tera = "1.19.1"
indexmap = { version = "2.0.2", optional = true }
toml = "0.8.2"
lazy-regex = "3.0.2"
next_version = "0.2.8"
semver = "1.0.19"
[dependencies.git2]
version = "0.18.1"

View File

@ -57,6 +57,13 @@ 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 parsing a SemVer version or version
/// requirement.
#[error("Semver error: `{0}`")]
SemverError(#[from] semver::Error),
/// Error that may occur when a version is not found for the next release.
#[error("Previous version is not found for calculating the next release.")]
PreviousVersionNotFound,
}
/// Result type of the core library.

View File

@ -1,5 +1,10 @@
use crate::commit::Commit;
use crate::error::Result;
use crate::error::{
Error,
Result,
};
use next_version::NextVersion;
use semver::Version;
use serde::{
Deserialize,
Serialize,
@ -22,6 +27,26 @@ pub struct Release<'a> {
pub previous: Option<Box<Release<'a>>>,
}
impl<'a> Release<'a> {
/// Calculates the next version based on the commits.
pub fn calculate_next_version(&self) -> Result<String> {
let version = self
.previous
.as_ref()
.and_then(|release| release.version.clone())
.ok_or_else(|| Error::PreviousVersionNotFound)?;
let next_version = Version::parse(version.trim_start_matches('v'))?
.next(
self.commits
.iter()
.map(|commit| commit.message.to_string())
.collect::<Vec<String>>(),
)
.to_string();
Ok(next_version)
}
}
/// Representation of a list of releases.
pub struct Releases<'a>(pub &'a Vec<Release<'a>>);
@ -31,3 +56,33 @@ impl<'a> Releases<'a> {
Ok(serde_json::to_string(self.0)?)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bump_version() -> Result<()> {
for (expected_version, commits) in [
("1.1.0", vec!["feat: add xyz", "fix: fix xyz"]),
("1.0.1", vec!["fix: add xyz", "fix: aaaaaa"]),
("2.0.0", vec!["feat!: add xyz", "feat: zzz"]),
] {
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("1.0.0")),
..Default::default()
})),
};
let next_version = release.calculate_next_version()?;
assert_eq!(expected_version, next_version);
}
Ok(())
}
}

View File

@ -142,6 +142,9 @@ pub struct Opt {
allow_hyphen_values = true
)]
pub tag: Option<String>,
/// Bumps the version for unreleased changes.
#[arg(long, help_heading = Some("FLAGS"))]
pub bump: bool,
/// Sets the template for the changelog body.
#[arg(
short,

View File

@ -229,6 +229,17 @@ fn process_repository<'a>(
}
}
// Bump the version.
if args.bump && releases[release_index].version.is_none() {
let next_version = releases[release_index].calculate_next_version()?;
debug!("Bumping the version to {next_version}");
releases[release_index].version = Some(next_version.to_string());
releases[release_index].timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)?
.as_secs()
.try_into()?;
}
Ok(releases)
}

View File

@ -17,13 +17,21 @@ Then simply create a changelog at your projects git root directory:
git cliff
```
Set a tag for the "unreleased" changes:
Set a tag for the unreleased changes:
```bash
# it doesn't have to be an existing tag
git cliff --tag 1.0.0
```
Calculate and set the next semantic version (i.e. _bump the version_) for the unreleased changes:
```bash
# Semver: {MAJOR}.{MINOR}.{PATCH}
# "fix:" increments PATCH, "feat:" increments MINOR and "scope!" (breaking changes) increments MAJOR
git cliff --bump
```
Generate a changelog for a certain part of git history:
```bash

View File

@ -1,6 +1,7 @@
---
sidebar_position: 3
---
# Usage
```
@ -13,6 +14,7 @@ git-cliff [FLAGS] [OPTIONS] [--] [RANGE]
-h, --help Prints help information
-V, --version Prints version information
-v, --verbose... Increases the logging verbosity
--bump Bumps the version for unreleased changes
-i, --init Writes the default configuration file to cliff.toml
-l, --latest Processes the commits starting from the latest tag
--current Processes the commits that belong to the current tag