Tuesday, 29 December 2015

How to migrate Parent pom from maven to gradle

Convert Maven parent/super pom to Gradle init file


When you think of moving to gradle for some reason from maven, You need to take care of lot of stuff around it, You can find lot of tools and help to convert maven pom.xml to gradle build.gradle But one of the most important task as an engineer or architect would be to convert your super pom (or parent pom) in to gradle init scripts.

Gradle init script provides same functionality as Maven super/parent pom. The basic difference is that you can call init script

  • Run time
  • As many as of them
This gives us flexibility to change the init script on run time but doubt of not tracking the changes.


Lets take parent pom section one by one to convert it to init script. (Java Projects)


1) Versioning


You do not need version it separately as you will use it at run time.

2) Repository


You need to define the repositories (local, remote) from where you want artifacts to be downloaded.


repositories {
            maven {
                name "release"
                url "http://maven.com:8080/artifactory/release"
            }
            maven {
                name "snapshot"
                url "http://maven.com:8080/artifactory/snapshot"
            }
            mavenLocal()

        }

These repositories are for dependency download during build time.


3) Distribution management


Here we will use maven plugin for uploading the artifact.

allprojects {
afterEvaluate { project ->
   project.apply plugin: 'java'
   project.apply plugin: 'maven'
 uploadArchives {
repositories {
 mavenDeployer {
pom.packaging = project.packaging
pom.groupId = project.group
pom.version = project.version
pom.artifactId = project.artifactid
if(project.version.endsWith('-SNAPSHOT')) {
 repository(url: 'http://maven.<yourdomain>.com:8080/libs-snapshot-local') {
   authentication(userName: RepoUsernameProp, password: repoPasswordProp);
 }
}
else {
 repository(url: 'http://maven<yourdonaim>.com:8080/libs-release-local') {
                            authentication(userName: RepoUsernameProp, password: repoPasswordProp);
                          }
                        }
 }
}
 }
}
}

Local file for password and username.
Note - RepoUsernameProp and repoPasswordProp are the variable to pass the username an password for your maven repo. This can be set in gradle.properties file under ~/.gradle .

File content would look like

RepoUsernameProp=abc

repoPasswordProp=abc@123


4) Init script Block


Init script can start the following block (You may keep maven central here if you are are specifying repository section)

initscript {
        repositories {
                mavenCentral()
        }

}

5) Profiling

If you want to add profile, You can do that, Here the configuration.
allprojects {
 afterEvaluate { project ->
        if (project.hasProperty("profile-name1")) {
           project.apply plugin: 'java'
<Do what ever you want to do>
    }
        if (project.hasProperty("profil-name2")) {
         project.apply plugin: 'java'
<Do what ever you want to do>
    }
  }

}

You can set profile depending on your requirements, 
Hint - You can set analysis in one profile and another profile without any checks (as mention below   section).

6) Additional Checks


Following can go in to your profile section as per your need.

    a) jacoco

   project.apply plugin: "jacoco"
   jacoco {
  toolVersion = "0.7.1.201405082137" 
   }
   jacocoTestReport {
  description = "Generate Jacoco coverage reports after running tests."
  sourceDirectories = files('src/java')
  classDirectories = files('build/classes/main')
    reports {
   xml.enabled true
   csv.enabled false
   html.enabled true
        }
   }
   jacocoTestReport.dependsOn test

This to generate the jacoco coverage from your unit test.

b) checkstyle

   project.apply plugin: 'checkstyle'
          checkstyle {
  ignoreFailures = true
  configFile = file("/jenkins/checks.xml")
   }
Coding style compatibility with the checks.xml file. Make sure checks.xml has right checkstyle configuration.

    c) findbugs

   project.apply plugin: 'findbugs'
   findbugs {
  ignoreFailures = true
  effort = "max"
  reportLevel = "high"
   }
Findbugs integration

    d) Adding checks dependency


check.dependsOn checkstyleMain, pmdMain, findbugsMain, jacocoTestReport

7) Versioning in gradle.properties

Now you need configuration where you can mention about the artifactID, grouId, version etc.
You need to create a file gradle.properties in your repository(project). This file contains the information about your project metadata like groupid, version etc.. Here are the entries (values are just example).

version=0.1.5-SNAPSHOT
group=com.org
packaging=war
artifactid=wine

