Global npm, grunt etc with workflows and jobs

I’m trying to create a multi-job workflow where vendor dependencies (node/npm modules) are installed during one job and used in another (i.e. install grunt-cli in job 1, use grunt in job 2).

I’m persisting the workspace in job 1 and re-attaching it job 2.

grunt is failing in job 2 with

/bin/bash: line 1: grunt: command not found
Exited with code 127

If I call my grunt task in job 1 then it works fine, but I’d like to understand how to do this in seperate jobs as described.

Any help gratefully received.

references:
  workspace_root: &workspace_root
    /home/circleci/my-workspace

  attach_workspace: &attach_workspace
    attach_workspace:
      at: *workspace_root

defaults: &defaults
  # Default directory to run the steps in.
  working_directory: *workspace_root

  docker:
    - image: circleci/node:7.10.0
      environment:
        TZ: "/usr/share/zoneinfo/Australia/Brisbane"

  test:
    override:
      - "true"

version: 2
jobs:
  build:
    <<: *defaults
    steps:
        - checkout
        - run:
            name: Install npm
            command: 'sudo npm update -g npm@latest'
        - run:
            name: Install bower
            command: 'sudo npm install -g bower'
        - run:
            name: Install grunt-cli
            command: |
              sudo npm install -g grunt-cli
        - run:
            name: Install vendor dependancies
            command: |
              cd /home/circleci/my-workspace/website
              npm install
              bower install
        # - run:
        #     name: Compiling site with environment specific values
        #     command: |
        #       cd /home/circleci/my-workspace/website
        #       grunt staging
        - persist_to_workspace:
            root: *workspace_root
            paths:
            - .

  compile_code:
    <<: *defaults
    # Override working directory for this job
    working_directory: /home/circleci/my-workspace/website
    steps:
        - *attach_workspace
        - run:
            name: Compiling site with environment specific values
            command: |
              cd /home/circleci/my-workspace/website
              grunt staging

  deploy:
    <<: *defaults
    steps:
        - *attach_workspace
        - run:
            name: Deploying to environment
            command: echo 'Deploying to environment!'

workflows:
  version: 2
  continuous-delivery-workflow:
    jobs:
      - build
      - compile_code:
          requires:
            - build
      - approve_staging:
          type: approval
          requires:
            - build
            - compile_code
      - deploy:
          requires:
            - approve_staging

When you install node modules with the -g flag, they will be installed into /usr/local/lib/node_modules/ and symlinked to /usr/local/bin.

circleci@b7a85feafdb2:/$ sudo npm install -g grunt
/usr/local/bin/grunt -> /usr/local/lib/node_modules/grunt/bin/grunt
+ grunt@1.0.1
added 92 packages in 4.158s
circleci@b7a85feafdb2:/$ which grunt
/usr/local/bin/grunt
circleci@b7a85feafdb2:/$ ls -al /usr/local/bin/grunt
lrwxrwxrwx 1 root root 35 Jul 14 05:15 /usr/local/bin/grunt -> ../lib/node_modules/grunt/bin/grunt

Unless you save this directory as a workspace, then it will not exist during the next step of the workflow. I have not tried this before and you may run into some permissions issues if you try it since the circleci user does not own /usr/local/*

If you are always going to need some node modules like grunt I would suggest making your own image instead of using our node base, this way you can bake in all of your project specific dependencies and not have to run them each time.

Great, thanks for the info.

I had figured I was probably going about this wrong, using a local machine mindset (i.e. global installs for use across multiple projects). I now install these packages (still globally admittedly) but call the commands within the same job.

I wasn’t sure if I could persist a system level folder such as /usr/local/bin and admit I didn’t try but thanks for the pointers.

1 Like