Repository: railwayapp/nixpacks
Pull request #636 (2022-10-27 02:32:58)

Basic support for .nvmrc files
This PR adds basic support for parsing the node version from a `.nvmrc` file

Fixes https://github.com/railwayapp/nixpacks/issues/625


Patch:
diff --git a/examples/node-nvmrc/.nvmrc b/examples/node-nvmrc/.nvmrc
new file mode 100644
index 0000000..e24183e
--- /dev/null
+++ b/examples/node-nvmrc/.nvmrc
@@ -0,0 +1,1 @@
+v14.18.1
diff --git a/examples/node-nvmrc/index.js b/examples/node-nvmrc/index.js
new file mode 100644
index 0000000..bc8aaff
--- /dev/null
+++ b/examples/node-nvmrc/index.js
@@ -0,0 +1,1 @@
+console.log("Hello from Node");
diff --git a/examples/node-nvmrc/package-lock.json b/examples/node-nvmrc/package-lock.json
new file mode 100644
index 0000000..d18e0e0
--- /dev/null
+++ b/examples/node-nvmrc/package-lock.json
@@ -0,0 +1,12 @@
+{
+  "name": "node",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "node",
+      "version": "1.0.0"
+    }
+  }
+}
diff --git a/examples/node-nvmrc/package.json b/examples/node-nvmrc/package.json
new file mode 100644
index 0000000..2f12d8f
--- /dev/null
+++ b/examples/node-nvmrc/package.json
@@ -0,0 +1,8 @@
+{
+  "name": "node",
+  "version": "1.0.0",
+  "main": "index.js",
+  "scripts": {
+    "start": "node index.js"
+  }
+}
diff --git a/src/providers/node/mod.rs b/src/providers/node/mod.rs
index 3d4c379..d3cf6c9 100644
--- a/src/providers/node/mod.rs
+++ b/src/providers/node/mod.rs
@@ -211,7 +211,11 @@ impl NodeProvider {
     }
 
     /// Parses the package.json engines field and returns a Nix package if available
-    pub fn get_nix_node_pkg(package_json: &PackageJson, environment: &Environment) -> Result<Pkg> {
+    pub fn get_nix_node_pkg(
+        package_json: &PackageJson,
+        app: &App,
+        environment: &Environment,
+    ) -> Result<Pkg> {
         let env_node_version = environment.get_config_variable("NODE_VERSION");
 
         let pkg_node_version = package_json
@@ -219,7 +223,14 @@ impl NodeProvider {
             .clone()
             .and_then(|engines| engines.get("node").cloned());
 
-        let node_version = env_node_version.or(pkg_node_version);
+        let nvmrc_node_version = if app.includes_file(".nvmrc") {
+            let nvmrc = app.read_file(".nvmrc")?;
+            Some(nvmrc.trim().replace('v', ""))
+        } else {
+            None
+        };
+
+        let node_version = env_node_version.or(pkg_node_version).or(nvmrc_node_version);
 
         let node_version = match node_version {
             Some(node_version) => node_version,
@@ -327,7 +338,7 @@ impl NodeProvider {
         } else {
             PackageJson::default()
         };
-        let node_pkg = NodeProvider::get_nix_node_pkg(&package_json, env)?;
+        let node_pkg = NodeProvider::get_nix_node_pkg(&package_json, app, env)?;
 
         let pm_pkg: Pkg;
         let mut pkgs = Vec::<Pkg>::new();
@@ -486,6 +497,7 @@ mod test {
                     name: Some(String::default()),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new(DEFAULT_NODE_PKG_NAME)
@@ -503,6 +515,7 @@ mod test {
                     engines: Some(engines_node("*")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new(DEFAULT_NODE_PKG_NAME)
@@ -520,6 +533,7 @@ mod test {
                     engines: Some(engines_node("14")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -537,6 +551,7 @@ mod test {
                     engines: Some(engines_node("18.x")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-18_x")
@@ -549,6 +564,7 @@ mod test {
                     engines: Some(engines_node("14.X")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -566,6 +582,7 @@ mod test {
                     engines: Some(engines_node("18.x.x")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-18_x")
@@ -578,6 +595,7 @@ mod test {
                     engines: Some(engines_node("14.X.x")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -595,6 +613,7 @@ mod test {
                     engines: Some(engines_node("18.4.2")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-18_x")
@@ -607,6 +626,7 @@ mod test {
                     engines: Some(engines_node("14.8.x")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -619,6 +639,7 @@ mod test {
                     engines: Some(engines_node("14.x.8")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -636,6 +657,7 @@ mod test {
                     engines: Some(engines_node(">=14.10.3 <16")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?,
             Pkg::new("nodejs-14_x")
@@ -652,6 +674,7 @@ mod test {
                     name: Some(String::default()),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::new(BTreeMap::from([(
                     "NIXPACKS_NODE_VERSION".to_string(),
                     "14".to_string()
@@ -663,6 +686,23 @@ mod test {
         Ok(())
     }
 
+    #[test]
+    fn test_version_from_nvmrc() -> Result<()> {
+        assert_eq!(
+            NodeProvider::get_nix_node_pkg(
+                &PackageJson {
+                    name: Some(String::default()),
+                    ..Default::default()
+                },
+                &App::new("examples/node-nvmrc")?,
+                &Environment::default()
+            )?,
+            Pkg::new("nodejs-14_x")
+        );
+
+        Ok(())
+    }
+
     #[test]
     fn test_engine_invalid_version() -> Result<()> {
         // this test now defaults to lts
@@ -673,6 +713,7 @@ mod test {
                     engines: Some(engines_node("15")),
                     ..Default::default()
                 },
+                &App::new("examples/node")?,
                 &Environment::default()
             )?
             .name,


Test patch:
diff --git a/tests/snapshots/generate_plan_tests__node_nvmrc.snap b/tests/snapshots/generate_plan_tests__node_nvmrc.snap
new file mode 100644
index 0000000..7964588
--- /dev/null
+++ b/tests/snapshots/generate_plan_tests__node_nvmrc.snap
@@ -0,0 +1,54 @@
+---
+source: tests/generate_plan_tests.rs
+expression: plan
+---
+{
+  "providers": [],
+  "buildImage": "[build_image]",
+  "variables": {
+    "CI": "true",
+    "NIXPACKS_METADATA": "node",
+    "NODE_ENV": "production",
+    "NPM_CONFIG_PRODUCTION": "false"
+  },
+  "phases": {
+    "build": {
+      "name": "build",
+      "dependsOn": [
+        "install"
+      ],
+      "cacheDirectories": [
+        "node_modules/.cache"
+      ]
+    },
+    "install": {
+      "name": "install",
+      "dependsOn": [
+        "setup"
+      ],
+      "cmds": [
+        "npm ci"
+      ],
+      "cacheDirectories": [
+        "/root/.npm"
+      ],
+      "paths": [
+        "/app/node_modules/.bin"
+      ]
+    },
+    "setup": {
+      "name": "setup",
+      "nixPkgs": [
+        "nodejs-14_x",
+        "npm-8_x"
+      ],
+      "nixOverlays": [
+        "https://github.com/railwayapp/nix-npm-overlay/archive/main.tar.gz"
+      ],
+      "nixpkgsArchive": "[archive]"
+    }
+  },
+  "start": {
+    "cmd": "npm run start"
+  }
+}