42

I'm trying to execute grunt tasks from within maven without needing to install Node.js or anything. This is because I wan't my artifact to be packaged by Jenkins and I can't install Node.js on that machine.

I know that it's easy with npm and a few commands to get it working, but I also think that it should be easy to integrate with maven, the problem is that I don't know where to start since I'm new to npm.

Gastón Fournier
  • 956
  • 1
  • 9
  • 25

9 Answers9

30

Yes, using the frontend-maven-plugin, you can compile Grunt projects via Maven (found via the NodeJS mailing list).

As the documentation points out, the plugin has the following features:

  • Let you keep your frontend and backend builds as separate as possible, by reducing the amount of interaction between them to the bare minimum; using only 1 plugin.
  • Let you use Node.js and its libraries in your build process without installing Node/NPM globally for your build system
  • Let you ensure that the version of Node and NPM being run is the same in every build environment

I've walked through the code and it's fairly simple. Thank goodness someone finally put this together; it's an elegant solution. The repository includes an example that uses a regular Gruntfile.js to invoke jshint analysis.

blong
  • 2,815
  • 8
  • 44
  • 110
  • This plugin is great! Quoting the README : _Not meant to replace the developer version of Node - frontend developers will still install Node on their laptops, but backend developers can run a clean build without even installing Node on their computer_ – olivieradam666 Feb 19 '14 at 15:10
  • 1
    I use this on multiple projects and it is awesome. One thing to be aware of it is will download node for the system it is being run on, so if you have a different OS on your build server, you'll need to make sure that is the version you have checked into version control, your local version (for me OSX) will have to be maintained local to your project. – am80l Sep 18 '14 at 21:26
  • 2
    Another alternative is to .gitignore the "node" folder (where the executables end up) so each architecture gets its own. – Eirik Sletteberg Sep 24 '14 at 22:04
15

UPDATE 2014-09-19: This is no longer the most accurate answer - please take a look at some of the other answers below. It was accurate at the time when I answered the question, but there seems to have been a good deal of progress in this area since then.

I'm afraid you're out of luck. Grunt is built using node and needs to be installed using npm. You might be able to copy an existing installation of Grunt from another machine if you don't want to use npm, but will still use the grunt executable and all of its dependencies on your build server.

In addition to that, many of the Grunt tasks are implemented as Node.js modules, and you will have to install them as well. Again, you might be able to copy them from another server, where you've done the Node.js/Grunt installation, but at one point, you have to do it.

For running Grunt from Maven, your best bet is to use the Maven exec plugin and then execute the grunt executable from there.

As an alternative, there are several Maven plugins that allow you to do things similar to Grunt in a Java-based fashion. They require additional configuration not compatible with Grunt, so YMMV. One that I've used in the past is http://code.google.com/p/wro4j/, which comes with a Maven plugin as well: http://code.google.com/p/wro4j/wiki/MavenPlugin

Any particular reason why you can't install Node.js on your build server?

