parse versions from .bumpversion.toml if args not passed (#2)

This commit is contained in:
Mahmoud 2024-03-03 13:14:59 +02:00 committed by GitHub
parent 5a867eb532
commit c9e019e8f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 174 additions and 74 deletions

View file

@ -64,7 +64,7 @@ pub struct Cli {
/// Version that needs to be updated.
#[arg(long = "current-version", value_name = "VERSION")]
pub current_version: String,
pub current_version: Option<String>,
/// Part of the version to be bumped.
#[arg(
@ -91,12 +91,12 @@ pub struct Cli {
pub serialize: String,
/// Don't write any files, just pretend.
#[arg(short = 'n', long = "dry-run")]
#[arg(short = 'n', long = "dry-run", default_value_t = false)]
pub dry_run: bool,
/// New version that should be in the files.
#[arg(long = "new-version", value_name = "VERSION")]
pub new_version: String,
pub new_version: Option<String>,
/// Create a commit in version control.
#[arg(long = "commit", default_value_t = true)]

View file

@ -1,6 +1,9 @@
use self::cli::Cli;
use crate::utils::attempt_version_bump;
use crate::utils::get_current_version_from_config;
use crate::utils::read_files_from_config;
use clap::Parser;
use std::collections::HashSet;
use std::fs;
use std::process::Command;
@ -8,23 +11,32 @@ mod cli;
mod utils;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse command-line arguments
let args = Cli::parse();
let config_file = args.config_file.clone();
let current_version = args.current_version.clone();
let config_content = fs::read_to_string(args.config_file.clone()).unwrap();
let config_version = get_current_version_from_config(&config_content).ok_or("")?;
let current_version = args
.current_version
.clone()
.unwrap_or(config_version)
.clone();
let attempted_new_version = attempt_version_bump(args.clone());
if !args.new_version.is_empty() && attempted_new_version.is_some() {
// TODO: fix let new_version = attempted_new_version.clone().unwrap();
let new_version = args.new_version.clone();
if attempted_new_version.is_some() {
let new_version = attempted_new_version.clone().unwrap();
let dry_run = args.dry_run;
let commit = args.commit;
let tag = args.tag;
let message = args.message;
let files: Vec<String> = args.files;
let files: Vec<String> = if args.files.is_empty() {
let config_files: HashSet<String> = read_files_from_config(&args.config_file)?;
config_files.into_iter().collect()
} else {
args.files
};
// Check if Git working directory is clean
if fs::metadata(".git").is_ok() {
@ -66,12 +78,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config_content = fs::read_to_string(config_file.clone())?;
config_content = config_content.replace(
&format!("new_version={}", attempted_new_version.unwrap()),
"",
);
config_content = config_content.replace(
&format!("current_version={}", current_version),
&format!("current_version={}", new_version),
&format!("current_version = {}", current_version),
&format!("current_version = {}", new_version),
);
if !dry_run {
@ -79,22 +87,62 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
commit_files.push(config_file);
}
}
// Git commit and tag
if commit {
for path in &commit_files {
Command::new("git").arg("add").arg(path).output()?;
}
let git_add_output = Command::new("git").arg("add").arg(path).output();
Command::new("git")
.arg("commit")
.arg("-m")
.arg(
message
.replace("{current_version}", &current_version)
.replace("{new_version}", &new_version),
)
.output()?;
match git_add_output {
Ok(output) => {
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
eprintln!("Error during git add:\n{}", stderr);
}
}
Err(err) => {
eprintln!("Failed to execute git add: {}", err);
}
}
}
let git_diff_output = Command::new("git").arg("diff").output();
match git_diff_output {
Ok(output) => {
if !output.stdout.is_empty() {
// There are changes, proceed with git commit
let commit_output = Command::new("git")
.arg("commit")
.arg("-m")
.arg(
message
.replace("{current_version}", &current_version)
.replace("{new_version}", &new_version),
)
.output();
match commit_output {
Ok(commit_output) => {
if commit_output.status.success() {
println!("Git commit successful");
} else {
eprintln!(
"Error during git commit:\n{}",
String::from_utf8_lossy(&commit_output.stderr)
);
}
}
Err(err) => {
eprintln!("Failed to execute git commit: {}", err);
}
}
} else {
// No changes to commit
println!("No changes to commit. Working tree clean.");
}
}
Err(err) => {
eprintln!("Failed to execute git diff: {}", err);
}
}
if tag {
Command::new("git")
@ -104,7 +152,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
} else {
println!("No files specified");
eprintln!("No files specified");
}
Ok(())

View file

@ -1,49 +1,101 @@
use crate::cli::Cli;
use regex::Regex;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs;
pub fn get_current_version_from_config(config_content: &str) -> Option<String> {
let current_version_regex =
Regex::new(r#"\[bumpversion\]\s*current_version\s*=\s*(?P<version>\d+(\.\d+){0,2})\s*"#)
.unwrap();
if let Some(captures) = current_version_regex.captures(config_content) {
if let Some(version) = captures.name("version") {
return Some(version.as_str().to_string());
}
}
None
}
// Function to read files from the configuration file
pub fn read_files_from_config(config_file: &str) -> Result<HashSet<String>, std::io::Error> {
let config_content = fs::read_to_string(config_file)?;
let mut config_files = HashSet::new();
for line in config_content.lines() {
if let Some(file_section) = line.strip_prefix("[bumpversion:file:") {
if let Some(file_name) = file_section.split(']').next() {
config_files.insert(file_name.trim().to_string());
}
}
}
Ok(config_files)
}
pub fn attempt_version_bump(args: Cli) -> Option<String> {
let parse_regex = args.parse;
let regex = Regex::new(&parse_regex).ok()?;
let parse_regex = args.parse.clone();
let regex = match Regex::new(&parse_regex) {
Ok(r) => r,
Err(_) => {
eprintln!("--patch '{}' is not a valid regex", args.parse.clone());
return None;
}
};
let current_version = args.current_version;
let match_result = regex.captures_iter(&current_version);
let config_content = fs::read_to_string(args.config_file.clone()).unwrap();
let current_version = get_current_version_from_config(&config_content).unwrap_or_else(|| {
panic!("Failed to extract current version from config file");
});
// let current_version = args.current_version.unwrap_or("".to_string());
let mut parsed: HashMap<String, String> = HashMap::new();
let mut parsed: HashMap<&str, &str> = HashMap::new();
for caps in match_result {
if let (Some(name), Some(value)) = (caps.name("name"), caps.name("value")) {
parsed.insert(name.as_str(), value.as_str());
if let Some(captures) = regex.captures(&current_version) {
for name in regex.capture_names() {
if let Some(name) = name {
if let Some(capture) = captures.name(name) {
parsed.insert(name.to_string(), capture.as_str().to_string());
}
}
}
}
let order: Vec<&str> = args
.serialize
.match_indices('{')
.map(|(i, _)| args.serialize[i + 1..].split('}').next().unwrap())
.map(|s| s.trim())
.map(|(i, _)| args.serialize[i + 1..].split('}').next().unwrap().trim())
.collect();
let mut bumped = true;
let mut bumped = false;
for label in order {
if label == args.bump {
if let Some(_part) = parsed.get_mut(label) {
// TODO: fix
// let new_value = part.parse::<u64>().unwrap() + 1;
// *part = &new_value.clone().to_string();
bumped = true;
if let Some(part) = parsed.get_mut(label) {
if label == args.bump {
if let Ok(new_value) = part.parse::<u64>() {
*part = (new_value + 1).to_string();
bumped = true;
} else {
eprintln!("Failed to parse '{}' as u64", part);
return None;
}
} else if bumped {
*part = "0".to_string();
}
} else if bumped {
parsed.insert(label, "0");
}
}
if bumped {
let new_version = args.serialize.replace(
|c| c == '{' || c == '}',
parsed.get(&"{").unwrap_or(&"").to_string().as_str(), // TODO: fix c
let new_version = format!(
"{}.{}.{}",
parsed.get("major").unwrap_or(&"0".to_string()),
parsed.get("minor").unwrap_or(&"0".to_string()),
parsed.get("patch").unwrap_or(&"0".to_string())
);
Some(new_version)
let version = args
.serialize
.replace("{major}.{minor}.{patch}", &new_version);
Some(version)
} else {
None
}