feat: add bump

This commit is contained in:
Kasper Juul Hermansen 2023-04-07 01:43:47 +02:00
parent f3b497d183
commit a02475d6a3
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
4 changed files with 225 additions and 0 deletions

7
Cargo.lock generated
View File

@ -1265,6 +1265,7 @@ dependencies = [
"eyre",
"regex",
"reqwest",
"semver",
"serde",
"serde_json",
"tokio",
@ -1427,6 +1428,12 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.159"

View File

@ -30,6 +30,7 @@ dotenv = "0.15.0"
eyre.workspace = true
regex = "1.7.3"
reqwest = "0.11.16"
semver = "1.0.17"
serde.workspace = true
serde_json = "1.0.95"
tokio.workspace = true

216
src/bump.rs Normal file
View File

@ -0,0 +1,216 @@
use regex::Regex;
use semver::{BuildMetadata, Prerelease, Version};
#[derive(PartialEq, Debug)]
pub enum BumpType {
Major,
Minor,
Patch,
}
pub fn get_most_significant_bump(commits: &[String]) -> Option<BumpType> {
let bump_regex = Regex::new(
r"^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|breaking)(\([a-zA-Z-_ ]+\))?: .*",
)
.unwrap();
let mut major_bump = None;
let mut minor_bump = None;
let mut patch_bump = None;
for commit in commits {
if let Some(captures) = bump_regex.captures(commit) {
let bump_type = captures.get(1).unwrap().as_str();
match bump_type {
"breaking" => {
if major_bump.is_none() {
major_bump = Some(1);
}
}
"feat" => {
if minor_bump.is_none() && major_bump.is_none() {
minor_bump = Some(1);
} else if let Some(mut count) = minor_bump {
count += 1;
minor_bump = Some(count);
}
}
"chore" | "docs" | "ci" | "test" => {}
_ => {
if patch_bump.is_none() && minor_bump.is_none() && major_bump.is_none() {
patch_bump = Some(1);
} else if let Some(mut count) = patch_bump {
count += 1;
patch_bump = Some(count);
}
}
}
}
}
let most_significant_bump = match (major_bump, minor_bump, patch_bump) {
(Some(count), _, _) if count > 0 => Some(BumpType::Major),
(_, Some(count), _) if count > 0 => Some(BumpType::Minor),
(_, _, Some(count)) if count > 0 => Some(BumpType::Patch),
_ => None,
};
most_significant_bump
}
fn increment_patch(v: &mut Version) {
v.patch += 1;
v.pre = Prerelease::EMPTY;
v.build = BuildMetadata::EMPTY;
}
fn increment_minor(v: &mut Version) {
v.minor += 1;
v.patch = 0;
v.pre = Prerelease::EMPTY;
v.build = BuildMetadata::EMPTY;
}
fn increment_major(v: &mut Version) {
v.major += 1;
v.minor = 0;
v.patch = 0;
v.pre = Prerelease::EMPTY;
v.build = BuildMetadata::EMPTY;
}
pub fn bump_semver(semver: &str, bump_type: BumpType) -> Result<String, semver::Error> {
let mut version = Version::parse(semver)?;
match bump_type {
BumpType::Major => increment_major(&mut version),
BumpType::Minor => increment_minor(&mut version),
BumpType::Patch => increment_patch(&mut version),
}
Ok(version.to_string())
}
#[cfg(test)]
mod tests {
use super::{get_most_significant_bump, BumpType};
#[test]
fn test_no_commits() {
let commits = vec![];
assert_eq!(get_most_significant_bump(&commits), None);
}
#[test]
fn test_major_bump() {
let commits = vec![
"breaking: new feature".to_string(),
"fix: bug fix".to_string(),
"chore: some chore".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Major));
}
#[test]
fn test_minor_bump() {
let commits = vec!["feat: bug fix".to_string(), "chore: some chore".to_string()];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Minor));
}
#[test]
fn test_patch_bump() {
let commits = vec![
"chore: some chore".to_string(),
"style: code style change".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Patch));
}
#[test]
fn test_no_significant_bumps() {
let commits = vec![
"docs: documentation update".to_string(),
"chore: another chore".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), None);
}
#[test]
fn test_combined_bumps() {
let commits = vec![
"breaking: new breaking change".to_string(),
"feat: new feature".to_string(),
"fix: bug fix".to_string(),
"style: code style change".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Major));
}
#[test]
fn test_major_bump_with_scope() {
let commits = vec![
"breaking(some-system): new feature".to_string(),
"fix(another-system): bug fix".to_string(),
"chore(third-system): some chore".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Major));
}
#[test]
fn test_minor_bump_with_scope() {
let commits = vec![
"feat(some-system): bug fix".to_string(),
"chore(another-system): some chore".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Minor));
}
#[test]
fn test_patch_bump_with_scope() {
let commits = vec![
"chore(some-system): some chore".to_string(),
"style(another-system): code style change".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Patch));
}
#[test]
fn test_combined_bumps_with_scope() {
let commits = vec![
"breaking(some-system): new feature".to_string(),
"feat(another-system): bug fix".to_string(),
"style(third-system): code style change".to_string(),
];
assert_eq!(get_most_significant_bump(&commits), Some(BumpType::Major));
}
use super::bump_semver;
#[test]
fn test_bump_semver_major() {
let semver = "1.2.3";
let bumped_version = bump_semver(semver, BumpType::Major).unwrap();
assert_eq!(bumped_version, "2.0.0");
}
#[test]
fn test_bump_semver_minor() {
let semver = "1.2.3";
let bumped_version = bump_semver(semver, BumpType::Minor).unwrap();
assert_eq!(bumped_version, "1.3.0");
}
#[test]
fn test_bump_semver_patch() {
let semver = "1.2.3";
let bumped_version = bump_semver(semver, BumpType::Patch).unwrap();
assert_eq!(bumped_version, "1.2.4");
}
#[test]
fn test_bump_semver_invalid() {
let semver = "1.2.invalid";
let bumped_version = bump_semver(semver, BumpType::Patch);
assert!(bumped_version.is_err());
}
}

View File

@ -1,3 +1,4 @@
mod bump;
mod conventional_commits;
mod gitea;