1
1
mirror of https://github.com/orhun/git-cliff.git synced 2024-09-19 03:08:30 +03:00

feat(bump): support bumping to a specific semver type (#744)

* feat: support bump a specific version type

* let --bump accept optional enum

* doc: update document for --bump

* test: add bump version fixtures tests

* refactor: polish implementation

* refactor: fix typo

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
Binbin 2024-07-31 14:48:03 +09:00 committed by GitHub
parent e2fb043078
commit 9dbf47bac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 338 additions and 18 deletions

View File

@ -0,0 +1,27 @@
[changelog]
# template for the changelog footer
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

View File

@ -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 "fix: fix feature 1"
GIT_COMMITTER_DATE="2021-01-23 01:23:46" git commit --allow-empty -m "fix: fix feature 2"

View File

@ -0,0 +1,19 @@
# Changelog
All notable changes to this project will be documented in this file.
## [1.0.0]
### Fix
- Fix feature 1
- Fix feature 2
## [0.1.0]
### Feat
- Add feature 1
- Add feature 2
<!-- generated by git-cliff -->

View File

@ -0,0 +1,27 @@
[changelog]
# template for the changelog footer
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

View File

@ -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"

View File

@ -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 -->

View File

@ -0,0 +1,27 @@
[changelog]
# template for the changelog footer
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

View File

@ -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"

View File

@ -0,0 +1,22 @@
# Changelog
All notable changes to this project will be documented in this file.
## [0.1.1]
### Feat
- [**breaking**] Add breaking feature
### Fix
- Fix feature 2
## [0.1.0]
### Feat
- Add feature 1
- Add feature 2
<!-- generated by git-cliff -->

View File

@ -38,6 +38,12 @@ jobs:
- fixtures-name: test-split-commits
- fixtures-name: test-bump-version
command: --bump
- fixtures-name: test-bump-version-major
command: --bump major
- fixtures-name: test-bump-version-minor
command: --bump minor
- fixtures-name: test-bump-version-patch
command: --bump patch
- fixtures-name: test-bump-version-custom-minor
command: --bump
- fixtures-name: test-bumped-version

View File

@ -174,6 +174,17 @@ impl Remote {
}
}
/// Version bump type.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
pub enum BumpType {
/// Bump major version.
Major,
/// Bump minor version.
Minor,
/// Bump patch version.
Patch,
}
/// Bump version configuration.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Bump {
@ -218,6 +229,9 @@ pub struct Bump {
///
/// `commit type` according to the spec is only `[a-zA-Z]+`
pub custom_minor_increment_regex: Option<String>,
/// Force to always bump in major, minor or patch.
pub bump_type: Option<BumpType>,
}
/// Parser for grouping commits.

View File

@ -1,5 +1,3 @@
use crate::commit::Commit;
use crate::config::Bump;
use crate::error::Result;
#[cfg(feature = "remote")]
use crate::remote::{
@ -8,7 +6,16 @@ use crate::remote::{
RemotePullRequest,
RemoteReleaseMetadata,
};
use next_version::VersionUpdater;
use crate::{
commit::Commit,
config::Bump,
config::BumpType,
};
use next_version::{
NextVersion,
VersionUpdater,
};
use semver::Version;
use serde::{
Deserialize,
@ -124,15 +131,23 @@ impl<'a> Release<'a> {
custom_minor_increment_regex,
)?;
}
let next_version = next_version
.increment(
&semver?,
self.commits
.iter()
.map(|commit| commit.message.trim_end().to_string())
.collect::<Vec<String>>(),
)
.to_string();
let next_version = if let Some(bump_type) = &config.bump_type {
match bump_type {
BumpType::Major => semver?.increment_major().to_string(),
BumpType::Minor => semver?.increment_minor().to_string(),
BumpType::Patch => semver?.increment_patch().to_string(),
}
} else {
next_version
.increment(
&semver?,
self.commits
.iter()
.map(|commit| commit.message.trim_end().to_string())
.collect::<Vec<String>>(),
)
.to_string()
};
if let Some(prefix) = prefix {
Ok(format!("{prefix}{next_version}"))
} else {
@ -282,6 +297,7 @@ mod test {
initial_tag: None,
custom_major_increment_regex: None,
custom_minor_increment_regex: None,
bump_type: None,
})?;
assert_eq!(expected_version, &next_version);
}
@ -305,6 +321,7 @@ mod test {
initial_tag: None,
custom_major_increment_regex: None,
custom_minor_increment_regex: None,
bump_type: None,
})?;
assert_eq!(expected_version, &next_version);
}
@ -328,6 +345,7 @@ mod test {
initial_tag: None,
custom_major_increment_regex: None,
custom_minor_increment_regex: None,
bump_type: None,
})?;
assert_eq!(expected_version, &next_version);
}
@ -351,6 +369,7 @@ mod test {
initial_tag: None,
custom_major_increment_regex: None,
custom_minor_increment_regex: None,
bump_type: None,
})?
);
}

View File

