Skip to content

Commit 44affa8

Browse files
committed
fetch and resolve yarn2+ using @yarnpkg/cli-dist
1 parent 6e26543 commit 44affa8

File tree

5 files changed

+81
-12
lines changed

5 files changed

+81
-12
lines changed

crates/volta-core/src/error/kind.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ pub enum ErrorKind {
402402
/// Thrown when the shim binary is called directly, not through a symlink
403403
RunShimDirectly,
404404

405+
/// Thrown when there was an error setting a tool to executable
406+
SetToolExecutable {
407+
tool: String,
408+
},
409+
405410
/// Thrown when there was an error copying an unpacked tool to the image directory
406411
SetupToolImageError {
407412
tool: String,
@@ -1207,6 +1212,13 @@ Please verify your internet connection.",
12071212
12081213
Please use the existing shims provided by Volta (node, yarn, etc.) to run tools."
12091214
),
1215+
ErrorKind::SetToolExecutable { tool } => write!(
1216+
f,
1217+
r#"Could not set "{}" to executable
1218+
1219+
{}"#,
1220+
tool, PERMISSIONS_CTA
1221+
),
12101222
ErrorKind::SetupToolImageError { tool, version, dir } => write!(
12111223
f,
12121224
"Could not create environment for {} v{}
@@ -1474,6 +1486,7 @@ impl ErrorKind {
14741486
ErrorKind::RegistryFetchError { .. } => ExitCode::NetworkError,
14751487
ErrorKind::RunShimDirectly => ExitCode::InvalidArguments,
14761488
ErrorKind::SetupToolImageError { .. } => ExitCode::FileSystemError,
1489+
ErrorKind::SetToolExecutable { .. } => ExitCode::FileSystemError,
14771490
ErrorKind::ShimCreateError { .. } => ExitCode::FileSystemError,
14781491
ErrorKind::ShimRemoveError { .. } => ExitCode::FileSystemError,
14791492
ErrorKind::StringifyBinConfigError => ExitCode::UnknownError,

crates/volta-core/src/tool/npm/fetch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ fn determine_remote_url(version: &Version, hooks: Option<&ToolHooks<Npm>>) -> Fa
129129
let distro_file_name = Npm::archive_filename(&version_str);
130130
hook.resolve(version, &distro_file_name)
131131
}
132-
_ => Ok(public_registry_package("npm", &version_str)),
132+
_ => Ok(public_registry_package("npm", "npm", &version_str)),
133133
}
134134
}
135135

crates/volta-core/src/tool/registry.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,13 @@ cfg_if! {
3030
}
3131
}
3232

33-
pub fn public_registry_package(package: &str, version: &str) -> String {
33+
// need package and filename for namespaced tools like @yarnpkg/cli-dist, which is located at
34+
// https://registry.npmjs.org/@yarnpkg/cli-dist/-/cli-dist-1.2.3.tgz
35+
pub fn public_registry_package(package: &str, filename: &str, version: &str) -> String {
3436
format!(
3537
"{}/-/{}-{}.tgz",
3638
public_registry_index(package),
37-
package,
39+
filename,
3840
version
3941
)
4042
}

crates/volta-core/src/tool/yarn/fetch.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Provides fetcher for Yarn distributions
22
3+
use std::env;
34
use std::fs::File;
4-
use std::path::Path;
5+
use std::path::{Path, PathBuf};
56

67
use super::super::download_tool_error;
78
use super::super::registry::{find_unpack_dir, public_registry_package};
89
use crate::error::{Context, ErrorKind, Fallible};
9-
use crate::fs::{create_staging_dir, create_staging_file, rename};
10+
use crate::fs::{create_staging_dir, create_staging_file, rename, set_executable};
1011
use crate::hook::ToolHooks;
1112
use crate::layout::volta_home;
1213
use crate::style::{progress_bar, tool_version};
@@ -15,7 +16,7 @@ use crate::version::VersionSpec;
1516
use archive::{Archive, Tarball};
1617
use fs_utils::ensure_containing_dir_exists;
1718
use log::debug;
18-
use semver::Version;
19+
use semver::{Version, VersionReq};
1920

2021
pub fn fetch(version: &Version, hooks: Option<&ToolHooks<Yarn>>) -> Fallible<()> {
2122
let yarn_dir = volta_home()?.yarn_inventory_dir();
@@ -79,11 +80,14 @@ fn unpack_archive(archive: Box<dyn Archive>, version: &Version) -> Fallible<()>
7980
version: version_string.clone(),
8081
})?;
8182

83+
let unpack_dir = find_unpack_dir(temp.path())?;
84+
// "bin/yarn" is not executable in the @yarnpkg/cli-dist package
85+
ensure_bin_is_executable(&unpack_dir, "yarn")?;
86+
8287
let dest = volta_home()?.yarn_image_dir(&version_string);
8388
ensure_containing_dir_exists(&dest)
8489
.with_context(|| ErrorKind::ContainingDirError { path: dest.clone() })?;
8590

