Maven: Skinning Generated Site

PROBLEM

The default Maven generated site looks like web pages created in the 80s:-

SOLUTION

The good news is Maven allows us to change the skin.

To use one of these pre-defined skins, create site.xml at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── site.xml

In site.xml, enter the following:-

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/DECORATION/1.6.0"
         xsi:schemaLocation="http://maven.apache.org/DECORATION/1.6.0
         http://maven.apache.org/xsd/decoration-1.6.0.xsd">
    <bannerLeft>
        <name>${project.name}</name>
        <href>${project.url}</href>
    </bannerLeft>

    <publishDate position="right" format="MMMM dd, yyyy"/>
    <version/>
    <poweredBy>
        <logo img="#" alt=""/>
    </poweredBy>

    <skin>
        <groupId>org.apache.maven.skins</groupId>
        <artifactId>maven-fluido-skin</artifactId>
        <version>1.4</version>
    </skin>

    <body>
        <menu ref="reports"/>
        <footer/>
    </body>
</project>

In the above example, we use Maven Fluido Skin.

In Maven 3, site:attach-descriptor has been removed from the built-in lifecycle bindings, so we need to explicitly define attach-descriptor goal in Maven Site Plugin to pick up src/site/site.xml.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-site-plugin</artifactId>
    <version>3.4</version>
    <executions>
        <execution>
            <id>attach-descriptor</id>
            <goals>
                <goal>attach-descriptor</goal>
            </goals>
        </execution>
    </executions>
</plugin>

When you run mvn clean site, oh hey, welcome to 21st century!

JaCoCo Web Report Not Rendering Properly in GitHub Pages

PROBLEM

When pushing JaCoCo web report to GitHub’s gh-pages branch, it does not render properly on the web. For example:-

The GitHub pages are powered by Jekyll. By default, Jekyll does not allow directories or files that begin with a dot, pound sign, tilde or underscore.

Since JaCoCo places all the image and CSS files in a directory called .resources, this directory will not be pushed to GitHub’s gh-pages branch.

SOLUTION UPDATE: 2015-07-23

GitHub Site Plugin has a noJekyll parameter that will create the .nojekyll file at the root path if it is set to true.

<plugin>
    <groupId>com.github.github</groupId>
    <artifactId>site-maven-plugin</artifactId>
    <version>${site-maven-plugin.version}</version>
    <configuration>
        <message>Creating site for ${project.version}</message>
        <server>github</server>
        <noJekyll>true</noJekyll>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>site</goal>
            </goals>
            <phase>site</phase>
        </execution>
    </executions>
</plugin>

SOLUTION 1: Disable Jekyll

One simple fix is to completely disable Jekyll by creating an empty file called .nojekyll at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── resources
│   │       └── .nojekyll

SOLUTION 2: Configure Jekyll

While disabling Jekyll works, it may 1) seem rather risky since there maybe other hidden directories/files and 2) possibly send too much garbage to GitHub’s gh-pages branch.

To fix this, we will instruct Jekyll to allow JaCoCo’s .resources directory through by creating a file called _config.yml at this location:-

myproject
├── pom.xml
├── src
│   ├── main
│   │   └── java
│   │       └── ...
│   ├── site
│   │   └── resources
│   │       └── _config.yml

In _config.yml, enter the following:-

# Allow JaCoCo's directory to bypass GitHub's Jekyll-powered pages 
# so that the web report renders properly
include: ['.resources']

OUTCOME

Either solution will produce a nicely rendered JaCoCo web report in GitHub pages.

Maven: Deploying Generated Site to GitHub

INTRO

GitHub provides an incredible feature that allows us to easily push Maven generated site to our project’s GitHub repository.

Here’s how to do it…

STEP 1: Define GitHub credential

Go to ~/.m2/settings.xml and add your GitHub username and password:-

<settings ...>
	<servers>
		<server>
			<id>github</id>
			<username>USERNAME</username>
			<password>PASSWORD</password>
		</server>
	</servers>
</settings>

STEP 2: Define GitHub’s site-maven-plugin

GitHub provides its own site-maven-plugin that can be used to deploy a Maven generated site to a gh-pages branch so that it can be served statically as a GitHub Project Page.

In pom.xml, add the following plugin configuration:-

<project ...>
    ...
    <build>
        <plugins>
            ...
            <plugin>
                <groupId>com.github.github</groupId>
                <artifactId>site-maven-plugin</artifactId>
                <version>0.11</version>
                <configuration>
                    <message>Creating site for ${project.version}</message>
                    <server>github</server>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>site</goal>
                        </goals>
                        <phase>site</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    ...
</project>

STEP 3: Define project URL

In pom.xml, define <url> tag that points to the project’s GitHub repository.

<project ...>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <url>https://github.com/USERNAME/PROJECT-NAME</url>
	...
</project>

If this is not defined, Maven will throw the following error when executing mvn clean site:-

Failed to execute goal com.github.github:site-maven-plugin:0.11:site 
(default) on project PROJECT-NAME: No GitHub repository (owner and 
name) configured -> [Help 1]

