@@ -35,6 +35,7 @@ use std::{ffi::OsStr, fs, path::Path, process::Command};
3535use clap:: { Parser , Subcommand } ;
3636use cmd_lib:: run_cmd;
3737use current_platform:: CURRENT_PLATFORM ;
38+ use regex:: Regex ;
3839
3940// ### Local
4041//
@@ -64,6 +65,11 @@ enum Commands {
6465 Test ,
6566 /// Build everything.
6667 Build ,
68+ /// Change the version for the client, server, and extensions.
69+ ChangeVersion {
70+ /// The new version number, such as "0.1.1".
71+ new_version : String ,
72+ } ,
6773 /// Steps to run before `cargo dist build`.
6874 Prerelease ,
6975 /// Steps to run after `cargo dist build`. This builds and publishes a
@@ -228,6 +234,29 @@ fn remove_dir_all_if_exists<P: AsRef<Path> + std::fmt::Display>(
228234 Ok ( ( ) )
229235}
230236
237+ fn search_and_replace_file <
238+ P : AsRef < Path > + std:: fmt:: Display ,
239+ S1 : AsRef < str > + std:: fmt:: Display ,
240+ S2 : AsRef < str > ,
241+ > (
242+ path : P ,
243+ search_regex : S1 ,
244+ replace_string : S2 ,
245+ ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
246+ let file_contents = fs:: read_to_string ( & path)
247+ . map_err ( |err| -> String { format ! ( "Unable to open file {path} for reading: {err}" ) } ) ?;
248+ let re = Regex :: new ( search_regex. as_ref ( ) )
249+ . map_err ( |err| -> String { format ! ( "Error in search regex {search_regex}: {err}" ) } ) ?;
250+ let file_contents_replaced = re. replace ( & file_contents, replace_string. as_ref ( ) ) ;
251+ assert_ne ! (
252+ file_contents, file_contents_replaced,
253+ "No replacements made."
254+ ) ;
255+ fs:: write ( & path, file_contents_replaced. as_bytes ( ) )
256+ . map_err ( |err| -> String { format ! ( "Error writing to {path}: {err}" ) } ) ?;
257+ Ok ( ( ) )
258+ }
259+
231260// ## Core routines
232261//
233262// These functions simplify common build-focused development tasks and support
@@ -375,6 +404,27 @@ fn run_build() -> Result<(), Box<dyn std::error::Error>> {
375404 Ok ( ( ) )
376405}
377406
407+ fn run_change_version ( new_version : & String ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
408+ let replacement_string = format ! ( "${{1}}{new_version}${{2}}" ) ;
409+ search_and_replace_file (
410+ "Cargo.toml" ,
411+ r#"(\r?\nversion = ")[\d.]+("\r?\n)"# ,
412+ & replacement_string,
413+ ) ?;
414+ let json_search_regex = r#"(\r?\n "version": ")[\d.]+(",\r?\n)"# ;
415+ search_and_replace_file (
416+ "../client/package.json" ,
417+ json_search_regex,
418+ & replacement_string,
419+ ) ?;
420+ search_and_replace_file (
421+ "../extensions/VSCode/package.json" ,
422+ json_search_regex,
423+ & replacement_string,
424+ ) ?;
425+ Ok ( ( ) )
426+ }
427+
378428fn run_prerelease ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
379429 // Clean out all bundled files before the rebuild.
380430 remove_dir_all_if_exists ( "../client/static/bundled" ) ?;
@@ -424,6 +474,7 @@ impl Cli {
424474 Commands :: Update => run_update ( ) ,
425475 Commands :: Test => run_test ( ) ,
426476 Commands :: Build => run_build ( ) ,
477+ Commands :: ChangeVersion { new_version } => run_change_version ( new_version) ,
427478 Commands :: Prerelease => run_prerelease ( ) ,
428479 Commands :: Postrelease { target, .. } => run_postrelease ( target) ,
429480 }
0 commit comments