From 5bfd81666d60be60fd06e44d3b522644116fc821 Mon Sep 17 00:00:00 2001 From: wiseaidev Date: Mon, 19 Feb 2024 16:50:38 +0200 Subject: [PATCH] init release --- .bumpversion.cfg | 22 ++++ .gitignore | 15 +-- Cargo.lock | 268 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 25 +++++ README.md | 63 ++++++++++- src/cli.rs | 121 +++++++++++++++++++++ src/lib.rs | 57 ++++++++++ src/main.rs | 111 ++++++++++++++++++++ src/utils.rs | 50 +++++++++ 9 files changed, 716 insertions(+), 16 deletions(-) create mode 100644 .bumpversion.cfg create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100755 src/cli.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/utils.rs diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..8103f3f --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,22 @@ +[bumpversion] +current_version = 0.1.0 +commit = True +tag = True +parse = (?P\d+)\.(?P\d+)\.(?P\d+)(-(?P[^.]*)\.(?P\d+))? +serialize = + {major}.{minor}.{patch}-{stage}.{devnum} + {major}.{minor}.{patch} + +[bumpversion:part:stage] +optional_value = stable +first_value = stable +values = + alpha + beta + stable + +[bumpversion:part:devnum] + +[bumpversion:file:Cargo.toml] +search = version = "{current_version}" +replace = version = "{new_version}" diff --git a/.gitignore b/.gitignore index 6985cf1..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1 @@ -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2ff24e9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,268 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "bump2version" +version = "0.1.0" +dependencies = [ + "clap", + "regex", +] + +[[package]] +name = "clap" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] +name = "syn" +version = "2.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9244bf6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "bump2version" +version = "0.1.0" +edition = "2021" +description = "⬆️ Easily manage version numbers in your projects." +license = "MIT" +keywords = ["cli", "parser"] +categories = ["command-line-utilities"] +repository = "https://github.com/wiseaidev/bump2version" +documentation = "https://docs.rs/bump2version" +authors = ["Mahmoud Harmouch "] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib"] + +[dependencies] +clap = { version = "4.5.1", features = ["derive"] } +regex = "1.10.3" + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = "thin" +strip = "symbols" diff --git a/README.md b/README.md index 1dd5ebb..74cf1e2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# bump2version -A simple rust-based implementation of python bump2version package. +# ⬆️ Bump2version + +[![Crates.io](https://img.shields.io/crates/v/bump2version.svg)](https://crates.io/crates/bump2version) +[![docs](https://docs.rs/bump2version/badge.svg)](https://docs.rs/bump2version/) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +> 🚀 **bump2version**: A command-line tool for managing version numbers in your projects. + +## 📖 Table of Contents + +- [Quick Start](#-quick-start) +- [Features](#-features) +- [Options](#-options) +- [GitHub Repository](#-github-repository) +- [Contributing](#-contributing) + +## 🚀 Quick Start + +Get started with the `bump2version` CLI by following these simple steps: + +1. Install the `bump2version` tool using Cargo: + + ```bash + cargo install bump2version + ``` + +1. Use the following options to manage version numbers and customize the behavior: + + ```bash + bump2version --current-version 1.2.3 --bump patch + ``` + +## ✨ Features + +- **Incremental Versioning:** Bump major, minor, or patch versions with ease. +- **Configurability:** Use a configuration file or command-line options to customize behavior. +- **Git Integration:** Create commits and tags in your version control system. + +## 🎨 Options + +| Option | Description | +|------------------------|-------------------------------------------------------------------| +| `--config-file` | Config file to read most of the variables from. | +| `--current-version` | Version that needs to be updated. | +| `--bump` | Part of the version to be bumped (default: patch). | +| `--parse` | Regex parsing the version string (default: \d+\.\d+\.\d+). | +| `--serialize` | How to format what is parsed back to a version (default: {major}.{minor}.{patch}). | +| `--dry-run` | Don't write any files, just pretend. | +| `--new-version` | New version that should be in the files. | +| `--commit` | Create a commit in version control (default: true). | +| `--tag` | Create a tag in version control. | +| `--message` | Commit message (default: Bump version: {current_version} → {new_version}). | +| `file` | Files to change. | + +## 🌐 GitHub Repository + +You can access the source code for this CLI tool on [GitHub](https://github.com/wiseaidev/bump2version). + +## 🤝 Contributing + +Contributions and feedback are welcome! If you'd like to contribute, report an issue, or suggest an enhancement, please engage with the project on [GitHub](https://github.com/wiseaidev/bump2version). Your contributions help improve this CLI tool for the community. diff --git a/src/cli.rs b/src/cli.rs new file mode 100755 index 0000000..a46d5df --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,121 @@ +use clap::builder::styling::{AnsiColor, Effects, Styles}; +use clap::Parser; + +fn styles() -> Styles { + Styles::styled() + .header(AnsiColor::Red.on_default() | Effects::BOLD) + .usage(AnsiColor::Red.on_default() | Effects::BOLD) + .literal(AnsiColor::Blue.on_default() | Effects::BOLD) + .error(AnsiColor::Red.on_default() | Effects::BOLD) + .placeholder(AnsiColor::Green.on_default()) +} + +#[derive(Parser, Debug, Clone)] +#[command( + author = "Mahmoud Harmouch", + version, + name = "bump2version", + propagate_version = true, + styles = styles(), + help_template = r#"{before-help}{name} {version} +{about-with-newline} + +{usage-heading} {usage} + +{all-args}{after-help} + +AUTHORS: + {author} +"#, +about=r#" +🚀 Bump2version CLI +=================== + +Bump2version CLI is a command-line tool for managing version numbers in your projects. +Easily update version strings, create commits, and manage version control tags. + +FEATURES: + - Incremental Versioning: Bump major, minor, or patch versions with ease. + - Configurability: Use a configuration file or command-line options to customize behavior. + - Git Integration: Create commits and tags in your version control system. + +USAGE: + bump2version [OPTIONS] + +EXAMPLES: + Bump patch version: + bump2version --current-version 1.2.3 --bump patch + + Bump minor version and create a commit: + bump2version --current-version 1.2.3 --bump minor --commit + +For more information, visit: https://github.com/wiseaidev/bump2version +"# +)] +pub struct Cli { + /// Config file to read most of the variables from. + #[arg( + short = 'c', + long = "config-file", + value_name = "FILE", + default_value_t = String::from(".bumpversion.toml") + )] + pub config_file: String, + + /// Version that needs to be updated. + #[arg(long = "current-version", value_name = "VERSION")] + pub current_version: String, + + /// Part of the version to be bumped. + #[arg( + long = "bump", + value_name = "PART", + default_value_t = String::from("patch") + )] + pub bump: String, + + /// Regex parsing the version string. + #[arg( + long = "parse", + value_name = "REGEX", + default_value_t = String::from(r"(?P\d+)\.(?P\d+)\.(?P\d+)") + )] + pub parse: String, + + /// How to format what is parsed back to a version. + #[arg( + long = "serialize", + value_name = "FORMAT", + default_value_t = String::from("{major}.{minor}.{patch}") + )] + pub serialize: String, + + /// Don't write any files, just pretend. + #[arg(short = 'n', long = "dry-run")] + pub dry_run: bool, + + /// New version that should be in the files. + #[arg(long = "new-version", value_name = "VERSION")] + pub new_version: String, + + /// Create a commit in version control. + #[arg(long = "commit", default_value_t = true)] + pub commit: bool, + + /// Create a tag in version control. + #[arg(long = "tag")] + pub tag: bool, + + /// Commit message. + #[arg( + short = 'm', + long = "message", + value_name = "COMMIT_MSG", + default_value_t = String::from("Bump version: {current_version} → {new_version}") + )] + pub message: String, + + /// Files to change. + #[arg(value_name = "file")] + pub files: Vec, +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cb53ad7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,57 @@ +//! # ⬆️ Bump2version +//! +//! Bump2version is a command-line tool for managing version numbers in your projects. +//! Easily update version strings, create commits, and manage version control tags. +//! +//! ## Features +//! +//! - **Incremental Versioning:** Bump major, minor, or patch versions with ease. +//! - **Configurability:** Use a configuration file or command-line options to customize behavior. +//! - **Git Integration:** Create commits and tags in your version control system. +//! +//! ## Quick Start +//! +//! Get started with the `bump2version` CLI by following these simple steps: +//! +//! 1. Install the `bump2version` tool using Cargo: +//! +//! ```bash +//! cargo install bump2version +//! ``` +//! +//! 2. Use the following options to manage version numbers and customize the behavior: +//! +//! ```bash +//! bump2version --current-version 1.2.3 --bump patch +//! ``` +//! +//! ## Options +//! +//! | Option | Description | +//! |------------------------|-------------------------------------------------------------------| +//! | `--config-file` | Config file to read most of the variables from. | +//! | `--current-version` | Version that needs to be updated. | +//! | `--bump` | Part of the version to be bumped (default: patch). | +//! | `--parse` | Regex parsing the version string (default: \d+\.\d+\.\d+). | +//! | `--serialize` | How to format what is parsed back to a version (default: {major}.{minor}.{patch}). | +//! | `--dry-run` | Don't write any files, just pretend. | +//! | `--new-version` | New version that should be in the files. | +//! | `--commit` | Create a commit in version control (default: true). | +//! | `--tag` | Create a tag in version control. | +//! | `--message` | Commit message (default: Bump version: {current_version} → {new_version}). | +//! | `file` | Files to change. | +//! +//! ## GitHub Repository +//! +//! You can access the source code for this CLI tool on [GitHub](https://github.com/wiseaidev/bump2version). +//! +//! ## Contributing +//! +//! Contributions and feedback are welcome! If you'd like to contribute, report an issue, or suggest an enhancement, +//! please engage with the project on [GitHub](https://github.com/wiseaidev/bump2version). +//! Your contributions help improve this CLI tool for the community. +//! +//! **Manage your project versions with ease! 🚀** + +pub mod cli; +pub mod utils; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..58e5c4c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,111 @@ +use self::cli::Cli; +use crate::utils::attempt_version_bump; +use clap::Parser; +use std::fs; +use std::process::Command; + +mod cli; +mod utils; + +fn main() -> Result<(), Box> { + // Parse command-line arguments + let args = Cli::parse(); + let config_file = args.config_file.clone(); + let current_version = args.current_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(); + + let dry_run = args.dry_run; + let commit = args.commit; + let tag = args.tag; + let message = args.message; + + let files: Vec = args.files; + + // Check if Git working directory is clean + if fs::metadata(".git").is_ok() { + let git_status = Command::new("git") + .arg("status") + .arg("--porcelain") + .output()?; + + let git_output = String::from_utf8_lossy(&git_status.stdout); + let git_lines: Vec<&str> = git_output + .lines() + .filter(|line| !line.trim().starts_with("??")) + .collect(); + + if !git_lines.is_empty() { + panic!("Git working directory not clean:\n{}", git_lines.join("\n")); + } + } + + // Update version in specified files + for path in &files { + let content = fs::read_to_string(path)?; + + if !content.contains(¤t_version) { + panic!("Did not find string {} in file {}", current_version, path); + } + + let updated_content = content.replace(¤t_version, &new_version); + + if !dry_run { + fs::write(path, updated_content)?; + } + } + + let mut commit_files = files.clone(); + + // Update config file if applicable + if fs::metadata(config_file.clone()).is_ok() { + 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), + ); + + if !dry_run { + fs::write(config_file.clone(), config_content)?; + commit_files.push(config_file); + } + } + + // Git commit and tag + if commit { + for path in &commit_files { + Command::new("git").arg("add").arg(path).output()?; + } + + Command::new("git") + .arg("commit") + .arg("-m") + .arg( + message + .replace("{current_version}", ¤t_version) + .replace("{new_version}", &new_version), + ) + .output()?; + + if tag { + Command::new("git") + .arg("tag") + .arg(format!("v{}", new_version)) + .output()?; + } + } + } else { + println!("No files specified"); + } + + Ok(()) +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..b8f327c --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,50 @@ +use crate::cli::Cli; +use regex::Regex; +use std::collections::HashMap; + +pub fn attempt_version_bump(args: Cli) -> Option { + let parse_regex = args.parse; + let regex = Regex::new(&parse_regex).ok()?; + + let current_version = args.current_version; + let match_result = regex.captures_iter(¤t_version); + + 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()); + } + } + + let order: Vec<&str> = args + .serialize + .match_indices('{') + .map(|(i, _)| args.serialize[i + 1..].split('}').next().unwrap()) + .map(|s| s.trim()) + .collect(); + + let mut bumped = true; + for label in order { + if label == args.bump { + if let Some(_part) = parsed.get_mut(label) { + // TODO: fix + // let new_value = part.parse::().unwrap() + 1; + // *part = &new_value.clone().to_string(); + bumped = true; + } + } 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 + ); + Some(new_version) + } else { + None + } +}