@ -13,6 +13,7 @@ use clap::{
ValueEnum,
};
use git_cliff_core::{
config::BumpType,
config::Remote,
DEFAULT_CONFIG,
DEFAULT_OUTPUT,
@ -190,9 +191,16 @@ 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,
/// Bumps the version for unreleased changes. Optionally with specified
/// version.
#[arg(
long,
value_name = "BUMP",
value_enum,
num_args = 0..=1,
default_missing_value = "auto",
value_parser = clap::value_parser!(BumpOption))]
pub bump: Option<BumpOption>,
/// Prints bumped version for unreleased changes.
#[arg(long, help_heading = Some("FLAGS"))]
pub bumped_version: bool,
@ -352,6 +360,54 @@ impl TypedValueParser for RemoteValueParser {
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum BumpOption {
Auto,
Specific(BumpType),
}
impl ValueParserFactory for BumpOption {
type Parser = BumpOptionParser;
fn value_parser() -> Self::Parser {
BumpOptionParser
}
}
/// Parser for bump type.
#[derive(Clone, Debug)]
pub struct BumpOptionParser;
impl TypedValueParser for BumpOptionParser {
type Value = BumpOption;
fn parse_ref(
&self,
cmd: &clap::Command,
arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
let inner = clap::builder::StringValueParser::new();
let value = inner.parse_ref(cmd, arg, value)?;
match value.as_str() {
"auto" => Ok(BumpOption::Auto),
"major" => Ok(BumpOption::Specific(BumpType::Major)),
"minor" => Ok(BumpOption::Specific(BumpType::Minor)),
"patch" => Ok(BumpOption::Specific(BumpType::Patch)),
_ => {
let mut err =
clap::Error::new(ErrorKind::ValueValidation).with_cmd(cmd);
if let Some(arg) = arg {
err.insert(
ContextKind::InvalidArg,
ContextValue::String(arg.to_string()),
);
}
err.insert(ContextKind::InvalidValue, ContextValue::String(value));
Err(err)
}
}
}
}
impl Opt {
/// Custom string parser for directories.
///
@ -409,4 +465,29 @@ mod tests {
.is_err());
Ok(())
}
#[test]
fn bump_option_parser() -> Result<(), clap::Error> {
let bump_option_parser = BumpOptionParser;
assert_eq!(
BumpOption::Auto,
bump_option_parser.parse_ref(
&Opt::command(),
None,
OsStr::new("auto")
)?
);
assert!(bump_option_parser
.parse_ref(&Opt::command(), None, OsStr::new("test"))
.is_err());
assert_eq!(
BumpOption::Specific(BumpType::Major),
bump_option_parser.parse_ref(
&Opt::command(),
None,
OsStr::new("major")
)?
);
Ok(())
}
}

View File

@ -14,6 +14,7 @@ pub mod logger;
extern crate log;
use args::{
BumpOption,
Opt,
Sort,
Strip,
@ -546,10 +547,13 @@ pub fn run(mut args: Opt) -> Result<()> {
}
// Process commits and releases for the changelog.
if let Some(BumpOption::Specific(bump_type)) = args.bump {
config.bump.bump_type = Some(bump_type)
}
let mut changelog = Changelog::new(releases, &config)?;
// Print the result.
if args.bump || args.bumped_version {
if args.bump.is_some() || args.bumped_version {
let next_version = if let Some(next_version) = changelog.bump_version()? {
next_version
} else if let Some(last_version) =

View File

@ -81,3 +81,14 @@ git-cliff --bumped-version
0.2.0
```
### bump_type
When set, it forces to always bump in major, minor or patch.
e.g.
```toml
[bump]
bump_type = "minor"
```

View File

@ -14,7 +14,6 @@ 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
--bumped-version Prints bumped version for unreleased changes
-l, --latest Processes the commits starting from the latest tag
--current Processes the commits that belong to the current tag
@ -28,6 +27,7 @@ git-cliff [FLAGS] [OPTIONS] [--] [RANGE]
```
-i, --init [<CONFIG>] Writes the default configuration file to cliff.toml
--bump Bumps the version for unreleased changes [default: auto] [possible values: auto, major, minor, patch]
-c, --config <PATH> Sets the configuration file [env: GIT_CLIFF_CONFIG=] [default: cliff.toml]
-w, --workdir <PATH> Sets the working directory [env: GIT_CLIFF_WORKDIR=]
-r, --repository <PATH>... Sets the git repository [env: GIT_CLIFF_REPOSITORY=]

View File

@ -22,18 +22,32 @@ How it works is that for a semantic versioning such as `<MAJOR>.<MINOR>.<PATCH>`
- "feat:" -> increments `MINOR`
- "scope!" (breaking changes) -> increments `MAJOR`
## Get version
You can also calculate and print the next semantic version to `stdout`:
```bash
git cliff --bumped-version
```
Tip: you can also get the bumped version [from the context](/docs/usage/print-context) as follows:
:::tip
You can also get the bumped version [from the context](/docs/usage/print-context) as follows:
```bash
git cliff --unreleased --bump --context | jq -r .[0].version
```
:::
## Bump to a specific version type
Optionally, you can specify a bump type in `--bump`:
```bash
git cliff --bump [major|minor|patch]
```
## Zero-based versioning scheme
When working with a zero-based versioning scheme (i.e., `0.x.y` or `0.0.x`),