Repository: railwayapp/nixpacks (rust)

Issue:
Yarn Berry implementation wrongly resolves versions
Hi!

I'm a Railway user, and actually a Yarn team member as of recently.  All my projects use the bleeding edge of Yarn Berry, and the way that the Node Nixpack works is that it is hard-coded to download the latest stable Berry version, as can be seen here:

https://github.com/railwayapp/nixpacks/blob/fac9c30a6f0aa3368a1b648d0398546681dbbb77/src/providers/node/mod.rs#L246

This is **not** what should be done.  As Yarn releases new versions, things can change.
For instance, if a project uses TypeScript, it would have broken with the recent Yarn 3.2.4 release (even though it was a semver-patch release) because the package SHAs changed.

What **should** be done:

- If the `package.json` has a `packageManager` field [(for Corepack)](https://github.com/nodejs/corepack#-corepack), that field should take priority on determining which package manager to use (Corepack should be used to download and run the version).
- If it doesn't, any version specified in the `.yarnrc.yml` should actually *be used*!

Corepack, while still marked as experimental, is the preferred installation method by [Yarn](https://yarnpkg.com/getting-started/install#install-corepack), [pnpm](https://pnpm.io/installation#using-corepack), and also has support for npm.

Pull request:
fix: get version from .yarnrc.yml
<!-- Thanks for sending a pull request! -->
<!-- Please add a useful description explaining what this PR does -->

This PR replaces `berry` with `.yarnrc.yml's yarnPath` (if it exists) as default version for node(yarn) provider.

fixes #621 

<!-- PR Checklist  -->
<!-- - [ ] Tests are added/updated if needed  -->
<!-- - [ ] Docs are updated if needed  -->


Patch:
diff --git a/src/providers/node/mod.rs b/src/providers/node/mod.rs
index e0fa20b..1f3c3b8 100644
--- a/src/providers/node/mod.rs
+++ b/src/providers/node/mod.rs
@@ -42,6 +42,12 @@ pub struct PackageJson {
     pub project_type: Option<String>,
 }
 
+#[derive(Serialize, Deserialize, Default, Debug)]
+pub struct Yarnrc {
+    #[serde(rename = "yarnPath")]
+    pub yarn_path: Option<String>,
+}
+
 #[derive(Default, Debug)]
 pub struct NodeProvider {}
 
@@ -237,23 +243,28 @@ impl NodeProvider {
             return None;
         }
 
-        let mut install_cmd = "npm i";
+        let mut install_cmd = "npm i".to_string();
         let package_manager = NodeProvider::get_package_manager(app);
         if package_manager == "pnpm" {
-            install_cmd = "pnpm i --frozen-lockfile";
+            install_cmd = "pnpm i --frozen-lockfile".to_string();
         } else if package_manager == "yarn" {
             if app.includes_file(".yarnrc.yml") {
-                install_cmd = "yarn set version berry && yarn install --check-cache";
+                install_cmd = "yarn set version berry && yarn install --check-cache".to_string();
+                let yarnrc_yml: Yarnrc = app.read_yaml(".yarnrc.yml").unwrap_or_default();
+                if let Some(path) = yarnrc_yml.yarn_path {
+                    install_cmd =
+                        format!("yarn set version ./{} && yarn install --check-cache", path);
+                }
             } else {
-                install_cmd = "yarn install --frozen-lockfile";
+                install_cmd = "yarn install --frozen-lockfile".to_string();
             }
         } else if app.includes_file("package-lock.json") {
-            install_cmd = "npm ci";
+            install_cmd = "npm ci".to_string();
         } else if app.includes_file("bun.lockb") {
-            install_cmd = "bun i --no-save";
+            install_cmd = "bun i --no-save".to_string();
         }
 
-        Some(install_cmd.to_string())
+        Some(install_cmd)
     }
 
     fn get_package_manager_cache_dir(app: &App) -> String {


Test patch:
diff --git a/tests/snapshots/generate_plan_tests__node_yarn_berry.snap b/tests/snapshots/generate_plan_tests__node_yarn_berry.snap
index 0838564..2f4e23f 100644
--- a/tests/snapshots/generate_plan_tests__node_yarn_berry.snap
+++ b/tests/snapshots/generate_plan_tests__node_yarn_berry.snap
@@ -30,7 +30,7 @@ expression: plan
         "setup"
       ],
       "cmds": [
-        "yarn set version berry && yarn install --check-cache"
+        "yarn set version ./.yarn/releases/yarn-3.2.4.cjs && yarn install --check-cache"
       ],
       "cacheDirectories": [
         "/usr/local/share/.cache/yarn/v6"