<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>NVM on Stuart Forrest</title><link>https://www.uglydirtylittlestrawberry.co.uk/categories/nvm/</link><description>Recent content in NVM on Stuart Forrest</description><generator>Hugo -- gohugo.io</generator><language>en-uk</language><lastBuildDate>Wed, 29 Sep 2021 20:02:49 +0000</lastBuildDate><atom:link href="https://www.uglydirtylittlestrawberry.co.uk/categories/nvm/" rel="self" type="application/rss+xml"/><item><title>Using NVM with yarn and Nix shells</title><link>https://www.uglydirtylittlestrawberry.co.uk/posts/using-nvm-with-yarn-nix-shells/</link><pubDate>Wed, 29 Sep 2021 20:02:49 +0000</pubDate><guid>https://www.uglydirtylittlestrawberry.co.uk/posts/using-nvm-with-yarn-nix-shells/</guid><description>&lt;p>This post outlines how to use &lt;a href="https://github.com/nvm-sh/nvm">Node Version Manager (NVM)&lt;/a> with &lt;a href="https://yarnpkg.com/">yarn&lt;/a> and &lt;a href="https://nixos.org/manual/nix/unstable/command-ref/nix-shell.html">Nix shells&lt;/a>. I&amp;rsquo;m pretty new to Nix but have been using it tentatively for about 3 months since getting a new company laptop, I took the opportunity to try out something very different and hopefully learn something along the way.&lt;/p>
&lt;p>I have used NVM for a long time but since starting my Nix journey hadn&amp;rsquo;t needed to run a specific node version beyond a major one. At this point I was using Nix for some things (like switching between major node versions) and falling back to &lt;a href="https://brew.sh/">brew&lt;/a> and global installations of other tools when I couldn&amp;rsquo;t figure out the Nix pathway for the desired outcome. I still am, but slightly more heavily weighted towards Nix than I was.&lt;/p>
&lt;p>First I&amp;rsquo;ll define some things so that you have some idea of what on earth I am talking about.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="#what-is-nix">What is Nix?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#what-is-a-nix-shell">What is a Nix shell?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#what-is-node">What is Node?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#what-is-node-version-manager">What is Node Version Manager?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#what-is-yarn">What is Yarn?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#how-did-i-get-here">Why did I have to figure this out?&lt;/a>&lt;/li>
&lt;li>&lt;a href="#using-nvm-with-yarn-and-nix-shells">Using NVM with yarn and Nix shells&lt;/a>&lt;/li>
&lt;li>&lt;a href="#summary">Summary&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="what-is-nix">What is Nix?&lt;/h2>
&lt;p>&lt;a href="https://nixos.org/">Nix&lt;/a> is a cross-platform package manager that utilises a purely functional deployment model. A Nix package describes how it should be built and the exact state of its required dependencies. Nix uniquely hashes each dependency and links the package to its declared dependency through some ninja symlinking. Nix provides the ability to describe and build reliably reproducible state for an environment.&lt;/p>
&lt;h2 id="what-is-a-nix-shell">What is a Nix shell?&lt;/h2>
&lt;p>Similar to a Nix package, a &lt;a href="https://nixos.org/manual/nix/unstable/command-ref/nix-shell.html">Nix shell&lt;/a> is describes all of the packages that should be installed in a (mostly) isolated shell that Nix will build for you. Nix shells provide the ability to create development environments with just the dependencies a project requires, or to use packages that you don&amp;rsquo;t want to install globally. Think of Nix shells as a different approach to what a &lt;a href="https://www.docker.com/">Docker container&lt;/a> can do for isolated, reproducible development environments&lt;/p>
&lt;h2 id="what-is-node">What is Node?&lt;/h2>
&lt;p>&lt;a href="https://nodejs.org/en/about/">Node is an asynchronous event-driven JavaScript runtime&lt;/a> released in 2009 that is based on the &lt;a href="https://v8.dev/">V8 Javascript engine&lt;/a> most famously used by the &lt;a href="https://www.chromium.org/Home">Chromium project&lt;/a> and it&amp;rsquo;s offshoots (eg. &lt;a href="https://www.google.com/intl/en_uk/chrome/">Chrome&lt;/a>). Node allows javascript projects to be built and run on any major platform by bundling a whole swathe of platform specific bindings for OS operations.&lt;/p>
&lt;h2 id="what-is-node-version-manager">What is Node Version Manager?&lt;/h2>
&lt;p>As with any piece of software Node has had plenty of different versions built and released since its inception. Ordinarily running multiple versions of the same software in a single environment is always a little tricky. &lt;a href="https://github.com/nvm-sh/nvm">Node Version Manager (NVM)&lt;/a> is a tool that makes that relatively simple to do with Node.&lt;/p>
&lt;h2 id="what-is-yarn">What is Yarn?&lt;/h2>
&lt;p>&lt;a href="https://yarnpkg.com/">Yarn&lt;/a> is a package manger for Node/javascript projects, the project does not have to be intended to run on node (eg. frontend projects) but does need to be built in a &lt;code>node&lt;/code> environment.&lt;/p>
&lt;h2 id="how-did-i-get-here">How did I get here?&lt;/h2>
&lt;p>I was asked to work on a yarn based project and created a &lt;code>shell.nix&lt;/code> file that looked like this to create a development environment (irrelevant parts removed):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>{ &lt;span style="color:#a6e22e">pkgs&lt;/span> &lt;span style="color:#f92672">?&lt;/span> &lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">nixpkgs&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {} }&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#a6e22e">pkgs&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">let&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#a6e22e">mkShell&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">buildInputs&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">nodejs&lt;/span>&lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">12_&lt;/span>&lt;span style="color:#a6e22e">x&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">yarn&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>Nix was able to build the shell fine but when I tried to build the project with &lt;code>yarn&lt;/code> I got the following error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>error @cumulusds/aws-cloudformation-wait-ready@0.0.2: The engine &lt;span style="color:#e6db74">&amp;#34;node&amp;#34;&lt;/span> is incompatible with this module. Expected version &lt;span style="color:#e6db74">&amp;#34;^12.13.0&amp;#34;&lt;/span>. Got &lt;span style="color:#e6db74">&amp;#34;12.22.6&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>error Found incompatible module.&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>It turns out the project requires a very specific version of node, &lt;code>12.13.1&lt;/code>. I didn&amp;rsquo;t have &lt;code>nvm&lt;/code> enabled at this point and had wanted to use Nix shells to create the development environment. But time is precious when I&amp;rsquo;m working so after not finding too much straight forward advice on Google I checked out the source for the Nix &lt;a href="https://github.com/NixOS/nixpkgs/blob/nixos-21.05/pkgs/development/web/nodejs/v12.nix">&lt;code>node-12_x&lt;/code> package&lt;/a>. Whilst &lt;code>nixpkgs&lt;/code> has derivatives for each major Node version, they install the latest minor/patch version, which makes sense. So, using the &lt;code>node-12_x&lt;/code> Nix derivation was not going to work.&lt;/p>
&lt;p>I removed &lt;code>node&lt;/code> from my Nix shell and enabled &lt;code>nvm&lt;/code> on my machine. With no &lt;code>node&lt;/code> being built into the shell the node installation available in my global environment should be used by &lt;code>yarn&lt;/code>. I ran &lt;code>nvm use 12.13.1&lt;/code> and built my Nix shell. My &lt;code>shell.nix&lt;/code> file now looked like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>{ &lt;span style="color:#a6e22e">pkgs&lt;/span> &lt;span style="color:#f92672">?&lt;/span> &lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">nixpkgs&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {} }&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#a6e22e">pkgs&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">let&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#a6e22e">mkShell&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">buildInputs&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">yarn&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>I built the the Nix shell and tried to build the project with &lt;code>yarn&lt;/code> again but got the following error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>error @cumulusds/aws-cloudformation-wait-ready@0.0.2: The engine &lt;span style="color:#e6db74">&amp;#34;node&amp;#34;&lt;/span> is incompatible with this module. Expected version &lt;span style="color:#e6db74">&amp;#34;^12.13.0&amp;#34;&lt;/span>. Got &lt;span style="color:#e6db74">&amp;#34;14.17.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>error Found incompatible module.&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>The version of &lt;code>node&lt;/code> I asked &lt;code>nvm&lt;/code> for is not being used. I ran &lt;code>nvm ls&lt;/code> and the &lt;code>14.17.1&lt;/code> is installed on my machine so I deleted it and tried again. Same error! I looked for an underlying &lt;code>node&lt;/code> installation from &lt;code>brew&lt;/code> in case it was confusing matters? There was one from &lt;code>brew&lt;/code>, which I deleted. The only possible version of node now in &lt;code>$PATH&lt;/code> should be &lt;code>12.13.1&lt;/code> managed by &lt;code>nvm&lt;/code>.&lt;/p>
&lt;p>I built the the Nix shell and tried to build the project with &lt;code>yarn&lt;/code> again. You might be able to guess what happened next, same error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>error @cumulusds/aws-cloudformation-wait-ready@0.0.2: The engine &lt;span style="color:#e6db74">&amp;#34;node&amp;#34;&lt;/span> is incompatible with this module. Expected version &lt;span style="color:#e6db74">&amp;#34;^12.13.0&amp;#34;&lt;/span>. Got &lt;span style="color:#e6db74">&amp;#34;14.17.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>error Found incompatible module.&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>I&amp;rsquo;m pretty sure at this point that &lt;code>node 14.17.1&lt;/code> is not on my machine. My next step was to remove &lt;code>node&lt;/code>, &lt;code>nvm&lt;/code> and directories where &lt;code>nvm&lt;/code> kept node installations, nuking my machine of node. I built the the Nix shell and tried to build the project with &lt;code>yarn&lt;/code> again. Same error:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>error @cumulusds/aws-cloudformation-wait-ready@0.0.2: The engine &lt;span style="color:#e6db74">&amp;#34;node&amp;#34;&lt;/span> is incompatible with this module. Expected version &lt;span style="color:#e6db74">&amp;#34;^12.13.0&amp;#34;&lt;/span>. Got &lt;span style="color:#e6db74">&amp;#34;14.17.1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>error Found incompatible module.&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>I go and have a cup of tea to rethink the problem.&lt;/p>
&lt;p>The problem is that in my Nix shell &lt;code>yarn&lt;/code> is finding a node installation and yet there is no &lt;code>node&lt;/code> installation on my machine. Additionally, when I do have &lt;code>node&lt;/code> on my machine the output of &lt;code>node --version&lt;/code> and &lt;code>yarn node --version&lt;/code> will differ within the Nix shell.&lt;/p>
&lt;p>By the time I&amp;rsquo;ve finished my cup of tea I think I know what is going on. When Nix builds a package from a derivation it links the built package explicitly to its declared dependencies, this allows two packages in the same environment to depend on different versions of the same dependency. If one of the &lt;code>buildInputs&lt;/code> for my Nix shell declares a dependency on &lt;code>node&lt;/code> then when it executes a &lt;code>node&lt;/code> command it will call the node installation it declared as a dependency, not the one available at the top level of the shell.&lt;/p>
&lt;p>Looking at the &lt;code>buildInputs&lt;/code> of my shells the first one worth checking for a &lt;code>node&lt;/code> dependency was &lt;code>yarn&lt;/code>. Sure enough, the &lt;a href="https://github.com/NixOS/nixpkgs/blob/nixos-21.05/pkgs/development/tools/yarn/default.nix#L12">source&lt;/a> for the &lt;code>yarn&lt;/code> derivation declared &lt;code>nodejs&lt;/code> as a dependency (the latest node version). This meant that version of &lt;code>node&lt;/code> managed by &lt;code>nvm&lt;/code> in my global environment would not be found by &lt;code>yarn&lt;/code> in my Nix shell.&lt;/p>
&lt;h2 id="using-nvm-with-yarn-and-nix-shells">Using NVM with yarn and Nix shells&lt;/h2>
&lt;p>If you want to use NVM with Yarn and Nix shells then you must modify the &lt;code>yarn&lt;/code> derivation within your Nix shell definition. Some more information about overrides and how they are intended to used within Nix is available &lt;a href="https://nixos.org/guides/nix-pills/override-design-pattern.html#idm140737319877072">here&lt;/a>&lt;/p>
&lt;p>The desired outcome is to not have a &lt;code>node&lt;/code> installation built into the Nix shell so that NVM&amp;rsquo;s managed installations are picked up from outside of the shell. To achieve this I modified my &lt;code>shell.nix&lt;/code> file to look like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-js" data-lang="js">&lt;span style="display:flex;">&lt;span>{ &lt;span style="color:#a6e22e">pkgs&lt;/span> &lt;span style="color:#f92672">?&lt;/span> &lt;span style="color:#66d9ef">import&lt;/span> &lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#a6e22e">nixpkgs&lt;/span>&lt;span style="color:#f92672">&amp;gt;&lt;/span> {} }&lt;span style="color:#f92672">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#a6e22e">pkgs&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">let&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">in&lt;/span> &lt;span style="color:#a6e22e">mkShell&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">buildInputs&lt;/span> &lt;span style="color:#f92672">=&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> (&lt;span style="color:#a6e22e">yarn&lt;/span>.&lt;span style="color:#a6e22e">override&lt;/span> { &lt;span style="color:#a6e22e">nodejs&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>; })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;p>As the &lt;code>yarn&lt;/code> derivation was listing &lt;code>nodejs&lt;/code> as a dependency I needed to override that, effectively to nullify it and stop Nix building it into the shell. This would mean that when &lt;code>yarn&lt;/code> executes a &lt;code>node&lt;/code> command the external &lt;code>node&lt;/code> installation would be used, in this case the one managed by NVM.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>I have found the learning curve with Nix pretty steep and I think I&amp;rsquo;ve only scratched the surface so far. As for using NVM with Nix shells, that was really helpful in beginning to transition to using Nix shells to orchestrate isolated development environments for my projects.&lt;/p>
&lt;p>If you are using Nix and need very specific versions of Node for a project, know that using NVM with yarn and Nix shells is possible. With minimal change to your Nix shells to use a familiar workflow that incorporates NVM I found to be a good compromise whilst still getting the hang of Nix.&lt;/p>
&lt;h3 id="get-in-contact">Get in contact&lt;/h3>
&lt;p>If you have comments, questions or better ways to do anything that I have discussed in this post then please get in contact via &lt;a href="https://linkedin.com/in/stuart-f-41a43b180">LinkedIn&lt;/a> or &lt;a href="mailto:stuart@uglydirtylittlestrawberry.co.uk">email&lt;/a>.&lt;/p></description></item></channel></rss>