View on GitHub

Gradle Manipulator @ Red Hat

Standardize Your Builds

Groovy Script Injection

Overview

The tool offers the ability to run arbitrary groovy scripts on the sources prior to running the build. This allows GME to be extensible by the user and to carry out non-generic modifications.

Configuration

If the property -DgroovyScripts=<value>,.... is set, it will load the remote Groovy script file.

The argument should a comma separated list of HTTP / HTTPS URLs.

Groovy Scripts

Each groovy script will be run on the execution root (i.e. where Gradle is invoked).

Each script must use the following annotations:

import org.commonjava.maven.ext.core.groovy.GMEBaseScript
import org.commonjava.maven.ext.core.groovy.InvocationPoint
import org.commonjava.maven.ext.core.groovy.InvocationStage
import org.jboss.gm.common.groovy.BaseScript

@InvocationPoint(invocationPoint = InvocationStage.FIRST)
@GMEBaseScript BaseScript gmeScript

where InvocationStage may be FIRST, LAST or BOTH. This denotes whether the script is ran before all other manipulators, after or both. The script therefore encodes how and when it is run.

NOTE : Prior to GME 1.4 the annotations used the simpler BaseScript annotation e.g. @BaseScript GMEBaseScript gmeScript

API

The following API is made available:

Method Description
Project getProject() Return the root Project.
Only valid in InvocationStage.LAST
ManipulationModel getModel() Return the current ManipulationModel.
Only valid in InvocationStage.LAST
NOTE : From version 1.4 the following extra API is available:
Properties getUserProperties() Get the user properties.
File getBaseDir() Get the working directory (the execution root).
InvocationStage getInvocationStage() Return the current stage of the groovy manipulation.
NOTE : From version 2.1 the following extra API is available:
Logger getLogger() Get the Logger.
NOTE : From version 2.7 the following extra API is available:
Translator getRESTAPI() Get the REST Version Translator.

When running as FIRST Gradle has not parsed and created the Project which means the getModel/getProject calls are not available. However it is possible to ammend the Gradle scripts directly on disk which will then be read as part of the following alignment process.

The API can then be invoked by e.g.

gmeScript.getProject()

A typical groovy script that alters a JSON file on disk might be:

import org.commonjava.maven.ext.core.groovy.GMEBaseScript
import org.commonjava.maven.ext.core.groovy.InvocationPoint
import org.commonjava.maven.ext.core.groovy.InvocationStage
import org.jboss.gm.common.groovy.BaseScript
import org.apache.commons.lang.StringUtils
import org.jboss.gm.common.model.ManipulationModel
import org.commonjava.maven.atlas.ident.ref.SimpleProjectRef

@InvocationPoint(invocationPoint = InvocationStage.LAST)
@GMEBaseScript BaseScript gmeScript

println "Running Groovy script on " + gmeScript.getProject()
println "\tgroovy found new version is " + gmeScript.getModel().getVersion()

String newVersion = gmeScript.getModel().getVersion()
File information = new File(gmeScript.getProject().getRootDir(), "gradle/base-information.gradle")

def newContent = information.text.replaceAll( "(new HibernateVersion[(]\\s')(.*)(',\\sproject\\s[)])", "\$1$newVersion\$3")
information.text = newContent

def newJpaVersion = gmeScript.getModel().getAllAlignedDependencies().values().find {it.asProjectRef() == new SimpleProjectRef("javax.persistence", "javax.persistence-api")}.getVersionString()

newContent = information.text.replaceAll( "(new JpaVersion[(]')(.*)('[)])", "\$1$newJpaVersion\$3")
information.text = newContent
newContent = information.text.replaceAll( "version \\+ \"\\.0\"", "version")
information.text = newContent

It is possible to use the Translator API to call onto the Dependency Anlalyser to make adjustments beyond what GME already provides e.g.

import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef
import org.commonjava.maven.atlas.ident.ref.SimpleProjectRef
import org.commonjava.maven.atlas.ident.ref.SimpleProjectVersionRef
import org.commonjava.maven.ext.io.rest.Translator
...
Translator translator = gmeScript.getRESTAPI();
ProjectVersionRef pvr = SimpleProjectVersionRef.parse("org.hibernate:hibernate-core:5.3.7.Final")
Map<ProjectVersionRef, String> result = translator.translateVersions(Collections.singletonList(pvr))
gmeScript.getLogger().warn("Alignment result is {}", result)
File target = gmeScript.getProject().getBuildFile()
newContent = target.text.replaceFirst("classpath \"org.hibernate:hibernate-core:5.3.7.Final",
    "classpath \"org.hibernate:hibernate-core:" + result.get(pvr))
target.text = newContent

Developing Groovy Scripts

To make it easier to develop scripts for both GME (this project) and PME an example project has been setup. The manipulator-groovy-examples provides a framework to develop and test such scripts.

Note: To debug Groovy scripts, while it is possible to use a debugger on the CLI for those scripts with invocation point FIRST, for those scripts with LAST it is not possible to run the CLI and debug on the Groovy script. Instead run Gradle directly and invoke the plugin like

gradle
--no-daemon
--info
--stacktrace
--init-script=<....analyzer-init.gradle>
...
-DgroovyScripts=file://...../script.groovy
-Dorg.gradle.debug=true

The are two crucial aspects - you need to activate Groovy debugging via org.gradle.debug=true and the source must be added to your IDE so it can see it.