diff --git a/cli/flags.rs b/cli/flags.rs index e0b8d807c7..bcc04d94de 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -70,6 +70,7 @@ pub enum DenoSubcommand { dry_run: bool, force: bool, version: Option, + output: Option, ca_file: Option, }, } @@ -569,11 +570,18 @@ fn upgrade_parse(flags: &mut Flags, matches: &clap::ArgMatches) { let dry_run = matches.is_present("dry-run"); let force = matches.is_present("force"); let version = matches.value_of("version").map(|s| s.to_string()); + let output = if matches.is_present("output") { + let install_root = matches.value_of("output").unwrap(); + Some(PathBuf::from(install_root)) + } else { + None + }; let ca_file = matches.value_of("cert").map(|s| s.to_string()); flags.subcommand = DenoSubcommand::Upgrade { dry_run, force, version, + output, ca_file, }; } @@ -848,7 +856,11 @@ Defaults to latest. The version is downloaded from https://github.com/denoland/deno/releases -and is used to replace the current executable.", +and is used to replace the current executable. + +If you want to not replace the current Deno executable but instead download an +update to a different location, use the --output flag + deno upgrade --output $HOME/my_deno", ) .arg( Arg::with_name("version") @@ -856,6 +868,12 @@ and is used to replace the current executable.", .help("The version to upgrade to") .takes_value(true), ) + .arg( + Arg::with_name("output") + .long("output") + .help("The path to output the updated version to") + .takes_value(true), + ) .arg( Arg::with_name("dry-run") .long("dry-run") @@ -1400,7 +1418,8 @@ mod tests { force: true, dry_run: true, version: None, - ca_file: None + output: None, + ca_file: None, }, ..Flags::default() } @@ -2637,6 +2656,7 @@ mod tests { force: false, dry_run: false, version: None, + output: None, ca_file: Some("example.crt".to_owned()), }, ca_file: Some("example.crt".to_owned()), diff --git a/cli/main.rs b/cli/main.rs index 7e61504d0a..5c54a87caf 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -733,8 +733,11 @@ pub fn main() { force, dry_run, version, + output, ca_file, - } => upgrade_command(dry_run, force, version, ca_file).boxed_local(), + } => { + upgrade_command(dry_run, force, version, output, ca_file).boxed_local() + } _ => unreachable!(), }; diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 5772c1a5c7..c88c595875 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -399,11 +399,7 @@ fn fmt_stdin_error() { #[test] fn upgrade_in_tmpdir() { let temp_dir = TempDir::new().unwrap(); - let exe_path = if cfg!(windows) { - temp_dir.path().join("deno") - } else { - temp_dir.path().join("deno.exe") - }; + let exe_path = temp_dir.path().join("deno"); let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap(); assert!(exe_path.exists()); let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap(); @@ -444,11 +440,7 @@ fn upgrade_with_space_in_path() { #[test] fn upgrade_with_version_in_tmpdir() { let temp_dir = TempDir::new().unwrap(); - let exe_path = if cfg!(windows) { - temp_dir.path().join("deno") - } else { - temp_dir.path().join("deno.exe") - }; + let exe_path = temp_dir.path().join("deno"); let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap(); assert!(exe_path.exists()); let _mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap(); @@ -471,6 +463,41 @@ fn upgrade_with_version_in_tmpdir() { // TODO(ry) assert!(mtime1 < mtime2); } +// Warning: this test requires internet access. +#[test] +fn upgrade_with_out_in_tmpdir() { + let temp_dir = TempDir::new().unwrap(); + let exe_path = temp_dir.path().join("deno"); + let new_exe_path = temp_dir.path().join("foo"); + let _ = std::fs::copy(util::deno_exe_path(), &exe_path).unwrap(); + assert!(exe_path.exists()); + let mtime1 = std::fs::metadata(&exe_path).unwrap().modified().unwrap(); + let status = Command::new(&exe_path) + .arg("upgrade") + .arg("--version") + .arg("1.0.2") + .arg("--output") + .arg(&new_exe_path.to_str().unwrap()) + .spawn() + .unwrap() + .wait() + .unwrap(); + assert!(status.success()); + assert!(new_exe_path.exists()); + let mtime2 = std::fs::metadata(&exe_path).unwrap().modified().unwrap(); + assert_eq!(mtime1, mtime2); // Original exe_path was not changed. + + let v = String::from_utf8( + Command::new(&new_exe_path) + .arg("-V") + .output() + .unwrap() + .stdout, + ) + .unwrap(); + assert!(v.contains("1.0.2")); +} + #[test] fn installer_test_local_module_run() { let temp_dir = TempDir::new().expect("tempdir fail"); diff --git a/cli/upgrade.rs b/cli/upgrade.rs index 9f3e0459de..244bf6c8af 100644 --- a/cli/upgrade.rs +++ b/cli/upgrade.rs @@ -56,6 +56,7 @@ pub async fn upgrade_command( dry_run: bool, force: bool, version: Option, + output: Option, ca_file: Option, ) -> Result<(), ErrBox> { let mut client_builder = Client::builder().redirect(Policy::none()); @@ -76,7 +77,7 @@ pub async fn upgrade_command( Ok(ver) => { if !force && current_version == ver { println!("Version {} is already installed", &ver); - std::process::exit(1) + return Ok(()); } else { ver } @@ -94,7 +95,7 @@ pub async fn upgrade_command( "Local deno version {} is the most recent release", &crate::version::DENO ); - std::process::exit(1) + return Ok(()); } else { latest_version } @@ -114,7 +115,13 @@ pub async fn upgrade_command( check_exe(&new_exe_path, &install_version)?; if !dry_run { - replace_exe(&new_exe_path, &old_exe_path)?; + match output { + Some(path) => { + fs::rename(&new_exe_path, &path) + .or_else(|_| fs::copy(&new_exe_path, &path).map(|_| ()))?; + } + None => replace_exe(&new_exe_path, &old_exe_path)?, + } } println!("Upgrade done successfully");