86-
let unpack_dir = find_unpack_dir(temp.path())?;
8791
rename(unpack_dir, &dest).with_context(|| ErrorKind::SetupToolImageError {
8892
tool: "Yarn".into(),
8993
version: version_string.clone(),
@@ -122,7 +126,19 @@ fn determine_remote_url(version: &Version, hooks: Option<&ToolHooks<Yarn>>) -> F
122126
let distro_file_name = Yarn::archive_filename(&version_str);
123127
hook.resolve(version, &distro_file_name)
124128
}
125-
_ => Ok(public_registry_package("yarn", &version_str)),
129+
_ => {
130+
let matches_yarn_berry = VersionReq::parse(">2").unwrap();
131+
if env::var_os("VOLTA_FEATURE_YARN_3").is_some() && matches_yarn_berry.matches(version)
132+
{
133+
Ok(public_registry_package(
134+
"@yarnpkg/cli-dist",
135+
"cli-dist",
136+
&version_str,
137+
))
138+
} else {
139+
Ok(public_registry_package("yarn", "yarn", &version_str))
140+
}
141+
}
126142
}
127143
}
128144

@@ -138,3 +154,8 @@ fn fetch_remote_distro(
138154
url,
139155
))
140156
}
157+
158+
fn ensure_bin_is_executable(unpack_dir: &PathBuf, tool: &str) -> Fallible<()> {
159+
let exec_path = unpack_dir.join("bin").join(tool);
160+
set_executable(&exec_path).with_context(|| ErrorKind::SetToolExecutable { tool: tool.into() })
161+
}

crates/volta-core/src/tool/yarn/resolve.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Provides resolution of Yarn requirements into specific versions
22
3+
use std::env;
4+
35
use super::super::registry::{
46
public_registry_index, PackageDetails, PackageIndex, RawPackageMetadata,
57
NPM_ABBREVIATED_ACCEPT_HEADER,
@@ -69,8 +71,8 @@ fn resolve_semver(matching: VersionReq, hooks: Option<&ToolHooks<Yarn>>) -> Fall
6971
}
7072
}
7173

72-
fn fetch_yarn_index() -> Fallible<(String, PackageIndex)> {
73-
let url = public_registry_index("yarn");
74+
fn fetch_yarn_index(package: &str) -> Fallible<(String, PackageIndex)> {
75+
let url = public_registry_index(package);
7476
let spinner = progress_spinner(format!("Fetching public registry: {}", url));
7577
let metadata: RawPackageMetadata = attohttpc::get(&url)
7678
.header(ACCEPT, NPM_ABBREVIATED_ACCEPT_HEADER)
@@ -84,7 +86,12 @@ fn fetch_yarn_index() -> Fallible<(String, PackageIndex)> {
8486
}
8587

8688
fn resolve_custom_tag(tag: String) -> Fallible<Version> {
87-
let (url, mut index) = fetch_yarn_index()?;
89+
// yarn2 and yarn3 are under "@yarnpkg/cli-dist" instead of "yarn"
90+
let package: &str = match env::var_os("VOLTA_FEATURE_YARN_3") {
91+
Some(_) => "@yarnpkg/cli-dist",
92+
None => "yarn",
93+
};
94+
let (url, mut index) = fetch_yarn_index(package)?;
8895

8996
match index.tags.remove(&tag) {
9097
Some(version) => {
@@ -109,7 +116,32 @@ fn resolve_latest_legacy(url: String) -> Fallible<Version> {
109116
}
110117

111118
fn resolve_semver_from_registry(matching: VersionReq) -> Fallible<Version> {
112-
let (url, index) = fetch_yarn_index()?;
119+
if env::var_os("VOLTA_FEATURE_YARN_3").is_some() {
120+
// first try yarn2+, which uses "@yarnpkg/cli-dist" instead of "yarn"
121+
let (url, index) = fetch_yarn_index("@yarnpkg/cli-dist")?;
122+
let details_opt = index
123+
.entries
124+
.into_iter()
125+
.find(|PackageDetails { version, .. }| matching.matches(version));
126+
127+
match details_opt {
128+
Some(details) => {
129+
debug!(
130+
"Found yarn@{} matching requirement '{}' from {}",
131+
details.version, matching, url
132+
);
133+
return Ok(details.version);
134+
}
135+
None => {
136+
debug!(
137+
"Did not find yarn matching requirement '{}' from {}",
138+
matching, url
139+
);
140+
}
141+
}
142+
}
143+
144+
let (url, index) = fetch_yarn_index("yarn")?;
113145

114146
let details_opt = index
115147
.entries
@@ -124,6 +156,7 @@ fn resolve_semver_from_registry(matching: VersionReq) -> Fallible<Version> {
124156
);
125157
Ok(details.version)
126158
}
159+
// at this point Yarn is not found in either registry
127160
None => Err(ErrorKind::YarnVersionNotFound {
128161
matching: matching.to_string(),
129162
}

0 commit comments

Comments
 (0)