diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-05-25 14:27:45 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-25 14:27:45 -0400 |
commit | 2ebd61ee1b09c8060c2da66890bf6ac024d97b67 (patch) | |
tree | 014c6d35e1f820811a2ff6dcb0e96c60f360de1f | |
parent | 76400149a49f44b734b5cacf438722bc3c07c1d1 (diff) |
fix(compile): handle when DENO_DIR is readonly (#19257)
Closes #19253
-rw-r--r-- | cli/args/mod.rs | 13 | ||||
-rw-r--r-- | cli/cache/cache_db.rs | 1 | ||||
-rw-r--r-- | cli/cache/caches.rs | 49 | ||||
-rw-r--r-- | cli/cache/deno_dir.rs | 28 | ||||
-rw-r--r-- | cli/cache/mod.rs | 1 | ||||
-rw-r--r-- | cli/factory.rs | 18 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 19 | ||||
-rw-r--r-- | cli/standalone/mod.rs | 6 | ||||
-rw-r--r-- | cli/tests/integration/compile_tests.rs | 65 | ||||
-rw-r--r-- | cli/tools/repl/mod.rs | 6 |
10 files changed, 134 insertions, 72 deletions
diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 8b3d79308..6dab0c973 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -67,7 +67,6 @@ use std::path::PathBuf; use std::sync::Arc; use thiserror::Error; -use crate::cache::DenoDir; use crate::file_fetcher::FileFetcher; use crate::npm::CliNpmRegistryApi; use crate::npm::NpmProcessState; @@ -724,10 +723,6 @@ impl CliOptions { } } - pub fn resolve_deno_dir(&self) -> Result<DenoDir, AnyError> { - Ok(DenoDir::new(self.maybe_custom_root())?) - } - /// Based on an optional command line import map path and an optional /// configuration file, return a resolved module specifier to an import map /// and a boolean indicating if unknown keys should not result in diagnostics. @@ -1114,12 +1109,8 @@ impl CliOptions { &self.flags.location } - pub fn maybe_custom_root(&self) -> Option<PathBuf> { - self - .flags - .cache_path - .clone() - .or_else(|| env::var("DENO_DIR").map(String::into).ok()) + pub fn maybe_custom_root(&self) -> &Option<PathBuf> { + &self.flags.cache_path } pub fn no_clear_screen(&self) -> bool { diff --git a/cli/cache/cache_db.rs b/cli/cache/cache_db.rs index e05ecd962..fd694062c 100644 --- a/cli/cache/cache_db.rs +++ b/cli/cache/cache_db.rs @@ -109,7 +109,6 @@ impl Drop for CacheDB { } impl CacheDB { - #[cfg(test)] pub fn in_memory( config: &'static CacheDBConfiguration, version: &'static str, diff --git a/cli/cache/caches.rs b/cli/cache/caches.rs index 62bec8a00..f630a82ff 100644 --- a/cli/cache/caches.rs +++ b/cli/cache/caches.rs @@ -1,19 +1,20 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use std::path::PathBuf; +use std::sync::Arc; use once_cell::sync::OnceCell; use super::cache_db::CacheDB; use super::cache_db::CacheDBConfiguration; use super::check::TYPE_CHECK_CACHE_DB; +use super::deno_dir::DenoDirProvider; use super::incremental::INCREMENTAL_CACHE_DB; use super::node::NODE_ANALYSIS_CACHE_DB; use super::parsed_source::PARSED_SOURCE_CACHE_DB; -use super::DenoDir; pub struct Caches { - dir: DenoDir, + dir_provider: Arc<DenoDirProvider>, fmt_incremental_cache_db: OnceCell<CacheDB>, lint_incremental_cache_db: OnceCell<CacheDB>, dep_analysis_db: OnceCell<CacheDB>, @@ -22,9 +23,9 @@ pub struct Caches { } impl Caches { - pub fn new(dir: DenoDir) -> Self { + pub fn new(dir: Arc<DenoDirProvider>) -> Self { Self { - dir, + dir_provider: dir, fmt_incremental_cache_db: Default::default(), lint_incremental_cache_db: Default::default(), dep_analysis_db: Default::default(), @@ -36,10 +37,16 @@ impl Caches { fn make_db( cell: &OnceCell<CacheDB>, config: &'static CacheDBConfiguration, - path: PathBuf, + path: Option<PathBuf>, ) -> CacheDB { cell - .get_or_init(|| CacheDB::from_path(config, path, crate::version::deno())) + .get_or_init(|| { + if let Some(path) = path { + CacheDB::from_path(config, path, crate::version::deno()) + } else { + CacheDB::in_memory(config, crate::version::deno()) + } + }) .clone() } @@ -47,7 +54,11 @@ impl Caches { Self::make_db( &self.fmt_incremental_cache_db, &INCREMENTAL_CACHE_DB, - self.dir.fmt_incremental_cache_db_file_path(), + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.fmt_incremental_cache_db_file_path()), ) } @@ -55,7 +66,11 @@ impl Caches { Self::make_db( &self.lint_incremental_cache_db, &INCREMENTAL_CACHE_DB, - self.dir.lint_incremental_cache_db_file_path(), + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.lint_incremental_cache_db_file_path()), ) } @@ -63,7 +78,11 @@ impl Caches { Self::make_db( &self.dep_analysis_db, &PARSED_SOURCE_CACHE_DB, - self.dir.dep_analysis_db_file_path(), + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.dep_analysis_db_file_path()), ) } @@ -71,7 +90,11 @@ impl Caches { Self::make_db( &self.node_analysis_db, &NODE_ANALYSIS_CACHE_DB, - self.dir.node_analysis_db_file_path(), + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.node_analysis_db_file_path()), ) } @@ -79,7 +102,11 @@ impl Caches { Self::make_db( &self.type_checking_cache_db, &TYPE_CHECK_CACHE_DB, - self.dir.type_checking_cache_db_file_path(), + self + .dir_provider + .get_or_create() + .ok() + .map(|dir| dir.type_checking_cache_db_file_path()), ) } } diff --git a/cli/cache/deno_dir.rs b/cli/cache/deno_dir.rs index 2cee0e7a7..07bd4a61d 100644 --- a/cli/cache/deno_dir.rs +++ b/cli/cache/deno_dir.rs @@ -1,10 +1,36 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use once_cell::sync::OnceCell; + use super::DiskCache; use std::env; use std::path::PathBuf; +/// Lazily creates the deno dir which might be useful in scenarios +/// where functionality wants to continue if the DENO_DIR can't be created. +pub struct DenoDirProvider { + maybe_custom_root: Option<PathBuf>, + deno_dir: OnceCell<std::io::Result<DenoDir>>, +} + +impl DenoDirProvider { + pub fn new(maybe_custom_root: Option<PathBuf>) -> Self { + Self { + maybe_custom_root, + deno_dir: Default::default(), + } + } + + pub fn get_or_create(&self) -> Result<&DenoDir, std::io::Error> { + self + .deno_dir + .get_or_init(|| DenoDir::new(self.maybe_custom_root.clone())) + .as_ref() + .map_err(|err| std::io::Error::new(err.kind(), err.to_string())) + } +} + /// `DenoDir` serves as coordinator for multiple `DiskCache`s containing them /// in single directory that can be controlled with `$DENO_DIR` env variable. #[derive(Clone)] @@ -18,6 +44,8 @@ pub struct DenoDir { impl DenoDir { pub fn new(maybe_custom_root: Option<PathBuf>) -> std::io::Result<Self> { + let maybe_custom_root = + maybe_custom_root.or_else(|| env::var("DENO_DIR").map(String::into).ok()); let root: PathBuf = if let Some(root) = maybe_custom_root { root } else if let Some(cache_dir) = dirs::cache_dir() { diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index 40d74ff66..772ba10a5 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -30,6 +30,7 @@ pub use caches::Caches; pub use check::TypeCheckCache; pub use common::FastInsecureHasher; pub use deno_dir::DenoDir; +pub use deno_dir::DenoDirProvider; pub use disk_cache::DiskCache; pub use emit::EmitCache; pub use http_cache::CachedUrlMetadata; diff --git a/cli/factory.rs b/cli/factory.rs index 3b171414f..8e20e5514 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -10,6 +10,7 @@ use crate::args::StorageKeyResolver; use crate::args::TsConfigType; use crate::cache::Caches; use crate::cache::DenoDir; +use crate::cache::DenoDirProvider; use crate::cache::EmitCache; use crate::cache::HttpCache; use crate::cache::NodeAnalysisCache; @@ -130,7 +131,7 @@ impl<T> Deferred<T> { #[derive(Default)] struct CliFactoryServices { - dir: Deferred<DenoDir>, + deno_dir_provider: Deferred<Arc<DenoDirProvider>>, caches: Deferred<Arc<Caches>>, file_fetcher: Deferred<Arc<FileFetcher>>, http_client: Deferred<Arc<HttpClient>>, @@ -182,16 +183,21 @@ impl CliFactory { &self.options } + pub fn deno_dir_provider(&self) -> &Arc<DenoDirProvider> { + self.services.deno_dir_provider.get_or_init(|| { + Arc::new(DenoDirProvider::new( + self.options.maybe_custom_root().clone(), + )) + }) + } + pub fn deno_dir(&self) -> Result<&DenoDir, AnyError> { - self - .services - .dir - .get_or_try_init(|| self.options.resolve_deno_dir()) + Ok(self.deno_dir_provider().get_or_create()?) } pub fn caches(&self) -> Result<&Arc<Caches>, AnyError> { self.services.caches.get_or_try_init(|| { - let caches = Arc::new(Caches::new(self.deno_dir()?.clone())); + let caches = Arc::new(Caches::new(self.deno_dir_provider().clone())); // Warm up the caches we know we'll likely need based on the CLI mode match self.options.sub_command() { DenoSubcommand::Run(_) => { diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 594c4adbc..9a2b067c6 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -582,9 +582,7 @@ fn create_npm_resolver_and_resolution( impl Inner { fn new(client: Client) -> Self { - let maybe_custom_root = env::var("DENO_DIR").map(String::into).ok(); - let dir = - DenoDir::new(maybe_custom_root).expect("could not access DENO_DIR"); + let dir = DenoDir::new(None).expect("could not access DENO_DIR"); let module_registries_location = dir.registries_folder_path(); let http_client = Arc::new(HttpClient::new(None, None)); let module_registries = @@ -904,7 +902,7 @@ impl Inner { &mut self, new_cache_path: Option<PathBuf>, ) -> Result<(), AnyError> { - let dir = self.deno_dir_from_maybe_cache_path(new_cache_path.clone())?; + let dir = DenoDir::new(new_cache_path.clone())?; let workspace_settings = self.config.workspace_settings(); let maybe_root_path = self .config @@ -938,19 +936,8 @@ impl Inner { Ok(()) } - fn deno_dir_from_maybe_cache_path( - &self, - cache_path: Option<PathBuf>, - ) -> std::io::Result<DenoDir> { - let maybe_custom_root = - cache_path.or_else(|| env::var("DENO_DIR").map(String::into).ok()); - DenoDir::new(maybe_custom_root) - } - async fn recreate_npm_services_if_necessary(&mut self) { - let deno_dir = match self - .deno_dir_from_maybe_cache_path(self.maybe_cache_path.clone()) - { + let deno_dir = match DenoDir::new(self.maybe_cache_path.clone()) { Ok(deno_dir) => deno_dir, Err(err) => { lsp_warn!("Error getting deno dir: {}", err); diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index 82c41b882..14d4ad533 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -7,7 +7,7 @@ use crate::args::CacheSetting; use crate::args::PackageJsonDepsProvider; use crate::args::StorageKeyResolver; use crate::cache::Caches; -use crate::cache::DenoDir; +use crate::cache::DenoDirProvider; use crate::cache::NodeAnalysisCache; use crate::file_fetcher::get_source_from_data_url; use crate::http_util::HttpClient; @@ -282,7 +282,7 @@ pub async fn run( let current_exe_path = std::env::current_exe().unwrap(); let current_exe_name = current_exe_path.file_name().unwrap().to_string_lossy(); - let dir = DenoDir::new(None)?; + let deno_dir_provider = Arc::new(DenoDirProvider::new(None)); let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider { ca_stores: metadata.ca_stores, ca_data: metadata.ca_data.map(CaData::Bytes), @@ -362,7 +362,7 @@ pub async fn run( let node_resolver = Arc::new(NodeResolver::new(fs.clone(), npm_resolver.clone())); let cjs_resolutions = Arc::new(CjsResolutionStore::default()); - let cache_db = Caches::new(dir.clone()); + let cache_db = Caches::new(deno_dir_provider.clone()); let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db()); let cjs_esm_code_analyzer = CliCjsEsmCodeAnalyzer::new(node_analysis_cache); let node_code_translator = Arc::new(NodeCodeTranslator::new( diff --git a/cli/tests/integration/compile_tests.rs b/cli/tests/integration/compile_tests.rs index 9c1c66d35..05128bc07 100644 --- a/cli/tests/integration/compile_tests.rs +++ b/cli/tests/integration/compile_tests.rs @@ -1,6 +1,7 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use std::fs::File; +use std::path::Path; use std::process::Command; use test_util as util; use test_util::TempDir; @@ -8,8 +9,9 @@ use util::assert_contains; use util::TestContextBuilder; #[test] -fn compile() { - let dir = TempDir::new(); +fn compile_basic() { + let context = TestContextBuilder::new().build(); + let dir = context.temp_dir(); let exe = if cfg!(windows) { dir.path().join("welcome.exe") } else { @@ -17,26 +19,45 @@ fn compile() { }; // try this twice to ensure it works with the cache for _ in 0..2 { - let output = util::deno_cmd_with_deno_dir(&dir) - .current_dir(util::root_path()) - .arg("compile") - .arg("--output") - .arg(&exe) - .arg("./test_util/std/examples/welcome.ts") - .stdout(std::process::Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - let output = Command::new(&exe) - .stdout(std::process::Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - assert_eq!(output.stdout, "Welcome to Deno!\n".as_bytes()); + let output = context + .new_command() + .args_vec([ + "compile", + "--output", + &exe.to_string_lossy(), + "../../../test_util/std/examples/welcome.ts", + ]) + .run(); + output.assert_exit_code(0); + output.skip_output_check(); + let output = context + .new_command() + .command_name(exe.to_string_lossy()) + .run(); + output.assert_matches_text("Welcome to Deno!\n"); + } + + // now ensure this works when the deno_dir is readonly + let readonly_dir = dir.path().join("readonly"); + make_dir_readonly(&readonly_dir); + let readonly_sub_dir = readonly_dir.join("sub"); + + let output = context + .new_command() + // it should fail creating this, but still work + .env("DENO_DIR", readonly_sub_dir.to_string_lossy()) + .command_name(exe.to_string_lossy()) + .run(); + output.assert_matches_text("Welcome to Deno!\n"); +} + +fn make_dir_readonly(dir: &Path) { + std::fs::create_dir_all(dir).unwrap(); + eprintln!("DIR: {}", dir.display()); + if cfg!(windows) { + Command::new("attrib").arg("+r").arg(dir).output().unwrap(); + } else if cfg!(unix) { + Command::new("chmod").arg("555").arg(dir).output().unwrap(); } } diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index dfd9931b8..34acb8a4e 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -107,9 +107,12 @@ pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result<i32, AnyError> { )?); let npm_resolver = factory.npm_resolver().await?.clone(); let resolver = factory.resolver().await?.clone(); - let dir = factory.deno_dir()?; let file_fetcher = factory.file_fetcher()?; let worker_factory = factory.create_cli_main_worker_factory().await?; + let history_file_path = factory + .deno_dir() + .ok() + .and_then(|dir| dir.repl_history_file_path()); let mut worker = worker_factory .create_main_worker(main_module, permissions) @@ -126,7 +129,6 @@ pub async fn run(flags: Flags, repl_flags: ReplFlags) -> Result<i32, AnyError> { sync_sender: rustyline_channel.0, }; - let history_file_path = dir.repl_history_file_path(); let editor = ReplEditor::new(helper, history_file_path)?; if let Some(eval_files) = repl_flags.eval_files { |