sous-chefs/nodejs

Global installation of npm packages does not create executable in /usr/local/bin

Closed this issue · 4 comments

System: Amazon Linux 2015.09
Chef: 11.10.4 (AWS OpsWorks)

When running the following

node.default['nodejs']['install_method'] = 'binary'
node.default['nodejs']['version'] = '4.2.1'
include_recipe 'nodejs'
nodejs_npm 'bower'
nodejs_npm 'gulp'

As expected

  • node 4.2.1 is installed and which node returns /usr/local/bin/node
  • npm is installed and which npm returns /usr/local/bin/npm

Not as expected

  • bower and gulp are installed, I can see them in /usr/local/nodejs-binary-4.2.1/bin, however there is no executable in any of the default paths

As a result, when any user on the system tries to bower install bash fails with the error bower: command not found

Is this normal or an issue?
Do I have to specify a new path?
Is this because I've done a binary install instead of source or package?

Hi @cp5w ,

I'm afraid the best answer for this will be " It's not a bug, it's a feature "

nodejs_npm custom resource isn't (and will not) managing custom symlinks. It already has been discussed. This is the wrapper job to manage symlink to wherever you need it.

It's not possible to use custom resource's path attribute because npm install creates a node_modules folder hierarchy and not only bin/ folder. But maybe you can work on this base to " fake " a "bin folder" setup.

The easiest way to manage this symlink _when using nodejs binary install method_ is to use this path when creating symlink :

File.join(default['ark']['prefix_root'], 'nodejs-binary', 'bin', '<your bin>')

real path : /usr/local/nodejs-binary/bin/<your bin>

@BarthV thanks for a prompt response, and yes I suspected this may be the case from the offset, hence not coming in guns blazing its a bug its a bug! Great explanation as to why this can't be resolved within the LWRP.

At the moment, this is my work-around

link '/usr/local/bin/bower' do
  to "/usr/local/nodejs-#{node['nodejs']['install_method']}-#{node['nodejs']['version']}/bin/bower"
end
link '/usr/local/bin/gulp' do
  to "/usr/local/nodejs-#{node['nodejs']['install_method']}-#{node['nodejs']['version']}/bin/gulp"
end

which is currently working flawlessly.

Obviously this is not an elegant solution or portable for the long term, so at some point I will probably switch to using attributes so I can iterate over the packages and create symlinks dynamically (and add guards), and even create a wrapper so I can use this in other recipes with a one-liner.

Hi @teaforchris what about this script ?

node['nodejs']['npm_packages'].each do |pkg|
  name = pkg['name']
  link "/usr/local/bin/#{name}" do
    to "/usr/local/nodejs-#{node['nodejs']['install_method']}-#{node['nodejs']['version']}/bin/#{name}"
  end
end if node['nodejs']['npm_packages']

But the only problem I can see is when package name is different from cli command name 😢

You could use the "system::profile" cookbook/recipe to manually add the node bin path to the your global path:


Vagrant.configure("2") do |config|
...
  ####### Provision #######
  config.vm.provision 'chef_zero' do |chef|
...
    chef.add_recipe "system::default"
    chef.add_recipe "system::profile"
    chef.add_recipe "nodejs::nodejs_from_binary"
    chef.add_recipe "nodejs::npm_packages"

    #### Override Attributes ####
    chef.json = {
      "system" => {
        "profile": {
          "path_append": %w[
            /usr/local/nodejs-binary-10.16.3/bin/
          ]
        }
      },
      "nodejs" => {
        "install_method" => "binary",
        "version" => "10.16.3",
        "binary" => {
          "url" => "https://nodejs.org/dist/v10.16.3/node-v10.16.3-linux-x64.tar.gz",
          "checksum" => "2f0397bb81c1d0c9901b9aff82a933257bf60f3992227b86107111a75b9030d9"
        },
        "npm_packages": [
          {
            "name": "pm2",
            "version": "3.5.1"
          }
        ]
      }
    }
  end
end