feat(install): bundle before installation (#5276)

This commit is contained in:
Yoshiya Hinosawa 2020-09-05 20:31:14 +09:00 committed by GitHub
parent a14b3c9e30
commit 34e98fa59c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 116 additions and 134 deletions

View file

@ -1,5 +1,8 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
use crate::flags::Flags; use crate::flags::Flags;
use crate::global_state::GlobalState;
use deno_core::ErrBox;
use deno_core::ModuleSpecifier;
use log::Level; use log::Level;
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
use std::env; use std::env;
@ -79,14 +82,18 @@ deno {} "$@"
Ok(()) Ok(())
} }
fn generate_config_file( async fn generate_bundle(
file_path: PathBuf, flags: Flags,
config_file_name: String, module_specifier: ModuleSpecifier,
) -> Result<(), Error> { script_path: PathBuf,
let config_file_copy_path = get_config_file_path(&file_path); ) -> Result<(), ErrBox> {
let cwd = std::env::current_dir().unwrap(); let global_state = GlobalState::new(flags.clone())?;
let config_file_path = cwd.join(config_file_name); let source = global_state
fs::copy(config_file_path, config_file_copy_path)?; .ts_compiler
.bundle(&global_state, module_specifier)
.await?;
let mut file = File::create(&script_path)?;
file.write_all(source.as_bytes())?;
Ok(()) Ok(())
} }
@ -127,14 +134,14 @@ fn infer_name_from_url(url: &Url) -> Option<String> {
Some(stem) Some(stem)
} }
pub fn install( pub async fn install(
flags: Flags, flags: Flags,
module_url: &str, module_url: &str,
args: Vec<String>, args: Vec<String>,
name: Option<String>, name: Option<String>,
root: Option<PathBuf>, root: Option<PathBuf>,
force: bool, force: bool,
) -> Result<(), Error> { ) -> Result<(), ErrBox> {
let root = if let Some(root) = root { let root = if let Some(root) = root {
root.canonicalize()? root.canonicalize()?
} else { } else {
@ -145,56 +152,47 @@ pub fn install(
// ensure directory exists // ensure directory exists
if let Ok(metadata) = fs::metadata(&installation_dir) { if let Ok(metadata) = fs::metadata(&installation_dir) {
if !metadata.is_dir() { if !metadata.is_dir() {
return Err(Error::new( return Err(ErrBox::from(Error::new(
ErrorKind::Other, ErrorKind::Other,
"Installation path is not a directory", "Installation path is not a directory",
)); )));
} }
} else { } else {
fs::create_dir_all(&installation_dir)?; fs::create_dir_all(&installation_dir)?;
}; };
// Check if module_url is remote let module_specifier = ModuleSpecifier::resolve_url_or_path(module_url)?;
let module_url = if is_remote_url(module_url) { let module_specifier_ = module_specifier.clone();
Url::parse(module_url).expect("Should be valid url") let url = module_specifier_.as_url();
} else {
let module_path = PathBuf::from(module_url);
let module_path = if module_path.is_absolute() {
module_path
} else {
let cwd = env::current_dir().unwrap();
cwd.join(module_path)
};
Url::from_file_path(module_path).expect("Path should be absolute")
};
let name = name.or_else(|| infer_name_from_url(&module_url)); let name = name.or_else(|| infer_name_from_url(&url));
let name = match name { let name = match name {
Some(name) => name, Some(name) => name,
None => return Err(Error::new( None => return Err(ErrBox::from(Error::new(
ErrorKind::Other, ErrorKind::Other,
"An executable name was not provided. One could not be inferred from the URL. Aborting.", "An executable name was not provided. One could not be inferred from the URL. Aborting.",
)), ))),
}; };
validate_name(name.as_str())?; validate_name(name.as_str())?;
let mut file_path = installation_dir.join(&name); let mut file_path = installation_dir.join(&name);
let script_path = file_path.with_extension("js");
if cfg!(windows) { if cfg!(windows) {
file_path = file_path.with_extension("cmd"); file_path = file_path.with_extension("cmd");
} }
if file_path.exists() && !force { if file_path.exists() && !force {
return Err(Error::new( return Err(ErrBox::from(Error::new(
ErrorKind::Other, ErrorKind::Other,
"Existing installation found. Aborting (Use -f to overwrite).", "Existing installation found. Aborting (Use -f to overwrite).",
)); )));
}; };
let mut executable_args = vec!["run".to_string()]; let mut executable_args = vec!["run".to_string()];
executable_args.extend_from_slice(&flags.to_permission_args()); executable_args.extend_from_slice(&flags.to_permission_args());
if let Some(ca_file) = flags.ca_file { if let Some(ca_file) = flags.ca_file.clone() {
executable_args.push("--cert".to_string()); executable_args.push("--cert".to_string());
executable_args.push(ca_file) executable_args.push(ca_file)
} }
@ -207,10 +205,10 @@ pub fn install(
Level::Debug => "debug", Level::Debug => "debug",
Level::Info => "info", Level::Info => "info",
_ => { _ => {
return Err(Error::new( return Err(ErrBox::from(Error::new(
ErrorKind::Other, ErrorKind::Other,
format!("invalid log level {}", log_level), format!("invalid log level {}", log_level),
)) )))
} }
}; };
executable_args.push(log_level.to_string()); executable_args.push(log_level.to_string());
@ -225,22 +223,11 @@ pub fn install(
executable_args.push("--unstable".to_string()); executable_args.push("--unstable".to_string());
} }
if flags.config_path.is_some() { executable_args.push(script_path.to_string_lossy().to_string());
let config_file_path = get_config_file_path(&file_path);
let config_file_path_option = config_file_path.to_str();
if let Some(config_file_path_string) = config_file_path_option {
executable_args.push("--config".to_string());
executable_args.push(config_file_path_string.to_string());
}
}
executable_args.push(module_url.to_string());
executable_args.extend_from_slice(&args); executable_args.extend_from_slice(&args);
generate_executable_file(file_path.to_owned(), executable_args)?; generate_executable_file(file_path.to_owned(), executable_args)?;
if let Some(config_path) = flags.config_path { generate_bundle(flags, module_specifier, script_path).await?;
generate_config_file(file_path.to_owned(), config_path)?;
}
println!("✅ Successfully installed {}", name); println!("✅ Successfully installed {}", name);
println!("{}", file_path.to_string_lossy()); println!("{}", file_path.to_string_lossy());
@ -269,12 +256,6 @@ fn is_in_path(dir: &PathBuf) -> bool {
false false
} }
fn get_config_file_path(file_path: &PathBuf) -> PathBuf {
let mut config_file_copy_path = PathBuf::from(file_path);
config_file_copy_path.set_extension("tsconfig.json");
config_file_copy_path
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -368,9 +349,10 @@ mod tests {
); );
} }
#[test] #[tokio::test]
fn install_basic() { async fn install_basic() {
let _guard = ENV_LOCK.lock().ok(); let _guard = ENV_LOCK.lock().ok();
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let temp_dir_str = temp_dir.path().to_string_lossy().to_string(); let temp_dir_str = temp_dir.path().to_string_lossy().to_string();
// NOTE: this test overrides environmental variables // NOTE: this test overrides environmental variables
@ -392,6 +374,7 @@ mod tests {
None, None,
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = temp_dir.path().join(".deno/bin/echo_test"); let mut file_path = temp_dir.path().join(".deno/bin/echo_test");
@ -405,8 +388,8 @@ mod tests {
// It's annoying when shell scripts don't have NL at the end. // It's annoying when shell scripts don't have NL at the end.
assert_eq!(content.chars().last().unwrap(), '\n'); assert_eq!(content.chars().last().unwrap(), '\n');
assert!(content assert!(content.contains(r#""run""#));
.contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); assert!(content.contains(r#"echo_test.js"#));
if let Some(home) = original_home { if let Some(home) = original_home {
env::set_var("HOME", home); env::set_var("HOME", home);
} }
@ -418,8 +401,9 @@ mod tests {
} }
} }
#[test] #[tokio::test]
fn install_unstable() { async fn install_unstable() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -435,6 +419,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -443,16 +428,17 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
println!("this is the file path {:?}", content); println!("this is the file path {:?}", content);
assert!(content.contains( assert!(content.contains(r#""run" "--unstable""#));
r#""run" "--unstable" "http://localhost:4545/cli/tests/echo_server.ts"# assert!(content.contains(r#"echo_test.js"#));
));
} }
#[test] #[tokio::test]
fn install_inferred_name() { async fn install_inferred_name() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -465,6 +451,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_server"); let mut file_path = bin_dir.join("echo_server");
@ -473,13 +460,16 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content assert!(content.contains(r#""run""#));
.contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); assert!(content.contains(r#"echo_server.js"#));
} }
#[test] #[tokio::test]
fn install_inferred_name_from_parent() { async fn install_inferred_name_from_parent() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -492,6 +482,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("subdir"); let mut file_path = bin_dir.join("subdir");
@ -500,13 +491,17 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content assert!(content.contains(r#""run""#));
.contains(r#""run" "http://localhost:4545/cli/tests/subdir/main.ts""#)); assert!(content.contains(r#"subdir.js"#));
} }
#[test] #[tokio::test]
fn install_custom_dir_option() { async fn install_custom_dir_option() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -519,6 +514,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -527,14 +523,17 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content assert!(content.contains(r#""run""#));
.contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); assert!(content.contains(r#"echo_test.js"#));
} }
#[test] #[tokio::test]
fn install_custom_dir_env_var() { async fn install_custom_dir_env_var() {
let _guard = ENV_LOCK.lock().ok(); let _guard = ENV_LOCK.lock().ok();
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -549,6 +548,7 @@ mod tests {
None, None,
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -557,16 +557,20 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content assert!(content.contains(r#""run""#));
.contains(r#""run" "http://localhost:4545/cli/tests/echo_server.ts""#)); assert!(content.contains(r#"echo_test.js"#));
if let Some(install_root) = original_install_root { if let Some(install_root) = original_install_root {
env::set_var("DENO_INSTALL_ROOT", install_root); env::set_var("DENO_INSTALL_ROOT", install_root);
} }
} }
#[test] #[tokio::test]
fn install_with_flags() { async fn install_with_flags() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -585,6 +589,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -593,17 +598,21 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content.contains(r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check" "http://localhost:4545/cli/tests/echo_server.ts" "--foobar""#)); assert!(content.contains(
r#""run" "--allow-read" "--allow-net" "--quiet" "--no-check""#
));
assert!(content.contains(r#"echo_test.js" "--foobar""#));
} }
#[test] #[tokio::test]
fn install_local_module() { async fn install_local_module() {
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
let local_module = env::current_dir().unwrap().join("echo_server.ts"); let local_module = env::current_dir().unwrap().join("tests/echo_server.ts");
let local_module_url = Url::from_file_path(&local_module).unwrap();
let local_module_str = local_module.to_string_lossy(); let local_module_str = local_module.to_string_lossy();
install( install(
@ -614,6 +623,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -622,12 +632,14 @@ mod tests {
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
let content = fs::read_to_string(file_path).unwrap(); let content = fs::read_to_string(file_path).unwrap();
assert!(content.contains(&local_module_url.to_string())); assert!(content.contains("echo_test.js"));
} }
#[test] #[tokio::test]
fn install_force() { async fn install_force() {
let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new().expect("tempdir fail"); let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin"); let bin_dir = temp_dir.path().join("bin");
std::fs::create_dir(&bin_dir).unwrap(); std::fs::create_dir(&bin_dir).unwrap();
@ -640,6 +652,7 @@ mod tests {
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
) )
.await
.expect("Install failed"); .expect("Install failed");
let mut file_path = bin_dir.join("echo_test"); let mut file_path = bin_dir.join("echo_test");
@ -647,16 +660,21 @@ mod tests {
file_path = file_path.with_extension("cmd"); file_path = file_path.with_extension("cmd");
} }
assert!(file_path.exists()); assert!(file_path.exists());
assert!(file_path.with_extension("js").exists());
// No force. Install failed. // No force. Install failed.
let no_force_result = install( let no_force_result = install(
Flags::default(), Flags {
unstable: true,
..Flags::default()
},
"http://localhost:4545/cli/tests/cat.ts", // using a different URL "http://localhost:4545/cli/tests/cat.ts", // using a different URL
vec![], vec![],
Some("echo_test".to_string()), Some("echo_test".to_string()),
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
false, false,
); )
.await;
assert!(no_force_result.is_err()); assert!(no_force_result.is_err());
assert!(no_force_result assert!(no_force_result
.unwrap_err() .unwrap_err()
@ -664,52 +682,24 @@ mod tests {
.contains("Existing installation found")); .contains("Existing installation found"));
// Assert not modified // Assert not modified
let file_content = fs::read_to_string(&file_path).unwrap(); let file_content = fs::read_to_string(&file_path).unwrap();
assert!(file_content.contains("echo_server.ts")); assert!(!file_content.contains("--unstable"));
// Force. Install success. // Force. Install success.
let force_result = install( let force_result = install(
Flags::default(), Flags {
unstable: true,
..Flags::default()
},
"http://localhost:4545/cli/tests/cat.ts", // using a different URL "http://localhost:4545/cli/tests/cat.ts", // using a different URL
vec![], vec![],
Some("echo_test".to_string()), Some("echo_test".to_string()),
Some(temp_dir.path().to_path_buf()), Some(temp_dir.path().to_path_buf()),
true, true,
); )
.await;
assert!(force_result.is_ok()); assert!(force_result.is_ok());
// Assert modified // Assert modified
let file_content_2 = fs::read_to_string(&file_path).unwrap(); let file_content_2 = fs::read_to_string(&file_path).unwrap();
assert!(file_content_2.contains("cat.ts")); assert!(file_content_2.contains("--unstable"));
}
#[test]
fn install_with_config() {
let temp_dir = TempDir::new().expect("tempdir fail");
let bin_dir = temp_dir.path().join("bin");
let config_file_path = temp_dir.path().join("test_tsconfig.json");
let config = "{}";
let mut config_file = File::create(&config_file_path).unwrap();
let result = config_file.write_all(config.as_bytes());
assert!(result.is_ok());
let result = install(
Flags {
config_path: Some(config_file_path.to_string_lossy().to_string()),
..Flags::default()
},
"http://localhost:4545/cli/tests/cat.ts",
vec![],
Some("echo_test".to_string()),
Some(temp_dir.path().to_path_buf()),
true,
);
eprintln!("result {:?}", result);
assert!(result.is_ok());
let config_file_name = "echo_test.tsconfig.json";
let file_path = bin_dir.join(config_file_name.to_string());
assert!(file_path.exists());
let content = fs::read_to_string(file_path).unwrap();
assert!(content == "{}");
} }
} }

View file

@ -304,15 +304,7 @@ async fn install_command(
root: Option<PathBuf>, root: Option<PathBuf>,
force: bool, force: bool,
) -> Result<(), ErrBox> { ) -> Result<(), ErrBox> {
// Firstly fetch and compile module, this step ensures that module exists. installer::install(flags, &module_url, args, name, root, force).await
let mut fetch_flags = flags.clone();
fetch_flags.reload = true;
let global_state = GlobalState::new(fetch_flags)?;
let main_module = ModuleSpecifier::resolve_url_or_path(&module_url)?;
let mut worker = MainWorker::create(&global_state, main_module.clone())?;
worker.preload_module(&main_module).await?;
installer::install(flags, &module_url, args, name, root, force)
.map_err(ErrBox::from)
} }
async fn lint_command( async fn lint_command(