How I make my Jenkins Slave Windows Service recognize the global modules installed by npm

I was trying to setup a Jenkins Slave on a Windows machine which will get protractor to run a series of functional tests that the team had written to test our AngularJS application.

Idea was that whenever that there was a code change being committed to the master branch on our GitHub repository, the Jenkins Slave will be called upon to checkout the latest codes and get protractor to run the functional tests base from a configuration.js file in the codebase.

One of the hurdles that I encountered was that my Jenkins Slave Windows Service was not able to recognize the executables from Node.js modules that were installed by npm globally, which in this case was the protractor executable.

This post describes the steps that I took in order to make Jenkins Slave Windows Service recognize the global modules installed by npm.

'protractor' is not recognized as an internal or external command, operable program or batch file.

After I had configured and assigned a build job to my Jenkins Slave Windows Service, I got the above message in the console output of my build job.

I encountered this message even though I was able to run protractor through a command prompt instance; Node.js 4.4.3 was installed and npm install -g protractor was ran through the command prompt.

Why can't my Jenkins recognize protractor as an internal or external command, operable program or batch file?

Finding out where npm had installed my global modules

The first diagnostic task that I performed was to check out where my npm program had installed my global Node.js modules.

By typing npm list -g, I was able to see the following output:

C:\Users\Administrator\AppData\Roaming\npm
└─┬ protractor@3.2.2
  ├── adm-zip@0.4.7
  ├─┬ glob@6.0.4
  │ ├─┬ inflight@1.0.4
  │ │ └── wrappy@1.0.1
  │ ├── inherits@2.0.1
  │ ├─┬ minimatch@3.0.0
  │ │ └─┬ brace-expansion@1.1.3
  │ │   ├── balanced-match@0.3.0
  │ │   └── concat-map@0.0.1
  │ ├─┬ once@1.3.3
  │ │ └── wrappy@1.0.1
  │ └── path-is-absolute@1.0.0
  ├─┬ jasmine@2.4.1
  │ ├── exit@0.1.2
  │ ├─┬ glob@3.2.11
  │ │ ├── inherits@2.0.1
  │ │ └─┬ minimatch@0.3.0
  │ │   ├── lru-cache@2.7.3
  │ │   └── sigmund@1.0.1
  │ └── jasmine-core@2.4.1
  ├── jasminewd2@0.0.8
  ├─┬ optimist@0.6.1
  │ ├── minimist@0.0.10
  │ └── wordwrap@0.0.3
  ├── q@1.4.1
  ├─┬ request@2.67.0
  │ ├── aws-sign2@0.6.0
  │ ├─┬ bl@1.0.3
  │ │ └─┬ readable-stream@2.0.6
  │ │   ├── core-util-is@1.0.2
  │ │   ├── inherits@2.0.1
  │ │   ├── isarray@1.0.0
  │ │   ├── process-nextick-args@1.0.6
  │ │   ├── string_decoder@0.10.31
  │ │   └── util-deprecate@1.0.2
  │ ├── caseless@0.11.0
  │ ├─┬ combined-stream@1.0.5
  │ │ └── delayed-stream@1.0.0
  │ ├── extend@3.0.0
  │ ├── forever-agent@0.6.1
  │ ├─┬ form-data@1.0.0-rc4
  │ │ └── async@1.5.2
  │ ├─┬ har-validator@2.0.6
  │ │ ├─┬ chalk@1.1.3
  │ │ │ ├── ansi-styles@2.2.1
  │ │ │ ├── escape-string-regexp@1.0.5
  │ │ │ ├─┬ has-ansi@2.0.0
  │ │ │ │ └── ansi-regex@2.0.0
  │ │ │ ├─┬ strip-ansi@3.0.1
  │ │ │ │ └── ansi-regex@2.0.0
  │ │ │ └── supports-color@2.0.0
  │ │ ├─┬ commander@2.9.0
  │ │ │ └── graceful-readlink@1.0.1
  │ │ ├─┬ is-my-json-valid@2.13.1
  │ │ │ ├── generate-function@2.0.0
  │ │ │ ├─┬ generate-object-property@1.2.0
  │ │ │ │ └── is-property@1.0.2
  │ │ │ ├── jsonpointer@2.0.0
  │ │ │ └── xtend@4.0.1
  │ │ └─┬ pinkie-promise@2.0.1
  │ │   └── pinkie@2.0.4
  │ ├─┬ hawk@3.1.3
  │ │ ├── boom@2.10.1
  │ │ ├── cryptiles@2.0.5
  │ │ ├── hoek@2.16.3
  │ │ └── sntp@1.0.9
  │ ├─┬ http-signature@1.1.1
  │ │ ├── assert-plus@0.2.0
  │ │ ├─┬ jsprim@1.2.2
  │ │ │ ├── extsprintf@1.0.2
  │ │ │ ├── json-schema@0.2.2
  │ │ │ └── verror@1.3.6
  │ │ └─┬ sshpk@1.7.4
  │ │   ├── asn1@0.2.3
  │ │   ├─┬ dashdash@1.13.0
  │ │   │ └── assert-plus@1.0.0
  │ │   ├── ecc-jsbn@0.1.1
  │ │   ├── jodid25519@1.0.2
  │ │   ├── jsbn@0.1.0
  │ │   └── tweetnacl@0.14.3
  │ ├── is-typedarray@1.0.0
  │ ├── isstream@0.1.2
  │ ├── json-stringify-safe@5.0.1
  │ ├─┬ mime-types@2.1.10
  │ │ └── mime-db@1.22.0
  │ ├── node-uuid@1.4.7
  │ ├── oauth-sign@0.8.1
  │ ├── qs@5.2.0
  │ ├── stringstream@0.0.5
  │ ├── tough-cookie@2.2.2
  │ └── tunnel-agent@0.4.2
  ├─┬ saucelabs@1.0.1
  │ └─┬ https-proxy-agent@1.0.0
  │   ├─┬ agent-base@2.0.1
  │   │ └── semver@5.0.3
  │   ├─┬ debug@2.2.0
  │   │ └── ms@0.7.1
  │   └── extend@3.0.0
  ├─┬ selenium-webdriver@2.52.0
  │ ├── adm-zip@0.4.4
  │ ├─┬ rimraf@2.5.2
  │ │ └─┬ glob@7.0.3
  │ │   ├─┬ inflight@1.0.4
  │ │   │ └── wrappy@1.0.1
  │ │   ├── inherits@2.0.1
  │ │   ├─┬ minimatch@3.0.0
  │ │   │ └─┬ brace-expansion@1.1.3
  │ │   │   ├── balanced-match@0.3.0
  │ │   │   └── concat-map@0.0.1
  │ │   ├─┬ once@1.3.3
  │ │   │ └── wrappy@1.0.1
  │ │   └── path-is-absolute@1.0.0
  │ ├── tmp@0.0.24
  │ ├─┬ ws@1.1.0
  │ │ ├── options@0.0.6
  │ │ └── ultron@1.0.2
  │ └─┬ xml2js@0.4.4
  │   ├── sax@0.6.1
  │   └── xmlbuilder@8.2.2
  └─┬ source-map-support@0.4.0
    └─┬ source-map@0.1.32
      └── amdefine@1.0.0

From this output, I got to know that my global Node.js modules were installed in the C:\Users\Administrator\AppData\Roaming\npm directory. And inside that directory, I saw the protractor.cmd file.

Making Jenkins Slave Windows Service recognize the global modules installed by npm

After knowing where my global Node.js modules reside in, I went on to check the PATH environment variable. I realised that the PATH environment variables in the 'User variables for Administrator' panel is set to the directory where my global Node.js modules reside in, but the PATH environment variables in the 'System Variables' section does not contain that value.

This explains why my Jenkins Slave Windows Service was not able to recognize the protractor executable even though I was able to run protractor via the local Administrator account.

Hence, in order to make my Jenkins Slave Windows Service recognize the the global modules (including my protractor executable) installed by npm, I included C:\Users\Administrator\AppData\Roaming\npm in the Path environment variable in the 'System Variables' section.

With that, I managed to achieve continuous functional testing of my AngularJS application.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.