diff --git a/Cargo.lock b/Cargo.lock index e7d3e7bc03c..dbb54d4c1ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5331,6 +5331,17 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "upgrade-version" +version = "0.1.0" +dependencies = [ + "clap 4.3.10", + "duct", + "regex", + "tempfile", + "walkdir", +] + [[package]] name = "url" version = "2.4.0" diff --git a/Cargo.toml b/Cargo.toml index f9586c56351..845f28e317f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ members = [ "crates/sdk/tests/test-counter", "crates/sdk/tests/test-client", "crates/sdk/tests/connect_disconnect_client", + "tools/upgrade-version", ] default-members = ["crates/cli"] # cargo feature graph resolver. v2 is default in edition2021 but workspace diff --git a/tools/upgrade-version.sh b/tools/upgrade-version.sh deleted file mode 100755 index de472a20863..00000000000 --- a/tools/upgrade-version.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -if [ $# != 1 ] ; then - echo "Incorrect number of arguments. Expected: 1 Received: $#" - exit 1 -fi - -fsed() { - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i.sed_bak "$@" - rm -f "$2.sed_bak" - else - sed -i "$@" - fi -} - -version="$1" -declare -a crates=("bench" "bindings" "bindings-macro" "bindings-sys" "client-api-messages" "cli" "client-api" "core" "lib" "sats" "standalone" "testing" "client-api-messages" "sdk") -upgrade_version() { - toml=crates/$1/Cargo.toml - if [ ! -f "$toml" ] ; then - echo "Invalid crate: $1" - exit 1 - fi - - # Upgrade the crate version - if [[ $# -lt 2 ]] || [ "$2" != "--skip-version" ] ; then - fsed '3s/.*version.*/version = "'"${version}"'"/' "${toml}" - fi - - # Upgrade any dependencies - for crate in "${crates[@]}" ; do - if [[ $# -lt 2 ]] || [ "$2" != "--no-include-version" ] ; then - fsed 's/.*'"spacetimedb-${crate}"'\s*=.*/'"spacetimedb-${crate}"' = { path = "..\/'"${crate}"'", version = "'"$version"'" }/' "${toml}" - else - fsed 's/.*'"spacetimedb-${crate}"'\s*=.*/'"spacetimedb-${crate}"' = { path = "..\/'"${crate}"'" }/' "${toml}" - fi - done -} - -upgrade_version bench --no-include-version -upgrade_version client-api --no-include-version -upgrade_version core --no-include-version -upgrade_version standalone --no-include-version - -upgrade_version bindings -upgrade_version bindings-macro -upgrade_version bindings-sys -upgrade_version cli -upgrade_version lib -upgrade_version sats -upgrade_version sdk - -upgrade_version testing --skip-version - -# Upgrade the template that is shipped with the cli -fsed 's@.*spacetimedb.*=.*".*".*@spacetimedb = "'"${version}"'"@' "crates/cli/src/subcommands/project/rust/Cargo._toml" -fsed 's@.*spacetimedb-lib.*=.*@spacetimedb-lib = { path = "../lib", default-features = false }@' "crates/bindings/Cargo.toml" -fsed 's@.*spacetimedb-bindings-macro.*=.*@spacetimedb-bindings-macro = { path = "../bindings-macro" }@' "crates/bindings/Cargo.toml" - -# Maintain any other options -fsed 's@.*spacetimedb-lib.*=.*@spacetimedb-lib = { path = "../lib", default-features = false, version = "'"$version"'"}@' "crates/bindings/Cargo.toml" -fsed 's@.*spacetimedb-bindings-macro.*=.*@spacetimedb-bindings-macro = { path = "../bindings-macro", version = "'"$version"'"}@' "crates/bindings/Cargo.toml" - -cargo check - -printf "Upgrade to version %s was successful.\n\n" "${version}" diff --git a/tools/upgrade-version/Cargo.toml b/tools/upgrade-version/Cargo.toml new file mode 100644 index 00000000000..7168e676041 --- /dev/null +++ b/tools/upgrade-version/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "upgrade-version" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +walkdir = "2" +tempfile.workspace = true +clap.workspace = true +regex.workspace = true +duct.workspace = true diff --git a/tools/upgrade-version/src/main.rs b/tools/upgrade-version/src/main.rs new file mode 100644 index 00000000000..6b8b092c44c --- /dev/null +++ b/tools/upgrade-version/src/main.rs @@ -0,0 +1,165 @@ +#![allow(clippy::disallowed_macros)] + +extern crate clap; +extern crate walkdir; + +use clap::{Arg, Command}; +use duct::cmd; +use regex::Regex; +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::fs::File; +use std::io::BufRead; +use std::io::BufReader; +use std::io::Write; +use std::path::PathBuf; +use tempfile::NamedTempFile; +use walkdir::WalkDir; + +static IGNORE_FILES: [&str; 5] = [ + "crates/sdk/tests/connect_disconnect_client/Cargo.toml", + "crates/sdk/tests/test-client/Cargo.toml", + "crates/sdk/tests/test-counter/Cargo.toml", + "crates/sqltest/Cargo.toml", + "crates/testing/Cargo.toml", +]; + +fn find_files(start_dir: &str, name: &str) -> Vec { + let mut files = Vec::new(); + for entry in WalkDir::new(start_dir).into_iter().filter_map(|e| e.ok()) { + if entry.file_type().is_file() && entry.path().file_name() == Some(OsStr::new(name)) { + if IGNORE_FILES.contains(&entry.path().to_string_lossy().as_ref()) { + continue; + } + files.push(entry.path().to_string_lossy().to_string()); + } + } + files +} + +enum FileProcessState { + Package, + Dependencies, +} + +fn process_crate_toml(path: &PathBuf, upgrade_version: &str, upgrade_package_version: bool) { + println!("Processing file: {}", path.to_string_lossy()); + + let file = File::open(path).unwrap_or_else(|_| panic!("File not found: {}", path.to_string_lossy())); + let reader = BufReader::new(file); + let mut temp_file = NamedTempFile::new().expect("Failed to create temporary file!"); + let mut state = FileProcessState::Package; + let re = Regex::new(r#"(version = ")([^"]+)"#).unwrap(); + + for line_result in reader.lines() { + match line_result { + Ok(line) => { + let new_line = match state { + FileProcessState::Package => { + if line.contains("version = ") && upgrade_package_version { + re.replace(&line, format!("version = \"{}", upgrade_version).as_str()) + .into() + } else if line.contains("[dependencies]") { + state = FileProcessState::Dependencies; + line + } else { + line + } + } + FileProcessState::Dependencies => { + if line.starts_with("spacetimedb") { + if !line.contains('{') { + format!("spacetimedb = \"{}\"", upgrade_version) + } else { + // Match the version number and capture it + re.replace(&line, format!("version = \"{}", upgrade_version).as_str()) + .into() + } + } else { + line + } + } + }; + + writeln!(temp_file, "{}", new_line).unwrap(); + } + Err(e) => eprintln!("Error reading line: {}", e), + } + } + + // Rename the temporary file to replace the original file + fs::rename(temp_file.path(), path).expect("Failed to overwrite source file."); +} + +fn process_license_file(upgrade_version: &str) { + let path = "LICENSE.txt"; + let file = File::open(path).unwrap_or_else(|_| panic!("File not found: {}", path)); + let reader = BufReader::new(file); + let mut temp_file = NamedTempFile::new().expect("Failed to create temporary file!"); + let re = Regex::new(r"(^Licensed Work:\s+SpacetimeDB )([\d\.]+)").unwrap(); + + for line_result in reader.lines() { + match line_result { + Ok(line) => { + let new_line = if line.starts_with("Licensed Work") { + re.replace( + &line, + format!("{}{}", &re.captures(&line).unwrap()[1], upgrade_version).as_str(), + ) + .into() + } else { + line + }; + writeln!(temp_file, "{}", new_line).unwrap(); + } + Err(e) => eprintln!("Error reading line: {}", e), + } + } + + // Rename the temporary file to replace the original file + fs::rename(temp_file.path(), path).expect("Failed to overwrite source file."); +} + +fn main() { + let matches = Command::new("upgrade-version") + .version("1.0") + .about("Upgrades the version of the SpacetimeDB repository") + .arg( + Arg::new("upgrade_version") + .required(true) + .help("The version to upgrade to"), + ) + .arg( + Arg::new("spacetime-path") + .value_parser(clap::value_parser!(PathBuf)) + .default_value("../..") + .long("spacetime-path") + .help("The path to SpacetimeDB"), + ) + .get_matches(); + + let version = matches.get_one::("upgrade_version").unwrap(); + env::set_current_dir(matches.get_one::("spacetime-path").unwrap()).ok(); + + let current_dir = env::current_dir().expect("No current directory!"); + let dir_name = current_dir.file_name().expect("No current directory!"); + if dir_name != "SpacetimeDB" { + println!("You must execute this binary from inside of the SpacetimeDB directory, or use --spacetime-path"); + return; + } + + for file in find_files("crates", "Cargo.toml") { + process_crate_toml(&PathBuf::from(file), version, true); + } + for file in find_files("modules", "Cargo.toml") { + process_crate_toml(&PathBuf::from(file), version, false); + } + for file in find_files("crates", "Cargo._toml") { + process_crate_toml(&PathBuf::from(file), version, false); + } + + process_crate_toml(&PathBuf::from("crates/testing/Cargo.toml"), version, false); + process_license_file(version); + cmd!("cargo", "check").run().expect("Cargo check failed!"); +}