From 5f0bb3c6f4328003012e98ba70ce18e4e2e842de Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Thu, 24 Oct 2024 20:03:56 +0200 Subject: [PATCH] fix: `.npmrc` settings not being passed to install/add command (#26473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We weren't passing the resolved npmrc settings to the install commands. This lead us to always fall back to the default registry url instead of using the one from npmrc. Fixes https://github.com/denoland/deno/issues/26139 Fixes https://github.com/denoland/deno/issues/26033 Fixes https://github.com/denoland/deno/issues/25924 Fixes https://github.com/denoland/deno/issues/25822 Fixes https://github.com/denoland/deno/issues/26152 --------- Co-authored-by: Bartek IwaƄczuk --- cli/args/mod.rs | 1 + cli/cache/mod.rs | 1 + cli/file_fetcher.rs | 30 +++++++- cli/http_util.rs | 21 ++++++ cli/lsp/npm.rs | 6 +- cli/lsp/registries.rs | 1 + cli/npm/managed/cache/mod.rs | 2 +- cli/npm/managed/cache/registry_info.rs | 68 +++++++++---------- cli/npm/managed/mod.rs | 2 +- cli/npm/mod.rs | 25 +++++-- cli/tools/registry/pm.rs | 6 +- .../@denotest3/basic/1.0.0/main.d.mts | 3 + .../@denotest3/basic/1.0.0/main.mjs | 11 +++ .../@denotest3/basic/1.0.0/other.mjs | 3 + .../@denotest3/basic/1.0.0/package.json | 7 ++ tests/specs/npm/npmrc_install_arg/.npmrc | 2 + .../npm/npmrc_install_arg/__test__.jsonc | 23 +++++++ tests/specs/npm/npmrc_install_arg/add.out | 4 ++ tests/specs/npm/npmrc_install_arg/install.out | 4 ++ tests/specs/npm/npmrc_install_arg/main.js | 5 ++ tests/specs/npm/npmrc_install_arg/main.out | 2 + .../specs/npm/npmrc_install_arg/package.json | 5 ++ tests/specs/npm/npmrc_no_auth/.npmrc | 1 + tests/specs/npm/npmrc_no_auth/__test__.jsonc | 23 +++++++ tests/specs/npm/npmrc_no_auth/add.out | 4 ++ tests/specs/npm/npmrc_no_auth/install.out | 4 ++ tests/specs/npm/npmrc_no_auth/main.js | 5 ++ tests/specs/npm/npmrc_no_auth/main.out | 2 + tests/specs/npm/npmrc_no_auth/package.json | 6 ++ tests/specs/npm/npmrc_no_auth_install/.npmrc | 1 + .../npm/npmrc_no_auth_install/__test__.jsonc | 14 ++++ .../npm/npmrc_no_auth_install/install.out | 3 + tests/specs/npm/npmrc_no_auth_install/main.js | 5 ++ .../specs/npm/npmrc_no_auth_install/main.out | 2 + .../npm/npmrc_no_auth_install/package.json | 7 ++ tests/util/server/src/lib.rs | 3 +- tests/util/server/src/npm.rs | 32 +++++++++ tests/util/server/src/servers/mod.rs | 4 ++ tests/util/server/src/servers/npm_registry.rs | 15 ++++ 39 files changed, 319 insertions(+), 44 deletions(-) create mode 100644 tests/registry/npm-private3/@denotest3/basic/1.0.0/main.d.mts create mode 100644 tests/registry/npm-private3/@denotest3/basic/1.0.0/main.mjs create mode 100644 tests/registry/npm-private3/@denotest3/basic/1.0.0/other.mjs create mode 100644 tests/registry/npm-private3/@denotest3/basic/1.0.0/package.json create mode 100644 tests/specs/npm/npmrc_install_arg/.npmrc create mode 100644 tests/specs/npm/npmrc_install_arg/__test__.jsonc create mode 100644 tests/specs/npm/npmrc_install_arg/add.out create mode 100644 tests/specs/npm/npmrc_install_arg/install.out create mode 100644 tests/specs/npm/npmrc_install_arg/main.js create mode 100644 tests/specs/npm/npmrc_install_arg/main.out create mode 100644 tests/specs/npm/npmrc_install_arg/package.json create mode 100644 tests/specs/npm/npmrc_no_auth/.npmrc create mode 100644 tests/specs/npm/npmrc_no_auth/__test__.jsonc create mode 100644 tests/specs/npm/npmrc_no_auth/add.out create mode 100644 tests/specs/npm/npmrc_no_auth/install.out create mode 100644 tests/specs/npm/npmrc_no_auth/main.js create mode 100644 tests/specs/npm/npmrc_no_auth/main.out create mode 100644 tests/specs/npm/npmrc_no_auth/package.json create mode 100644 tests/specs/npm/npmrc_no_auth_install/.npmrc create mode 100644 tests/specs/npm/npmrc_no_auth_install/__test__.jsonc create mode 100644 tests/specs/npm/npmrc_no_auth_install/install.out create mode 100644 tests/specs/npm/npmrc_no_auth_install/main.js create mode 100644 tests/specs/npm/npmrc_no_auth_install/main.out create mode 100644 tests/specs/npm/npmrc_no_auth_install/package.json diff --git a/cli/args/mod.rs b/cli/args/mod.rs index d8f5531015..927f43e85a 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -578,6 +578,7 @@ fn discover_npmrc( let resolved = npmrc .as_resolved(npm_registry_url()) .context("Failed to resolve .npmrc options")?; + log::debug!(".npmrc found at: '{}'", path.display()); Ok(Arc::new(resolved)) } diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index ded163b4e4..bf8f1b1f0b 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -378,6 +378,7 @@ impl Loader for FetchCacher { } else { FetchPermissionsOptionRef::DynamicContainer(&permissions) }, + maybe_auth: None, maybe_accept: None, maybe_cache_setting: maybe_cache_setting.as_ref(), }, diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index e92aca5420..95d778f0bb 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -24,6 +24,7 @@ use deno_graph::source::LoaderChecksum; use deno_path_util::url_to_file_path; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::BlobStore; +use http::header; use log::debug; use std::borrow::Cow; use std::collections::HashMap; @@ -181,6 +182,7 @@ pub enum FetchPermissionsOptionRef<'a> { pub struct FetchOptions<'a> { pub specifier: &'a ModuleSpecifier, pub permissions: FetchPermissionsOptionRef<'a>, + pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, pub maybe_accept: Option<&'a str>, pub maybe_cache_setting: Option<&'a CacheSetting>, } @@ -350,6 +352,7 @@ impl FileFetcher { maybe_accept: Option<&str>, cache_setting: &CacheSetting, maybe_checksum: Option<&LoaderChecksum>, + maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, ) -> Result { debug!( "FileFetcher::fetch_remote_no_follow - specifier: {}", @@ -442,6 +445,7 @@ impl FileFetcher { .as_ref() .map(|(_, etag)| etag.clone()), maybe_auth_token: maybe_auth_token.clone(), + maybe_auth: maybe_auth.clone(), maybe_progress_guard: maybe_progress_guard.as_ref(), }) .await? @@ -538,7 +542,18 @@ impl FileFetcher { specifier: &ModuleSpecifier, ) -> Result { self - .fetch_inner(specifier, FetchPermissionsOptionRef::AllowAll) + .fetch_inner(specifier, None, FetchPermissionsOptionRef::AllowAll) + .await + } + + #[inline(always)] + pub async fn fetch_bypass_permissions_with_maybe_auth( + &self, + specifier: &ModuleSpecifier, + maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, + ) -> Result { + self + .fetch_inner(specifier, maybe_auth, FetchPermissionsOptionRef::AllowAll) .await } @@ -552,6 +567,7 @@ impl FileFetcher { self .fetch_inner( specifier, + None, FetchPermissionsOptionRef::StaticContainer(permissions), ) .await @@ -560,12 +576,14 @@ impl FileFetcher { async fn fetch_inner( &self, specifier: &ModuleSpecifier, + maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, permissions: FetchPermissionsOptionRef<'_>, ) -> Result { self .fetch_with_options(FetchOptions { specifier, permissions, + maybe_auth, maybe_accept: None, maybe_cache_setting: None, }) @@ -585,12 +603,14 @@ impl FileFetcher { max_redirect: usize, ) -> Result { let mut specifier = Cow::Borrowed(options.specifier); + let mut maybe_auth = options.maybe_auth.clone(); for _ in 0..=max_redirect { match self .fetch_no_follow_with_options(FetchNoFollowOptions { fetch_options: FetchOptions { specifier: &specifier, permissions: options.permissions, + maybe_auth: maybe_auth.clone(), maybe_accept: options.maybe_accept, maybe_cache_setting: options.maybe_cache_setting, }, @@ -602,6 +622,10 @@ impl FileFetcher { return Ok(file); } FileOrRedirect::Redirect(redirect_specifier) => { + // If we were redirected to another origin, don't send the auth header anymore. + if redirect_specifier.origin() != specifier.origin() { + maybe_auth = None; + } specifier = Cow::Owned(redirect_specifier); } } @@ -666,6 +690,7 @@ impl FileFetcher { options.maybe_accept, options.maybe_cache_setting.unwrap_or(&self.cache_setting), maybe_checksum, + options.maybe_auth, ) .await } @@ -756,6 +781,7 @@ mod tests { FetchOptions { specifier, permissions: FetchPermissionsOptionRef::AllowAll, + maybe_auth: None, maybe_accept: None, maybe_cache_setting: Some(&file_fetcher.cache_setting), }, @@ -1255,6 +1281,7 @@ mod tests { FetchOptions { specifier: &specifier, permissions: FetchPermissionsOptionRef::AllowAll, + maybe_auth: None, maybe_accept: None, maybe_cache_setting: Some(&file_fetcher.cache_setting), }, @@ -1268,6 +1295,7 @@ mod tests { FetchOptions { specifier: &specifier, permissions: FetchPermissionsOptionRef::AllowAll, + maybe_auth: None, maybe_accept: None, maybe_cache_setting: Some(&file_fetcher.cache_setting), }, diff --git a/cli/http_util.rs b/cli/http_util.rs index 9c9ae9e413..4b17936d68 100644 --- a/cli/http_util.rs +++ b/cli/http_util.rs @@ -19,6 +19,7 @@ use deno_runtime::deno_fetch; use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::CreateHttpClientOptions; use deno_runtime::deno_tls::RootCertStoreProvider; +use http::header; use http::header::HeaderName; use http::header::HeaderValue; use http::header::ACCEPT; @@ -204,6 +205,7 @@ pub struct FetchOnceArgs<'a> { pub maybe_accept: Option, pub maybe_etag: Option, pub maybe_auth_token: Option, + pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>, pub maybe_progress_guard: Option<&'a UpdateGuard>, } @@ -382,6 +384,8 @@ impl HttpClient { request .headers_mut() .insert(AUTHORIZATION, authorization_val); + } else if let Some((header, value)) = args.maybe_auth { + request.headers_mut().insert(header, value); } if let Some(accept) = args.maybe_accept { let accepts_val = HeaderValue::from_str(&accept)?; @@ -792,6 +796,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -818,6 +823,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -845,6 +851,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -866,6 +873,7 @@ mod test { maybe_etag: Some("33a64df551425fcc55e".to_string()), maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; assert_eq!(res.unwrap(), FetchOnceResult::NotModified); @@ -885,6 +893,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -914,6 +923,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, _)) = result { @@ -939,6 +949,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Redirect(url, _)) = result { @@ -974,6 +985,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -1021,6 +1033,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; @@ -1083,6 +1096,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; @@ -1136,6 +1150,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -1177,6 +1192,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -1199,6 +1215,7 @@ mod test { maybe_etag: Some("33a64df551425fcc55e".to_string()), maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; assert_eq!(res.unwrap(), FetchOnceResult::NotModified); @@ -1233,6 +1250,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; if let Ok(FetchOnceResult::Code(body, headers)) = result { @@ -1262,6 +1280,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; assert!(result.is_err()); @@ -1283,6 +1302,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; @@ -1306,6 +1326,7 @@ mod test { maybe_etag: None, maybe_auth_token: None, maybe_progress_guard: None, + maybe_auth: None, }) .await; diff --git a/cli/lsp/npm.rs b/cli/lsp/npm.rs index 8bdeb7e7d8..2decfc3429 100644 --- a/cli/lsp/npm.rs +++ b/cli/lsp/npm.rs @@ -4,6 +4,7 @@ use dashmap::DashMap; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::serde_json; +use deno_npm::npm_rc::NpmRc; use deno_semver::package::PackageNv; use deno_semver::Version; use serde::Deserialize; @@ -25,7 +26,10 @@ pub struct CliNpmSearchApi { impl CliNpmSearchApi { pub fn new(file_fetcher: Arc) -> Self { - let resolver = NpmFetchResolver::new(file_fetcher.clone()); + let resolver = NpmFetchResolver::new( + file_fetcher.clone(), + Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()), + ); Self { file_fetcher, resolver, diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index 5f7ce00823..ade353e683 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -482,6 +482,7 @@ impl ModuleRegistry { .fetch_with_options(FetchOptions { specifier: &specifier, permissions: FetchPermissionsOptionRef::AllowAll, + maybe_auth: None, maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"), maybe_cache_setting: None, }) diff --git a/cli/npm/managed/cache/mod.rs b/cli/npm/managed/cache/mod.rs index fa0e8c8a59..aaec486681 100644 --- a/cli/npm/managed/cache/mod.rs +++ b/cli/npm/managed/cache/mod.rs @@ -26,7 +26,7 @@ use crate::cache::CACHE_PERM; use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::hard_link_dir_recursive; -mod registry_info; +pub mod registry_info; mod tarball; mod tarball_extract; diff --git a/cli/npm/managed/cache/registry_info.rs b/cli/npm/managed/cache/registry_info.rs index 6c4a7503b5..6d39d3c13f 100644 --- a/cli/npm/managed/cache/registry_info.rs +++ b/cli/npm/managed/cache/registry_info.rs @@ -84,7 +84,7 @@ impl RegistryInfoDownloader { self.load_package_info_inner(name).await.with_context(|| { format!( "Error getting response at {} for package \"{}\"", - self.get_package_url(name), + get_package_url(&self.npmrc, name), name ) }) @@ -190,7 +190,7 @@ impl RegistryInfoDownloader { fn create_load_future(self: &Arc, name: &str) -> LoadFuture { let downloader = self.clone(); - let package_url = self.get_package_url(name); + let package_url = get_package_url(&self.npmrc, name); let registry_config = self.npmrc.get_registry_config(name); let maybe_auth_header = match maybe_auth_header_for_npm_registry(registry_config) { @@ -239,36 +239,36 @@ impl RegistryInfoDownloader { .map(|r| r.map_err(Arc::new)) .boxed_local() } - - fn get_package_url(&self, name: &str) -> Url { - let registry_url = self.npmrc.get_registry_url(name); - // The '/' character in scoped package names "@scope/name" must be - // encoded for older third party registries. Newer registries and - // npm itself support both ways - // - encoded: https://registry.npmjs.org/@rollup%2fplugin-json - // - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json - // To support as many third party registries as possible we'll - // always encode the '/' character. - - // list of all characters used in npm packages: - // !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~ - const ASCII_SET: percent_encoding::AsciiSet = - percent_encoding::NON_ALPHANUMERIC - .remove(b'!') - .remove(b'\'') - .remove(b'(') - .remove(b')') - .remove(b'*') - .remove(b'-') - .remove(b'.') - .remove(b'@') - .remove(b'_') - .remove(b'~'); - let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET); - registry_url - // Ensure that scoped package name percent encoding is lower cased - // to match npm. - .join(&name.to_string().replace("%2F", "%2f")) - .unwrap() - } +} + +pub fn get_package_url(npmrc: &ResolvedNpmRc, name: &str) -> Url { + let registry_url = npmrc.get_registry_url(name); + // The '/' character in scoped package names "@scope/name" must be + // encoded for older third party registries. Newer registries and + // npm itself support both ways + // - encoded: https://registry.npmjs.org/@rollup%2fplugin-json + // - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json + // To support as many third party registries as possible we'll + // always encode the '/' character. + + // list of all characters used in npm packages: + // !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~ + const ASCII_SET: percent_encoding::AsciiSet = + percent_encoding::NON_ALPHANUMERIC + .remove(b'!') + .remove(b'\'') + .remove(b'(') + .remove(b')') + .remove(b'*') + .remove(b'-') + .remove(b'.') + .remove(b'@') + .remove(b'_') + .remove(b'~'); + let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET); + registry_url + // Ensure that scoped package name percent encoding is lower cased + // to match npm. + .join(&name.to_string().replace("%2F", "%2f")) + .unwrap() } diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index ec50a9c65a..d0880557fe 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -55,7 +55,7 @@ use super::CliNpmResolver; use super::InnerCliNpmResolverRef; use super::ResolvePkgFolderFromDenoReqError; -mod cache; +pub mod cache; mod registry; mod resolution; mod resolvers; diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index 53baaf77b4..f48f7a7405 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -8,10 +8,12 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use common::maybe_auth_header_for_npm_registry; use dashmap::DashMap; use deno_ast::ModuleSpecifier; use deno_core::error::AnyError; use deno_core::serde_json; +use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError; @@ -19,10 +21,10 @@ use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::ops::process::NpmProcessStateProvider; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use managed::cache::registry_info::get_package_url; use node_resolver::NpmResolver; use thiserror::Error; -use crate::args::npm_registry_url; use crate::file_fetcher::FileFetcher; pub use self::byonm::CliByonmNpmResolver; @@ -115,14 +117,19 @@ pub struct NpmFetchResolver { nv_by_req: DashMap>, info_by_name: DashMap>>, file_fetcher: Arc, + npmrc: Arc, } impl NpmFetchResolver { - pub fn new(file_fetcher: Arc) -> Self { + pub fn new( + file_fetcher: Arc, + npmrc: Arc, + ) -> Self { Self { nv_by_req: Default::default(), info_by_name: Default::default(), file_fetcher, + npmrc, } } @@ -157,11 +164,21 @@ impl NpmFetchResolver { return info.value().clone(); } let fetch_package_info = || async { - let info_url = npm_registry_url().join(name).ok()?; + let info_url = get_package_url(&self.npmrc, name); let file_fetcher = self.file_fetcher.clone(); + let registry_config = self.npmrc.get_registry_config(name); + // TODO(bartlomieju): this should error out, not use `.ok()`. + let maybe_auth_header = + maybe_auth_header_for_npm_registry(registry_config).ok()?; // spawn due to the lsp's `Send` requirement let file = deno_core::unsync::spawn(async move { - file_fetcher.fetch_bypass_permissions(&info_url).await.ok() + file_fetcher + .fetch_bypass_permissions_with_maybe_auth( + &info_url, + maybe_auth_header, + ) + .await + .ok() }) .await .ok()??; diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 2060b9a13f..d1be901d67 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -367,10 +367,14 @@ pub async fn add( Default::default(), None, ); + + let npmrc = cli_factory.cli_options().unwrap().npmrc(); + deps_file_fetcher.set_download_log_level(log::Level::Trace); let deps_file_fetcher = Arc::new(deps_file_fetcher); let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone())); - let npm_resolver = Arc::new(NpmFetchResolver::new(deps_file_fetcher)); + let npm_resolver = + Arc::new(NpmFetchResolver::new(deps_file_fetcher, npmrc.clone())); let mut selected_packages = Vec::with_capacity(add_flags.packages.len()); let mut package_reqs = Vec::with_capacity(add_flags.packages.len()); diff --git a/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.d.mts b/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.d.mts new file mode 100644 index 0000000000..29da1e6d7b --- /dev/null +++ b/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.d.mts @@ -0,0 +1,3 @@ +export declare function setValue(val: number): void; +export declare function getValue(): number; +export declare const url: string; diff --git a/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.mjs b/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.mjs new file mode 100644 index 0000000000..0a44f75859 --- /dev/null +++ b/tests/registry/npm-private3/@denotest3/basic/1.0.0/main.mjs @@ -0,0 +1,11 @@ +let value = 0; + +export function setValue(newValue) { + value = newValue; +} + +export function getValue() { + return value; +} + +export const url = import.meta.url; diff --git a/tests/registry/npm-private3/@denotest3/basic/1.0.0/other.mjs b/tests/registry/npm-private3/@denotest3/basic/1.0.0/other.mjs new file mode 100644 index 0000000000..00ed99da45 --- /dev/null +++ b/tests/registry/npm-private3/@denotest3/basic/1.0.0/other.mjs @@ -0,0 +1,3 @@ +export function hello() { + return "hello, world!"; +} \ No newline at end of file diff --git a/tests/registry/npm-private3/@denotest3/basic/1.0.0/package.json b/tests/registry/npm-private3/@denotest3/basic/1.0.0/package.json new file mode 100644 index 0000000000..ce6ea33830 --- /dev/null +++ b/tests/registry/npm-private3/@denotest3/basic/1.0.0/package.json @@ -0,0 +1,7 @@ +{ + "name": "@denotest3/basic", + "version": "1.0.0", + "type": "module", + "main": "main.mjs", + "types": "main.d.mts" +} diff --git a/tests/specs/npm/npmrc_install_arg/.npmrc b/tests/specs/npm/npmrc_install_arg/.npmrc new file mode 100644 index 0000000000..de3704b924 --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/.npmrc @@ -0,0 +1,2 @@ +@denotest:registry=http://localhost:4261/ +//localhost:4261/:_authToken=private-reg-token diff --git a/tests/specs/npm/npmrc_install_arg/__test__.jsonc b/tests/specs/npm/npmrc_install_arg/__test__.jsonc new file mode 100644 index 0000000000..f34cfe116a --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/__test__.jsonc @@ -0,0 +1,23 @@ +{ + "tempDir": true, + "tests": { + "deno_install_add": { + "steps": [{ + "args": "install npm:@denotest/basic", + "output": "install.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] + }, + "deno_add": { + "steps": [{ + "args": "add npm:@denotest/basic", + "output": "add.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] + } + } +} diff --git a/tests/specs/npm/npmrc_install_arg/add.out b/tests/specs/npm/npmrc_install_arg/add.out new file mode 100644 index 0000000000..11948e54d5 --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/add.out @@ -0,0 +1,4 @@ +Add npm:@denotest/basic@1.0.0 +Download http://localhost:4261/@denotest%2fbasic +Download http://localhost:4261/@denotest/basic/1.0.0.tgz +Initialize @denotest/basic@1.0.0 diff --git a/tests/specs/npm/npmrc_install_arg/install.out b/tests/specs/npm/npmrc_install_arg/install.out new file mode 100644 index 0000000000..11948e54d5 --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/install.out @@ -0,0 +1,4 @@ +Add npm:@denotest/basic@1.0.0 +Download http://localhost:4261/@denotest%2fbasic +Download http://localhost:4261/@denotest/basic/1.0.0.tgz +Initialize @denotest/basic@1.0.0 diff --git a/tests/specs/npm/npmrc_install_arg/main.js b/tests/specs/npm/npmrc_install_arg/main.js new file mode 100644 index 0000000000..e8ccf7611c --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/main.js @@ -0,0 +1,5 @@ +import { getValue, setValue } from "@denotest/basic"; + +console.log(getValue()); +setValue(42); +console.log(getValue()); diff --git a/tests/specs/npm/npmrc_install_arg/main.out b/tests/specs/npm/npmrc_install_arg/main.out new file mode 100644 index 0000000000..dcd912c89f --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/main.out @@ -0,0 +1,2 @@ +0 +42 diff --git a/tests/specs/npm/npmrc_install_arg/package.json b/tests/specs/npm/npmrc_install_arg/package.json new file mode 100644 index 0000000000..c1318b361a --- /dev/null +++ b/tests/specs/npm/npmrc_install_arg/package.json @@ -0,0 +1,5 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": {} +} diff --git a/tests/specs/npm/npmrc_no_auth/.npmrc b/tests/specs/npm/npmrc_no_auth/.npmrc new file mode 100644 index 0000000000..860a7c3d9a --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/.npmrc @@ -0,0 +1 @@ +@denotest3:registry=http://localhost:4263/ diff --git a/tests/specs/npm/npmrc_no_auth/__test__.jsonc b/tests/specs/npm/npmrc_no_auth/__test__.jsonc new file mode 100644 index 0000000000..efd04f0e46 --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/__test__.jsonc @@ -0,0 +1,23 @@ +{ + "tempDir": true, + "tests": { + "deno_install_arg": { + "steps": [{ + "args": "install npm:@denotest3/basic", + "output": "install.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] + }, + "deno_add_arg": { + "steps": [{ + "args": "add npm:@denotest3/basic", + "output": "add.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] + } + } +} diff --git a/tests/specs/npm/npmrc_no_auth/add.out b/tests/specs/npm/npmrc_no_auth/add.out new file mode 100644 index 0000000000..31b34da13f --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/add.out @@ -0,0 +1,4 @@ +Add npm:@denotest3/basic@1.0.0 +Download http://localhost:4263/@denotest3%2fbasic +Download http://localhost:4263/@denotest3/basic/1.0.0.tgz +Initialize @denotest3/basic@1.0.0 diff --git a/tests/specs/npm/npmrc_no_auth/install.out b/tests/specs/npm/npmrc_no_auth/install.out new file mode 100644 index 0000000000..31b34da13f --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/install.out @@ -0,0 +1,4 @@ +Add npm:@denotest3/basic@1.0.0 +Download http://localhost:4263/@denotest3%2fbasic +Download http://localhost:4263/@denotest3/basic/1.0.0.tgz +Initialize @denotest3/basic@1.0.0 diff --git a/tests/specs/npm/npmrc_no_auth/main.js b/tests/specs/npm/npmrc_no_auth/main.js new file mode 100644 index 0000000000..22713bb0da --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/main.js @@ -0,0 +1,5 @@ +import { getValue, setValue } from "@denotest3/basic"; + +console.log(getValue()); +setValue(42); +console.log(getValue()); diff --git a/tests/specs/npm/npmrc_no_auth/main.out b/tests/specs/npm/npmrc_no_auth/main.out new file mode 100644 index 0000000000..dcd912c89f --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/main.out @@ -0,0 +1,2 @@ +0 +42 diff --git a/tests/specs/npm/npmrc_no_auth/package.json b/tests/specs/npm/npmrc_no_auth/package.json new file mode 100644 index 0000000000..dddaaac464 --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth/package.json @@ -0,0 +1,6 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + } +} diff --git a/tests/specs/npm/npmrc_no_auth_install/.npmrc b/tests/specs/npm/npmrc_no_auth_install/.npmrc new file mode 100644 index 0000000000..860a7c3d9a --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/.npmrc @@ -0,0 +1 @@ +@denotest3:registry=http://localhost:4263/ diff --git a/tests/specs/npm/npmrc_no_auth_install/__test__.jsonc b/tests/specs/npm/npmrc_no_auth_install/__test__.jsonc new file mode 100644 index 0000000000..56a598e3b9 --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/__test__.jsonc @@ -0,0 +1,14 @@ +{ + "tempDir": true, + "tests": { + "deno_install_arg": { + "steps": [{ + "args": "install", + "output": "install.out" + }, { + "args": "run -A main.js", + "output": "main.out" + }] + } + } +} diff --git a/tests/specs/npm/npmrc_no_auth_install/install.out b/tests/specs/npm/npmrc_no_auth_install/install.out new file mode 100644 index 0000000000..b82747af79 --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/install.out @@ -0,0 +1,3 @@ +Download http://localhost:4263/@denotest3%2fbasic +Download http://localhost:4263/@denotest3/basic/1.0.0.tgz +Initialize @denotest3/basic@1.0.0 diff --git a/tests/specs/npm/npmrc_no_auth_install/main.js b/tests/specs/npm/npmrc_no_auth_install/main.js new file mode 100644 index 0000000000..22713bb0da --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/main.js @@ -0,0 +1,5 @@ +import { getValue, setValue } from "@denotest3/basic"; + +console.log(getValue()); +setValue(42); +console.log(getValue()); diff --git a/tests/specs/npm/npmrc_no_auth_install/main.out b/tests/specs/npm/npmrc_no_auth_install/main.out new file mode 100644 index 0000000000..dcd912c89f --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/main.out @@ -0,0 +1,2 @@ +0 +42 diff --git a/tests/specs/npm/npmrc_no_auth_install/package.json b/tests/specs/npm/npmrc_no_auth_install/package.json new file mode 100644 index 0000000000..2d4fb15bae --- /dev/null +++ b/tests/specs/npm/npmrc_no_auth_install/package.json @@ -0,0 +1,7 @@ +{ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + "@denotest3/basic": "1.0.0" + } +} diff --git a/tests/util/server/src/lib.rs b/tests/util/server/src/lib.rs index 88e8287e08..e4a2cc02e3 100644 --- a/tests/util/server/src/lib.rs +++ b/tests/util/server/src/lib.rs @@ -307,7 +307,7 @@ async fn get_tcp_listener_stream( futures::stream::select_all(listeners) } -pub const TEST_SERVERS_COUNT: usize = 30; +pub const TEST_SERVERS_COUNT: usize = 32; #[derive(Default)] struct HttpServerCount { @@ -360,6 +360,7 @@ impl Default for HttpServerStarter { let mut ready_count = 0; for maybe_line in lines { if let Ok(line) = maybe_line { + eprintln!("LINE: {}", line); if line.starts_with("ready:") { ready_count += 1; } diff --git a/tests/util/server/src/npm.rs b/tests/util/server/src/npm.rs index f1c341738a..4b17b95f72 100644 --- a/tests/util/server/src/npm.rs +++ b/tests/util/server/src/npm.rs @@ -18,6 +18,7 @@ use crate::PathRef; pub const DENOTEST_SCOPE_NAME: &str = "@denotest"; pub const DENOTEST2_SCOPE_NAME: &str = "@denotest2"; +pub const DENOTEST3_SCOPE_NAME: &str = "@denotest3"; pub static PUBLIC_TEST_NPM_REGISTRY: Lazy = Lazy::new(|| { TestNpmRegistry::new( @@ -54,6 +55,18 @@ pub static PRIVATE_TEST_NPM_REGISTRY_2: Lazy = ) }); +pub static PRIVATE_TEST_NPM_REGISTRY_3: Lazy = + Lazy::new(|| { + TestNpmRegistry::new( + NpmRegistryKind::Private, + &format!( + "http://localhost:{}", + crate::servers::PRIVATE_NPM_REGISTRY_3_PORT + ), + "npm-private3", + ) + }); + pub enum NpmRegistryKind { Public, Private, @@ -90,6 +103,7 @@ impl TestNpmRegistry { } pub fn root_dir(&self) -> PathRef { + eprintln!("root {}", self.local_path); tests_path().join("registry").join(&self.local_path) } @@ -106,6 +120,7 @@ impl TestNpmRegistry { } pub fn registry_file(&self, name: &str) -> Result>> { + eprintln!("registry file {}", name); self.get_package_property(name, |p| p.registry_file.as_bytes().to_vec()) } @@ -123,6 +138,7 @@ impl TestNpmRegistry { package_name: &str, func: impl FnOnce(&CustomNpmPackage) -> TResult, ) -> Result> { + eprintln!("get package property {}", package_name); // it's ok if multiple threads race here as they will do the same work twice if !self.cache.lock().contains_key(package_name) { match get_npm_package(&self.hostname, &self.local_path, package_name)? { @@ -139,6 +155,7 @@ impl TestNpmRegistry { &self, uri_path: &'s str, ) -> Option<(&'s str, &'s str)> { + eprintln!("GEETT {}", uri_path); let prefix1 = format!("/{}/", DENOTEST_SCOPE_NAME); let prefix2 = format!("/{}%2f", DENOTEST_SCOPE_NAME); @@ -161,6 +178,17 @@ impl TestNpmRegistry { return Some((DENOTEST2_SCOPE_NAME, package_name_with_path)); } + let prefix1 = format!("/{}/", DENOTEST3_SCOPE_NAME); + let prefix2 = format!("/{}%2f", DENOTEST3_SCOPE_NAME); + + let maybe_package_name_with_path = uri_path + .strip_prefix(&prefix1) + .or_else(|| uri_path.strip_prefix(&prefix2)); + + if let Some(package_name_with_path) = maybe_package_name_with_path { + return Some((DENOTEST3_SCOPE_NAME, package_name_with_path)); + } + None } } @@ -170,6 +198,10 @@ fn get_npm_package( local_path: &str, package_name: &str, ) -> Result> { + eprintln!( + "get npm package {} {} {}", + registry_hostname, local_path, package_name + ); let registry_hostname = if package_name == "@denotest/tarballs-privateserver2" { "http://localhost:4262" diff --git a/tests/util/server/src/servers/mod.rs b/tests/util/server/src/servers/mod.rs index 3e18aafce4..3a9c440104 100644 --- a/tests/util/server/src/servers/mod.rs +++ b/tests/util/server/src/servers/mod.rs @@ -91,6 +91,7 @@ const PROVENANCE_MOCK_SERVER_PORT: u16 = 4251; pub(crate) const PUBLIC_NPM_REGISTRY_PORT: u16 = 4260; pub(crate) const PRIVATE_NPM_REGISTRY_1_PORT: u16 = 4261; pub(crate) const PRIVATE_NPM_REGISTRY_2_PORT: u16 = 4262; +pub(crate) const PRIVATE_NPM_REGISTRY_3_PORT: u16 = 4263; // Use the single-threaded scheduler. The hyper server is used as a point of // comparison for the (single-threaded!) benchmarks in cli/bench. We're not @@ -143,6 +144,8 @@ pub async fn run_all_servers() { npm_registry::private_npm_registry1(PRIVATE_NPM_REGISTRY_1_PORT); let private_npm_registry_2_server_futs = npm_registry::private_npm_registry2(PRIVATE_NPM_REGISTRY_2_PORT); + let private_npm_registry_3_server_futs = + npm_registry::private_npm_registry3(PRIVATE_NPM_REGISTRY_3_PORT); let mut futures = vec![ redirect_server_fut.boxed_local(), @@ -173,6 +176,7 @@ pub async fn run_all_servers() { futures.extend(npm_registry_server_futs); futures.extend(private_npm_registry_1_server_futs); futures.extend(private_npm_registry_2_server_futs); + futures.extend(private_npm_registry_3_server_futs); assert_eq!(futures.len(), TEST_SERVERS_COUNT); diff --git a/tests/util/server/src/servers/npm_registry.rs b/tests/util/server/src/servers/npm_registry.rs index acbd9cab48..4ada468fac 100644 --- a/tests/util/server/src/servers/npm_registry.rs +++ b/tests/util/server/src/servers/npm_registry.rs @@ -56,6 +56,14 @@ pub fn private_npm_registry2(port: u16) -> Vec> { ) } +pub fn private_npm_registry3(port: u16) -> Vec> { + run_npm_server( + port, + "npm private registry server error", + private_npm_registry3_handler, + ) +} + fn run_npm_server( port: u16, error_msg: &'static str, @@ -141,6 +149,13 @@ async fn private_npm_registry2_handler( handle_req_for_registry(req, &npm::PRIVATE_TEST_NPM_REGISTRY_2).await } +async fn private_npm_registry3_handler( + req: Request, +) -> Result>, anyhow::Error> { + // No auth for this registry + handle_req_for_registry(req, &npm::PRIVATE_TEST_NPM_REGISTRY_3).await +} + async fn handle_req_for_registry( req: Request, test_npm_registry: &npm::TestNpmRegistry,