npm cross platform gradle builds

This is a quick and dirty one, mostly so I dont forget to write down something painful I had to do recently.

If you use node npm in a gradle build process, which is very likely in java projects with react or angular frontends, if you're like me you may think doing something like this will work fine:

    task compileMyFrontend(type: Exec) {
        // or whatever npm script...
        commandLine "npm","run", "build"
    }

You code away on your macbook and have no problems! Fast forward to having to be developing on windows, you quickly realize it will fail to find NPM which is odd. When I initially setup build gradles that needed to execute npm, i ran into the same problem, and found many stackoverflows recommending doing the following (which I did, being windows based at work):

    task compileMyFrontend(type: Exec) {
        // or whatever npm script...
        executable "cmd"
        args "/c npm install && run build"
    }

There is a better way!

It is apparently caused by the Process launching code in JVM needing the FULL executable file name. windows command prompt and powershell are smart enough to know npm's extension and execute it if possible, which they will do for anything that ends in .exe,.bat,.cmd, probably others..

So, what exactly is npm on windows then? it's npm.cmd on node7, you can check yourself with:

> where npm

so to get gradle to work, you need to execute

commandLine 'npm.cmd','args..'.

Great! problem solved, time to drink beer right? Well doing that will break *nix builds. As one would expect, we need to add some glue to the build. Conditionally getting the build os is not built into gradle so you have to import some stuff, but any gradle3+ should work. tldr add this to top of your build.gradle:

    import org.apache.tools.ant.taskdefs.condition.Os

    def iswindows = Os.isFamily(Os.FAMILY_WINDOWS)
    project.ext."npmExe" = swindows ? "npm.cmd" : "npm"

I arbitrary called the variable "npmExe", not "npm" because that looked slightly confusing. Now any npm tasks just need to use that variable directly in their command line, eg:

    task compileMyFrontend(type: Exec) {
        // or whatever npm script...
        commandLine project.ext."npmExe","run", "build"
    }

This will work on windows and mac/linux, etc. Bleck

..Back to Dexter Haslem home