diff --git a/packages/zpm-utils/src/system.rs b/packages/zpm-utils/src/system.rs index 4ed8cecd..f7588b76 100644 --- a/packages/zpm-utils/src/system.rs +++ b/packages/zpm-utils/src/system.rs @@ -221,6 +221,14 @@ impl Requirements { !self.arch.is_empty() || !self.os.is_empty() || !self.libc.is_empty() } + pub fn has_linux_os(&self) -> bool { + self.os.contains(&Os::Linux) + } + + pub fn set_libc(&mut self, libc: Libc) { + self.libc = vec![libc]; + } + pub fn validate_system(&self, system: &System) -> bool { let is_arch_valid = self.arch.is_empty() || system.arch.as_ref().map_or(false, |arch| self.arch.contains(&arch)); diff --git a/packages/zpm/src/commands/debug/bench.rs b/packages/zpm/src/commands/debug/bench.rs index 068d9fa3..e50af0e2 100644 --- a/packages/zpm/src/commands/debug/bench.rs +++ b/packages/zpm/src/commands/debug/bench.rs @@ -174,13 +174,13 @@ impl BenchRun { let hyperfine_args = vec![ "--min-runs=30".to_string(), "--warmup=4".to_string(), - "--show-output".to_string(), format!("--export-json={bench_json_string}"), format!("--prepare={current_exec_string} debug bench {mode_string} --cleanup"), format!("{current_exec_string} debug bench {mode_string} --iteration"), ]; ScriptEnvironment::new()? + .enable_shell_forwarding() .with_cwd(temp_directory) .run_exec("hyperfine", hyperfine_args) .await? diff --git a/packages/zpm/src/fetchers/npm.rs b/packages/zpm/src/fetchers/npm.rs index 9d19ba00..63d0487c 100644 --- a/packages/zpm/src/fetchers/npm.rs +++ b/packages/zpm/src/fetchers/npm.rs @@ -65,7 +65,7 @@ pub async fn fetch_locator<'a>(context: &InstallContext<'a>, locator: &Locator, let cached_blob = package_cache.ensure_blob(locator.clone(), ".zip", || async { let bytes - = http_npm::get(&http_npm::NpmHttpParams { + = http_npm::get_raw(&http_npm::NpmHttpParams { http_client: &project.http_client, registry: ®istry_base, path: ®istry_path, diff --git a/packages/zpm/src/http.rs b/packages/zpm/src/http.rs index 6f5a5048..988b3cc0 100644 --- a/packages/zpm/src/http.rs +++ b/packages/zpm/src/http.rs @@ -363,7 +363,7 @@ impl HttpClient { /// Performs a cached GET request. If the URL has already been fetched, /// returns the cached response bytes. Concurrent requests to the same URL /// will wait for the first request to complete and share the result. - pub async fn cached_get(&self, url: impl AsRef) -> Result { + pub async fn cached_get(&self, url: impl AsRef, headers: Option) -> Result { let url_str = url.as_ref().to_string(); @@ -374,7 +374,8 @@ impl HttpClient { let result = cell.get_or_init(|| async { let request - = self.get(&url_str)?; + = self.get(&url_str)? + .add_headers(headers); let result = request.send().await?; diff --git a/packages/zpm/src/http_npm.rs b/packages/zpm/src/http_npm.rs index ff4d58d8..521dc0cf 100644 --- a/packages/zpm/src/http_npm.rs +++ b/packages/zpm/src/http_npm.rs @@ -274,19 +274,48 @@ pub async fn get(params: &NpmHttpParams<'_>) -> Result { let url = format!("{}{}", params.registry, params.path); - let bytes = match params.authorization { - Some(authorization) => { - params.http_client.get(&url)? - .header("authorization", Some(authorization)) - .send().await? - .error_for_status()? - .bytes().await? - }, + let mut headers + = http::HeaderMap::new(); + + headers.insert( + http::header::ACCEPT, + http::HeaderValue::from_static("application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"), + ); + + if let Some(authorization) = params.authorization { + headers.insert( + http::header::AUTHORIZATION, + http::HeaderValue::from_str(authorization).unwrap(), + ); + } - None => { - params.http_client.cached_get(&url).await? - }, - }; + let bytes + = params.http_client.cached_get(&url, Some(headers)).await?; + + Ok(bytes) +} + +pub async fn get_raw(params: &NpmHttpParams<'_>) -> Result { + let url + = format!("{}{}", params.registry, params.path); + + let mut headers + = http::HeaderMap::new(); + + if let Some(authorization) = params.authorization { + headers.insert( + http::header::AUTHORIZATION, + http::HeaderValue::from_str(authorization).unwrap(), + ); + } + + let bytes + = params.http_client.get(&url)? + .add_headers(Some(headers)) + .send() + .await? + .error_for_status()? + .bytes().await?; Ok(bytes) } diff --git a/packages/zpm/src/resolvers/npm.rs b/packages/zpm/src/resolvers/npm.rs index 9db79f5f..beaea156 100644 --- a/packages/zpm/src/resolvers/npm.rs +++ b/packages/zpm/src/resolvers/npm.rs @@ -7,6 +7,7 @@ use serde_with::{serde_as, MapSkipError}; use zpm_config::ConfigExt; use zpm_parsers::{JsonDocument, RawJsonValue}; use zpm_primitives::{AnonymousSemverRange, Descriptor, Ident, Locator, Reference, RegistryReference, RegistrySemverRange, RegistryTagRange, UrlReference}; +use zpm_utils::Libc; use crate::{ error::Error, @@ -33,7 +34,7 @@ struct RemoteManifestWithScripts { scripts: BTreeMap, } -fn fix_manifest(manifest: &mut RemoteManifestWithScripts) { +fn fix_manifest(manifest: &mut RemoteManifestWithScripts, package_ident: &Ident) { // Manually add node-gyp dependency if there is a script using it and not already set // This is because the npm registry will automatically add a `node-gyp rebuild` install script // in the metadata if there is not already an install script and a binding.gyp file exists. @@ -47,13 +48,24 @@ fn fix_manifest(manifest: &mut RemoteManifestWithScripts) { } } } + + // Infer libc field for Linux packages. The abbreviated packument format + // (application/vnd.npm.install-v1+json) doesn't include the libc field, + // so we infer it from the package name and os field. + if manifest.remote.requirements.has_linux_os() { + if package_ident.as_str().contains("musl") { + manifest.remote.requirements.set_libc(Libc::Musl); + } else { + manifest.remote.requirements.set_libc(Libc::Glibc); + } + } } fn build_resolution_result(context: &InstallContext, descriptor: &Descriptor, package_ident: &Ident, version: zpm_semver::Version, mut manifest: RemoteManifestWithScripts) -> ResolutionResult { let project = context.project .expect("The project is required for resolving a workspace package"); - fix_manifest(&mut manifest); + fix_manifest(&mut manifest, package_ident); let dist_manifest = manifest.remote.dist .as_ref() @@ -268,7 +280,7 @@ pub async fn resolve_locator(context: &InstallContext<'_>, locator: &Locator, pa let mut manifest: RemoteManifestWithScripts = JsonDocument::hydrate_from_slice(&bytes[..])?; - fix_manifest(&mut manifest); + fix_manifest(&mut manifest, ¶ms.ident); let resolution = Resolution::from_remote_manifest(locator.clone(), manifest.remote.clone());