Simple CircleCI 2.0 configuration fails for global NPM package installation
Asked Answered
D

4

8

I have a Dockerfile that works fine:

FROM node:10
RUN npm set unsafe-perm true
RUN npm install -g '@oresoftware/[email protected]'

but the same CircleCI config.yml file that mirrors the above Dockerfile does not work:

{
  "version": 2,
  "jobs": {
    "build": {
      "docker": [
        {
          "image": "circleci/node:10"
        }
      ],
      "steps": [
        {
          "run": "npm set unsafe-perm true"
        },
        {
          "run": "npm install -g --loglevel=warn '@oresoftware/[email protected]'"
        }
      ]
    }
  }
}

I get the following error on CircleCI using the above config.yml file:

#!/bin/bash -eo pipefail
npm install -g --loglevel=warn @oresoftware/r2g
npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules
npm ERR! path /usr/local/lib/node_modules
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access
npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules'
npm ERR!  { [Error: EACCES: permission denied, access '/usr/local/lib/node_modules']
npm ERR!   stack:
npm ERR!    'Error: EACCES: permission denied, access \'/usr/local/lib/node_modules\'',
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/local/lib/node_modules' }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! 
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator (though this is not recommended).

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/circleci/.npm/_logs/2018-06-18T18_26_53_651Z-debug.log
Exited with code 243

CircleCI 2.0 should use Docker, so I am not sure why this permissions error is happening.

Develop answered 18/6, 2018 at 18:29 Comment(9)
note that I shouldn't have to use sudo for this, but if I do use sudo, that also fails on CircleCI with a different permissions error.Develop
Just curious, I'm a new user to CircleCI myself - aren't you doing these same steps twice? Once in the Docker build and once in CircleCI steps? Shouldn't you just be doing it once? (I'd say in the CircleCI steps)Fogy
I am not sure what you mean, in the above config, "steps" are part of the "build", right?Develop
I could be misunderstanding, but I assumed that the Docker image will build and do those two RUN npm steps as it builds, then CircleCI will try to do what's under steps separately on the Docker image - basically the same steps which have already been run when the image was built.Fogy
I'm wondering if it would work if you'd either drop the two RUN steps from the Dockerfile OR drop the two steps from the CircleCI config. As I said, I'm just a beginner with this myself. I just feel like your duplicating and that's where you're getting the permissions conflict.Fogy
Maybe changing the installation directory to something in your home directory works, you can set the prefix config setting and adjust the $PATH. There's more detail in this articleSteeplebush
@Steeplebush maybe that will work, but I guess I am wondering why it doesnt work out of the box, it should be congruent with Dockerfile as far as I knowDevelop
Ah the Dockerfiles are not exactly the same, the one you posted is derived from node:10, and on circle circleci/node:10 is used. The difference is that the circleci node image drops to the circleci user and thus loses root privileges. This is described in the circleci/node docker hub page (point 4 under the heading "Why"). So an equivalent node-based image would look like this. And this dockerfile yields the same result as circle.Steeplebush
@Steeplebush thanks, you saved my day once again, that makes sense, feel free to add that as answer and I will acceptDevelop
S
7

As said the Dockerfile from the top is not fully identical with the one in the CircleCI-config. In the Dockerfile the base image is node which by default runs under the root user.

The circleci/node image on the on the other hand drops to the unprivileged circleci user. So a 100% identical Dockerfile based on the node image would look like this:

FROM node:10
RUN useradd -m circleci
USER circleci
RUN npm set unsafe-perm true
RUN npm install -g '@oresoftware/[email protected]'

And with this Dockerfile the same error appears as in CircleCI.

One solution would be to use sudo, the problem with this is that you would have to use sudo on every command which makes use of the node package you installed (since with sudo it would actually be installed in the /root directory which is not accessible with the circleci user).

I think the better option would be to install the package in the circleci home directory.

{
  "version": 2,
  "jobs": {
    "build": {
      "docker": [
        {
          "image": "circleci/node:10"
        }
      ],
      "steps": [
        {
          "run": "npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc"
        },
        {
          "run": "npm install -g --loglevel=warn '@oresoftware/[email protected]'"
        }
      ]
    }
  }
}

This way you don't have to sudo everytime you want to use the package.

Steeplebush answered 19/6, 2018 at 17:58 Comment(1)
Wtf indeed but the user is circleci? Maybe the env variable is set somewhere else?Steeplebush
Z
17

tldr - use the following prefix:

npm install --prefix=$HOME/.local --global serverless
  • Replace serverless with your own global package requirements.

Background:

  • After a bit of experimentation the above seems to be the cleanest way I've found.
  • CircleCI's current circleci/node:lts-buster image has the following on the path:

    /home/circleci/.local/bin:/home/circleci/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

  • I was unable to write to /home/circleci/bin due to blocked write permissions.

  • I was able to write to /home/circleci/.local/bin
  • Adding the --prefix=$HOME/.local option to npm install command means the global package is then installed into /home/circleci/.local/bin
  • After install, the command, in my case serverless, is executable.
Zephan answered 6/1, 2020 at 1:11 Comment(0)
S
7

As said the Dockerfile from the top is not fully identical with the one in the CircleCI-config. In the Dockerfile the base image is node which by default runs under the root user.

The circleci/node image on the on the other hand drops to the unprivileged circleci user. So a 100% identical Dockerfile based on the node image would look like this:

FROM node:10
RUN useradd -m circleci
USER circleci
RUN npm set unsafe-perm true
RUN npm install -g '@oresoftware/[email protected]'

And with this Dockerfile the same error appears as in CircleCI.

One solution would be to use sudo, the problem with this is that you would have to use sudo on every command which makes use of the node package you installed (since with sudo it would actually be installed in the /root directory which is not accessible with the circleci user).

I think the better option would be to install the package in the circleci home directory.

{
  "version": 2,
  "jobs": {
    "build": {
      "docker": [
        {
          "image": "circleci/node:10"
        }
      ],
      "steps": [
        {
          "run": "npm set prefix=/home/circleci/npm && echo 'export PATH=$HOME/circleci/npm/bin:$PATH' >> /home/circleci/.bashrc"
        },
        {
          "run": "npm install -g --loglevel=warn '@oresoftware/[email protected]'"
        }
      ]
    }
  }
}

This way you don't have to sudo everytime you want to use the package.

Steeplebush answered 19/6, 2018 at 17:58 Comment(1)
Wtf indeed but the user is circleci? Maybe the env variable is set somewhere else?Steeplebush
C
3

On CircleCI you'd need to use sudo. The default user is circleci which has passwordless sudo access.

Cutcliffe answered 19/6, 2018 at 16:52 Comment(0)
W
0

what worked for me was to add the following environment variable to the job:

environment:
  NPM_CONFIG_PREFIX: "~/.npm-global"

and modify the $PATH as follows in a step:

steps: 
  - echo 'export PATH=~/.npm-global/bin:$PATH' >> $BASH_ENV
  - npm install
  - npm install -g some-global-package
Wallacewallach answered 9/4, 2021 at 21:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.