8) Release Plugin


Add following entry in your build.gradle under buildscripts section to support release plugin

buildscript {
    repositories {
        jcenter()
    }

}
plugins {

   id 'net.researchgate.release' version '2.2.1' }
apply plugin: 'maven'
afterReleaseBuild.dependsOn { allprojects.uploadArchives }

The researchgate release plugin is equal to maven release plugin and perform same actions. For more information please refer.


Note - > Make sure you have  "allprojects.uploadArchives" in curly braces {}, without braces your build may pass but it will not upload artifacts to maven repository.

9) Calling your build

gradle clean build upload <whatever you goal> -P<profile_name> check

whitebox analysis will perform checkstyle, findbugs, pmd, code coverage jacoco etc.
Coverage-analysis will perform codecoverage jacoco.


10) Calling your Release Build

gradle clean <goal> release -Prelease.useAutomaticVersion=true -I /jenkins/<InitScript>


  • Either you can keep the init script in your ~/.gradle folder to be picked or call it via -I option.
  • release.useAutomaticVersion=true is to update the version automatically without user input.

Lets take parent pom section one by one to convert it to init script. (Android Projects)
Android projects are little different then Java projects. You can not use java and android project together and hence you can not have same init script for both projects.
Here are the changes which needs to be done for android projects.

(I am going to list of only differences).


1) Adding Dependency


Add following in your module level build.gradle (For multi module projects).


check.doLast {
    project.tasks.getByName("findbugs").execute() 
    project.tasks.getByName("checkstyle").execute()
    project.tasks.getByName("jacocoTestReport").execute() 
}
Project is android app and using versionCode and versionName for google play and want to align the with how google play handles the version code
  • The version code needs to increase for each release.
  • You can only upload one APK per version code, so it needs to be unique for each release.
  • Sometimes you want to use Release Candidates for alpha and beta testing, so these needs to have unique version codes.
  • The version codes of your Release Candidates must however be lesser than the version code of the final release.
  • You also want to be able to release point/patch releases of older apps, with lesser version codes than the latest version.
The solution to all of this is to adapt a standardized versioning schema. The chosen schema is therefore a simplified version of the Maven versioning schema. This schema uses Major, Minor and Patch versions, together with -SNAPSHOT and -RC suffixes. Versions will look like “1.0.1”, “1.2.0-SNAPSHOT”, “2.4.0-RC1″ and so on.

You have already added the right version in gradle.properties as per previous instruction.

2) Edit the build.gradle file and change the android.defaultConfig section to read
defaultConfig {
 versionCode buildVersionCode()
 versionName version
 ...
 }

3) In android block, Please add following


lintOptions {
     abortOnError false
 }

4) Add following code to make your build version code and version name

allprojects {
    repositories {
        jcenter()
    }
ext {
    /**
     * Builds an Android version code from the version of the project.
     * This is designed to handle the -SNAPSHOT and -RC format.
     *
     * I.e. during development the version ends with -SNAPSHOT. As the code stabilizes and release nears
     * one or many Release Candidates are tagged. These all end with "-RC1", "-RC2" etc.
     * And the final release is without any suffix.
     * @return
     */
    buildVersionCode = {
        //The rules is as follows:
        //-SNAPSHOT counts as 0
        //-RC* counts as the RC number, i.e. 1 to 98
        //final release counts as 99.
        //Thus you can only have 98 Release Candidates, which ought to be enough for everyone
        def candidate = "99"
        def (major, minor, patch) = version.toLowerCase().replaceAll('-', '').tokenize('.')
        if (patch.endsWith("snapshot")) {
            candidate = "0"
            patch = patch.replaceAll("[^0-9]","")
        } else {
            def rc
            (patch, rc) = patch.tokenize("rc")
            if (rc) {
                candidate = rc
            }
        }
        (major, minor, patch, candidate) = [major, minor, patch, candidate].collect{it.toInteger()}
        (major * 1000000) + (minor * 10000) + (patch * 100) + candidate;
    }
}
}





You can find the sample gradle files and instruction in my next blog.

http://www.scmtechblog.net/2016/01/sample-gradle-file.html

Note-> This is not just about generating Init script same as Super pom, You can even keep all these information in you build gradle file without having init script.