nwinkler
  • 52,665
  • 21
  • 154
  • 168
  • Thanks @nwinkler! Very complete answer. There is no particular reason, I found it a little awkward to install extra build systems when I already have one that can handle the requirements of my project. Before trying to use grunt, I was using compass with maven to build sass: https://gist.github.com/mkristian/1671207. Then I saw Grunt that can handle that and more. Since with JRuby I was able to install the ruby gems inside a target directory and run it from there, I thought that something similar could be achieved with Node.js and Grunt. It seems that it can, but there's no copy&paste way yet – Gastón Fournier Feb 19 '13 at 12:51
  • For running Grunt from Maven you can use [grunt-maven-plugin](https://github.com/allegro/grunt-maven-plugin). See my answer below. – pbetkier Oct 25 '13 at 17:46
  • I'd recommend the [`frontend-maven-plugin`](https://github.com/eirslett/frontend-maven-plugin), see my answer below. – blong May 01 '14 at 14:56
11

You can use grunt-maven-plugin. It allows you to easily integrate Grunt tasks into Maven build process. No dirty hacks.

This is what I use in my current project and it works just perfect.

pbetkier
  • 1,657
  • 1
  • 13
  • 10
  • Have you tried integrating something like the [nodejs-maven-plugin](https://github.com/skwakman/nodejs-maven-plugin) to abstract the NodeJS runtime installation? – blong Oct 16 '13 at 05:08
  • @b.long no, we didn't like the idea of having an artifact for nodejs coming from maven central managed by somebody. This way we wouldn't be able to control nodejs version, like having the latest version when we like it. – pbetkier Oct 16 '13 at 12:17
  • That's understandable, certainly a reasonable concern. Thanks for the update :) – blong Oct 16 '13 at 14:20
  • not sure if this plugin fully works, or something has broken recently but when we use it, the copy-resources goal doesn't run and it doesn't use a different gruntBuildDirectory when set – wenic Nov 15 '13 at 17:26
  • @pbetkier have you any example configuration to share? I am struggling with grunt not finding my js files. – pethel Nov 19 '13 at 12:54
  • @rury You mean create-resources goal? You can post an issue on the plugin's github page. The author responds pretty quickly. My configuration is very similar to the one from the github page. – pbetkier Nov 19 '13 at 15:13
  • @rury Sorry for the slow reply. The plugin's been refactored quite a bit in the last month, maybe try it again? I'd agree the plugin author is pretty friendly & responsive. – blong Dec 27 '13 at 21:49
  • This is a good plugin but don't use it when your IDE is Eclipse. Eclipse has its own virtual filesystem layer and will not update javascript files in tomcat when they are modified from outside Eclipse, so you can't use the live development mode. – Bastian Voigt May 15 '14 at 06:44
  • @pbetkier I did using grunt-maven-plugin but now I cant build using "grunt build" by pointing to the Gruntfile.js file. Im getting following error. "Native thread-sleep not available. This will result in much slower performance, but it will still work. You should re-install spawn-sync or upgrade to the lastest version of node if possible." any Idea? – Sameera Thilakasiri Nov 02 '15 at 10:06
10

Finally I ended up with this (which is close enough but doesn't solve the problem):

<plugin>
<groupId>org.mule.tools.javascript</groupId>
<artifactId>npm-maven-plugin</artifactId>
<version>1.0</version>
<executions>
    <execution>
        <phase>generate-resources</phase>
            <goals>
                <goal>fetch-modules</goal>
            </goals>
            <configuration>
                <packages>
                    <package>grunt-cli:0.1.6</package>
                </packages>
            </configuration>
        </execution>
    </executions>
</plugin>

that installs locally the grunt-cli, but if I don't have installed node.js it's worthless. Although I try to install node.js locally there's the need to have installed python, g++ and make. So I'll go with the KISS solution: install grunt in the build server.

References:
https://github.com/mulesoft/npm-maven-plugin
https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
https://github.com/mcheely/requirejs-maven-plugin

Gastón Fournier
  • 956
  • 1
  • 9
  • 25
6

You might want to checkout http://jhipster.github.io/ : it's a Yeoman generator, that generates an application which has Maven, Grunt and Bower all working together.

It's a bit like your third option, but everything is configured for you, which isn't that easy. It's also generating the basic AngularJS and Java REST services for you

Er KK Chopra
  • 1,834
  • 8
  • 31
  • 55
3

This is a full copy/paste solution which work in 2017 using frontend-maven-plugin for front build, and maven-war-plugin to build the war.

What it does ? install npm, bower grunt,and everything you need, then run npm install, bower install and finally grunt build.

You can remove/add replace the steps you want, for me it's a full 30 sec install/build library and project.

<dependencies>
  ...
</dependencies>

<dependencyManagement>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.github.eirslett/frontend-maven-plugin -->
        <dependency>
            <groupId>com.github.eirslett</groupId>
            <artifactId>frontend-maven-plugin</artifactId>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <warSourceDirectory>src/main/webapp/YourFrontJsFolder/dist</warSourceDirectory>
                    <warName>YouWarName</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <warSourceExcludes>node_modules/**</warSourceExcludes>
                    <includeScope>system</includeScope>
                    <webResources>
                        <resource>
                            <directory>WebContent/WEB-INF</directory>
                            <targetPath>WEB-INF</targetPath>
                            <includes>
                                <include>**/*.jar</include>
                                <include>**/*.jsp</include>
                            </includes>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <encoding>Cp1252</encoding>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>

    <finalName>YourAppName</finalName>
</build>

<profiles>
    <profile>
        <id>release</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>com.github.eirslett</groupId>
                    <artifactId>frontend-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <!-- optional: you don't really need execution ids, but it looks 
                                nice in your build log. -->
                            <id>install node and npm</id>
                            <goals>
                                <goal>install-node-and-npm</goal>
                            </goals>
                            <!-- optional: default phase is "generate-resources" -->
                            <phase>generate-resources</phase>

                            <configuration>
                                <nodeVersion>v7.6.0</nodeVersion>
                            </configuration>
                        </execution>

                        <execution>
                            <id>npm install</id>
                            <goals>
                                <goal>npm</goal>
                            </goals>

                            <!-- optional: default phase is "generate-resources" -->
                            <phase>generate-resources</phase>

                            <configuration>
                                <arguments>install</arguments>
                            </configuration>
                        </execution>

                        <execution>
                            <id>bower install</id>
                            <goals>
                                <goal>bower</goal>
                            </goals>

                            <configuration>
                                <!-- optional: The default argument is actually "install", so unless 
                                    you need to run some other bower command, you can remove this whole <configuration> 
                                    section. -->
                                <arguments>install</arguments>
                            </configuration>
                        </execution>

                        <execution>
                            <id>grunt build</id>
                            <goals>
                                <goal>grunt</goal>
                            </goals>

                            <!-- optional: the default phase is "generate-resources" -->
                            <phase>generate-resources</phase>

                            <configuration>
                                <!-- optional: if not specified, it will run Grunt's default task 
                                    (and you can remove this whole <configuration> section.) -->
                                <arguments>build</arguments>
                            </configuration>
                        </execution>
                    </executions>

                    <configuration>
                        <installDirectory>target</installDirectory>
                        <workingDirectory>src/main/webapp/YourFrontJsFolder</workingDirectory>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>debug</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>IDE</id>
        <activation>
            <property>
                <name>m2e.version</name>
            </property>
        </activation>
        <build>
            <!-- Put the IDE's build output in a folder other than target, so that 
                IDE builds don't interact with Maven builds -->
            <directory>target-ide</directory>
        </build>
    </profile>
</profiles>

Then you can Run as -> Maven build ..., with goal clean install and profile release

amdev
  • 3,010
  • 3
  • 35
  • 47
1

The first problem is that Maven is Java, but Grunt.js runs on the Node.js runtime. The easiest integration I ever achieved between the two involved the maven-exec-plugin. The maven-exec-plugin is capable of executing .sh/.bat/.cmd scripts, whichever are native to the OS you are using. So during a Maven build I would have the maven-exec-plugin execute a script named optimize-js.sh, for example, which would simply do something like “grunt release –force”, or whatever. The scripts can be made to do whatever. The important thing is to configure the maven-exec-plugin to execute them in the correct working directory. Of course, “grunt” and “node” need to be executable from the command-line.

jdobry
  • 1,041
  • 6
  • 17
1

If the problem is installing NodeJS on the Jenkins machine then you can use the NodeJS Jenkins plugin.

https://wiki.jenkins-ci.org/display/JENKINS/NodeJS+Plugin

We're not using it with Maven (yet) but we've got grunt running.

jamie
  • 2,963
  • 1
  • 26
  • 27
0

Can be done with exec-maven-plugin.

Define a script and dependency to grunt-cli in your package.json:

...
  "scripts": {
    "build": "./node_modules/.bin/grunt install"
  },
  "devDependencies": {
  "grunt-cli": "^1.2.0",
...

In your pom, add the commands to run:

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>X.Y.Z</version>
            <executions>
                <execution>
                    <id>exec-npm-install</id>
                    <phase>generate-sources</phase>
                    <configuration>
                        <workingDirectory>${project.basedir}</workingDirectory>
                        <executable>npm</executable>
                        <arguments>
                            <argument>install</argument>
                        </arguments>
                    </configuration>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                </execution>
                <execution>
                    <id>exec-grunt-install</id>
                    <phase>generate-sources</phase>
                    <configuration>
                        <workingDirectory>${project.basedir}</workingDirectory>
                        <executable>npm</executable>
                        <arguments>
                            <argument>run</argument>
                            <argument>build</argument>
                        </arguments>
                    </configuration>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

It will now run on mvn package

Tomas Bjerre
  • 3,270
  • 22
  • 27