STEP 4: Generate Maven site and push to GitHub

Finally, run the following command: mvn clean site.

The generated site can be viewed from https://USERNAME.github.io/PROJECT-NAME/ .

That’s it…. very simple.

Jenkins: Getting Karma Generated Test Results to Appear in Maven Project Job

PROBLEM

Jenkins, for some reason, does not pick up Karma generated JUnit test reports even though they are created in the right directory… and apparently, it is a known problem. While Freestyle project job allows us to manually publish these JUnit reports, my intention is to rely on Maven project job to do the same thing.

PREREQUISITES

ENSURING KARMA GENERATED JUNIT REPORT SHOWS UP IN JENKINS…

Instead of manually running karma start command in Jenkins, we will rely on maven-karma-plugin to do this for us. The key here is to specify the correct <phase> so that Jenkins picks up and presents the generated report.

pom.xml

<build>
    <plugins>
        <plugin>
            <groupId>com.kelveden</groupId>
            <artifactId>maven-karma-plugin</artifactId>
            <version>1.6</version>
            <executions>
                <execution>
                    <phase>process-test-classes</phase>
                    <goals>
                        <goal>start</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <karmaExecutable>${basedir}/node_modules/karma/bin/karma</karmaExecutable>
                <configFile>src/test/resources/karma.jenkins.conf.js</configFile>
            </configuration>
        </plugin>
    </plugins>
</build>

CONFIGURING JENKINS

Since Karma test runner requires NodeJS, we will install NodeJS Plugin in Jenkins. This allows us to automatically install NodeJS from Jenkins.

Once installed, go to Manage Jenkins -> Configure System and go to NodeJS section:-

Although this section allows us to specify npm packages to install, I’m having trouble installing certain packages, such as karma-phantomjs-launcher. The phantomJS package invokes node install.js during the installation, however, the node command isn’t available in PATH environment variable at this point. Thus, the installation will always fail. So, the npm packages will be configured at the job level in the next step. Further, it is not a good idea to install Karma-related plugins globally here because every Jenkins job may use slightly different versions of the same plugin (think Spring or Hibernate versions in every job’s pom.xml).

Next, create a Maven project job and configure it.

Configuring Build Environment, Pre Steps and Build

We exposed NodeJS to PATH environment variable so that we can install phantomJS package.

Next, we created a pre-build step to execute npm install, which reads package.json from the job and installs the needed dependencies within the job.

Finally, we will want Maven to invoke test goal so that it runs both Java tests and Karma test runner.

Configuring Coverage Report

We provided the Karma generated coverage report XML file.

OUTCOME

When we run Build Now in Jenkins, both unit test and coverage reports will display both Java and JavaScript execution results.

Since we ran npm install as a pre-build step, the job will now have node_modules directory.

Maven: Plugin Execution Not Covered by Lifecycle Configuration

PROBLEM

When running a Maven project in Eclipse or Spring Tool Suite (STS), we get an exception that is similar to this:-

Plugin execution not covered by lifecycle configuration:
org.jacoco:jacoco-maven-plugin:0.6.4.201312101107:prepare-agent (execution: pre-unit-test, phase: initialize)

SOLUTION

This problem stems from m2eclipse (m2e) plugin, which provides Maven support in Eclipse-based IDEs.

STEP 1: Suppress the Error Message

The simplest solution is to instruct m2e to silently ignore the plugin execution by adding the following configuration in the project root pom.xml:-

IMPORTANT: Please make sure to enter the correct plugin’s groupId, artifactId, versionRange and goals that causes the problem in the first place.

<pluginManagement>
    <plugins>
        <plugin>
            <groupId>org.eclipse.m2e</groupId>
            <artifactId>lifecycle-mapping</artifactId>
            <version>1.0.0</version>
            <configuration>
                <lifecycleMappingMetadata>
                    <pluginExecutions>
                        <pluginExecution>
                            <pluginExecutionFilter>
                                <groupId>org.jacoco</groupId>
                                <artifactId>jacoco-maven-plugin</artifactId>
                                <versionRange>[0.0.1,)</versionRange>
                                <goals>
                                    <goal>prepare-agent</goal>
                                </goals>
                            </pluginExecutionFilter>
                            <action>
                                <ignore />
                            </action>
                        </pluginExecution>
                    </pluginExecutions>
                </lifecycleMappingMetadata>
            </configuration>
        </plugin>
    </plugins>
</pluginManagement>

STEP 2: Learn More About This Problem

Eclipse provides a great documentation regarding this issue. You can learn the “what”, “why” and “how” at http://wiki.eclipse.org/M2E_plugin_execution_not_covered

STEP 3: A More Elegant Fix

The problem with the above fix is we clutter our pristine Maven project configuration with an IDE plugin specific configuration that has nothing to do with the project in the first place.

So, the best solution is ditch Eclipse-based IDEs and switch to IntelliJ IDEA… because you either have idea, or don’t.