Creating Jenkins pipeline using a multi-project gradle build - jenkins

I have a multi-project Gradle build working locally.
There is a parent gradle file, build.gradle
The settings.gradle file assigns the projects to their respective gradle build files:
include 'load'
include 'loadRemote'
project(':loadRemote').buildFileName = 'remoteLoad_build.gradle'
project(':load').buildFileName = 'load_build.gradle'
rootProject.name = 'EquipLoad'
The build.gradle parent file calls a buildAll command to build the 2 projects from the command line locally.
I created a Jenkins file to build both projects. But the Jenkins pipeline does not recognize the specific project tasks.
These are the tasks for the buildAll command
gradle.projectsEvaluated {
task compileAll (dependsOn: [project(':loadRemote').remoteLoadCleanCompileStage]) {
compileAll.finalizedBy project(':load').loadCleanCompileStage
}
task packageAll (dependsOn: [project(':loadRemote').remoteLoadPackage]) {
packageAll.finalizedBy project(':load').loadPackage
}
task buildAll (dependsOn: [compileAll]) {
buildAll.finalizedBy packageAll
}
}
The error in Jenkins is that it does not recognize the task project(':loadRemote').remoteLoadCleanCompileStage
How can I identify a multi-build project in Jenkins?
Do I have to add the settings.gradle file?
UPDATE
I thought that the different build files could not be located in the project so I added this to the settings.gradle file
rootProject.name = 'EquipLoad'
include 'load'
project(':load').projectDir = new File(settingsDir, rootProject.rootDir.getAbsolutePath() + "/Load")
project(':load').buildFileName = 'load_build.gradle'
include 'loadRemote'
project(':loadRemote').projectDir = new File(settingsDir, rootProject.rootDir.getAbsolutePath() + "/LoadRemote")
project(':loadRemote').buildFileName = 'remoteLoad_build.gradle'
The error is still the same, the build.gradle file (parent) does not recognize the dependency task project(':loadRemote').remoteLoadCleanCompileStage
Looking at the debug statements, the child build gradle files are found and identified:
Evaluating project ':loadRemote' using build file '/var/.../loadRemote/remoteLoad_build.gradle'.
The same text is shown for the load build file.
Yet the tasks within these gradle build files are not recognized in the parent build.gradle file.

The problem was a simple case sensitive mistake.
I named the folders: Load and LoadRemote. But identified them in the gradle scripts as ':load' and ':loadRemote'. By changing the script text to ':Load' and ':LoadRemote' fixed my problem.

Related

Jenkins, Gradle : How to publish Dependency report to Sonar Dashboard

Currently we're using Jenkins free style job for Gradle project and using following commands to run Sonar and Dependencycheck
./gradlew clean build sonarqube dependencyCheckAnalyze \
and I'm getting following message
Analyzing /opt/jenkins_slave_home/workspace/AA/package-lock.json - however, the node_modules directory does not exist. Please run npm install prior to running dependency-check
Generating report for project AA_ArbitraryBuild
Found 0 vulnerabilities in project AA
and we can able to see a file inside "ws/build/reports/" but it dint scanned anything.
Following are the "build.gardle" file
buildscript {
repositories {
maven { url artifactoryRepoUrl }
mavenCentral()
}
dependencies {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7'
classpath 'org.owasp:dependency-check-gradle:6.0.3'
}
}
apply plugin: 'org.sonarqube'
apply plugin: 'org.owasp.dependencycheck'
sonarqube {
properties {
property 'sonar.projectName', sonarProjectName
property 'sonar.projectKey', sonarProjectKey
property 'sonar.host.url', sonarHostUrl
property 'sonar.login', sonarAuthToken
property 'sonar.dependencyCheck.reportPath', sonarDependencyCheckReport
property 'sonar.dependencyCheck.htmlReportPath', sonarDependencyCheckHTMLReport
}
}
Can you plz help on what are the additional steps that I need to add.
You've got all you need to push result to sonar. Make sure that you provide right path for your owasp vulnerabilities report for sonar plugin. It's sonar.dependencyCheck.reportPath and should point to build/reports directroy, and if you produce html report file you can point it with sonar.dependencyCheck.htmlReportPath.

Copy subfolder with groovy in Jenkins

I am trying to script a groovy script which copies a complete folder with all subfolder and jobs to the actual folder, where the script is executed.
Here you can see how my folderstructure looks like.
--> Templ
|-->Folder
|-->Folder
|-->Subfolder
|-->Subsubfolder
|-->Subfolder
|-->Folder
-->Execution 2020
|-->Copyscript
I tried with different Plug-Ins like Jobcopy Builder.
Finally I tried with groovy scrips but nothing seems to work.
the simplest way to use AntBuilder
def ant = new AntBuilder()
ant.copy(todir: myDir) {
fileset(dir: "src/test") {
include(name: "**/*.java")
}
}
example taken from here
http://docs.groovy-lang.org/latest/html/documentation/ant-builder.html
to see all parameters of ant copy command see documentation:
https://ant.apache.org/manual/Tasks/copy.html

How to load a variable within a pipeline using a property file from another freestyle project?

I have a Jenkins "freestyle" project which triggers a "pipeline" project (in fact my "freestyle" project is mentionned as a trigger in "Build Triggers" step of the pipeline project).
How could I grab values of variables from a ".properties" file created by each build of the "parent/freestyle" project?
Currently I have checked "archive artifacts" on the "parent/freestyle" projet and add following code to my "child/pipeline":
node
{
load "${WORKSPACE}/variables.properties"
echo "${PARAM_FROM_TRIGGER}"
}
pipeline
{
agent any
stages
{
stage('STEP1')
{
steps
{
sh '''
#!/bin/bash
echo 'STEP 1'
'''
}
}
}
}
I encounter an exception after the "child/pipeline" build:
java.nio.file.NoSuchFileException:
/var/lib/jenkins/workspace/my_pipeline/variables.properties
How could I load values from my property file?
Since you're already archiving the .properties file, I think you're looking for the Copy Artifact Plugin.
You can use the command:
copyArtifacts(projectName: 'sourceproject');
to copy the artifacts from parent/freestyle into the workspace of child/pipeline.

how to create a simple build script for grails 2.5

I have read the documentation here, which gives the structure, but does not offer any help on how to create the actual logic.
I want to build a very simple script which does a clean, creates the war, creates a zip of the sources (without the target directory, and without any svn dirs), and creates a tar (idealy gzipped) of the migrations dir so this can be used with liquibase. Each of these 3 artifacts should have the app version in their name (like the existing "grails war" does.
The project structure looks like this:
svn
main-app
grails-app
migrations
target
:
exploded-plugin-1
grails-app
:
exploded-plugin-2
grails-app
:
This is how far I have got:
includeTargets << grailsScript("_GrailsInit")
target(packageUs: "Creates war, zips source, tars liquibase scripts") {
depends(clean, war-us, zip-source, tar-liquibase)
}
setDefaultTarget(packageUs)
target ("war-us" : "creates war") {
ant.war() // this was a guess at calling the existing war - it doesnt work
}
target ("zip-source" : "zips sources") {
// cd to top dir of project, i.e. above the app.
}
target ("tar-liquibase":"produces tar of migrations dir") {
// tar up the migrations dir
// name it with the app-version
// put it in the target dir along side the war etc.
}
target ("clean") {
// call the default clean some how, or cd to target dir, and delete everything
}
The above script was originally created with "grails create-script package-us"
Sadly, not even this works, it produces the following error:
| Error Error executing script PackageUs: No signature of method: org.codehaus.gant.GantBinding$_initializeGantBinding_closure5.doCall() is applicable
for argument types: (java.lang.String, PackageUs$_run_closure5) values: [clean, PackageUs$_run_closure5#5ed0b4e3]
I could not find any example scripts nor documentation beyond the very basic overview in the link.
I cant even get ant.echo() to work - intellij says there is only one ant.echo function which takes a LinkedHashmap, a String and a Closure, but the ant documentation says echo only takes a "message" string. What should the linkedhashmap, string and closure be? I have tried minimum 30 different variations, none work.
Update 1: What I have got working so far
includeTargets << grailsScript("_GrailsInit")
target(packageUs: "Creates war, zips source, tars liquibase scripts") {
depends(clean, "war-us", "zip-source")
}
setDefaultTarget(packageUs)
target ("war-us" : "creates war") {
ant.echo(message:"hello") // this compiles and runs, but does nothing.
println "hello there" // this does work.
// ant.war(?)
}
// puts in it wrong dir, and doen't have app version in name, but at least it zips the right content!
target ("zip-source" : "zips sources") {
ant.delete(file: 'sources.zip') // dont know how to add this to the clean cycle
ant.zip(destfile: 'sources.zip', basedir: '..', excludes: "**/*.zip **/target/** **.idea **/.classpath **/.iml")
}
What I havent figured out:
How to get hold of app.version so it can put put in file names. e.g this: println "creating war for $app.version" does not work
how to build the war. It is not possible to put it in the depdends list, and ant.war("myfile.war") does not work unfortunately. Other strategies might be running this script on the war building event, which is not ideal, as wars are built frequiently without needing this, or perhaps to call "grails war" by calling a shell command.
Update 2 - Can't produce a "prod" war
With help from Ashraf Purno, we have a script (below) which creates a war, zips the sources and tar.gz the liquibase files and produces our package of them. However, it has one major flaw, the war that is created is always the "dev" version, in that when you deploy it to a tomcat, it tires to use the dev datasource, and dev environment. There seems to be no way to change this in the build scirpt, and setting the environment to be prod on the command line which calls the script (e.g. "grails prod myscript") also has no influence - it also produces a dev version of the war (which is of no use)
includeTargets << grailsScript("_GrailsInit")
includeTargets << grailsScript("_GrailsClean")
includeTargets << grailsScript("War")
target(warpack: "Creates war, zips source, tars liquibase scripts") {
depends(cleanAll, cleanRealyAll, war, "zip-source", "tar-liquibase", "package-all")
}
setDefaultTarget(warpack)
target ("cleanRealyAll" : "Cleans stuff that clean-all wont touch") {
println "wiping the target dir"
ant.delete(dir: "target")
ant.mkdir(dir: "target")
}
target ("zip-source" : "zips sources") {
println "zipping sources for ${metadata.'app.version'}"
String zipFile = "target/sources-${metadata.'app.version'}.zip"
ant.delete(file: zipFile)
ant.zip(destfile: zipFile, basedir: '..', excludes: "**/*.zip **/target/** **.idea **/.classpath **/.iml")
}
target ("tar-liquibase":"produces tar of migrations dir") {
println "tarring liquibase for ${metadata.'app.version'}"
String tarFile = "target/migrations-${metadata.'app.version'}.tar"
String gzipfile = "target/migrations-${metadata.'app.version'}.tar.gz"
ant.tar(destfile:tarFile, basedir: "grails-app/migrations")
ant.gzip(src: tarFile, destfile : gzipfile )
ant.delete(file: tarFile)
}
target ("package-all":"puts it all together in one file, relies on externally running 'grails war' first") {
println "creating package for ${metadata.'app.version'}"
String packageFile = "target/ourpackage-${metadata.'app.version'}.tar"
ant.delete(file: packageFile)
ant.tar (destfile: packageFile, basedir:"target", includes: "*.war *.zip *.gz" )
}
After a bit googling and peeking through grails core scripts I managed to create a script which does the things you mentioned in your question. Here it is
import grails.util.Metadata
includeTargets << grailsScript("_GrailsInit")
includeTargets << grailsScript("_GrailsClean")
includeTargets << grailsScript("_GrailsWar")
target(pack: "Creates war, zips source, tars liquibase scripts") {
depends(cleanAll, war)
String appVersion = Metadata.current[Metadata.APPLICATION_VERSION],
zipFileName = "${basedir}/target/sources-${appVersion}.zip",
tarFileName = "${basedir}/target/migrations-${appVersion}.tar.gz"
println "Creating Sources Zip"
ant.delete(file: zipFileName)
ant.zip(destfile: zipFileName, basedir: basedir, excludes: "**/target/** **/.idea/** **/.classpath/** **/.iml/**")
println "Creating Migrations Tar Ball"
ant.delete(file: tarFileName)
ant.tar(destfile: tarFileName, basedir: "${basedir}/grails-app/migrations")
}
setDefaultTarget(pack)
I have put all the tasks in a single target just for simplicity. You can divide them in several targets and add them to depends if you want. I have used Grails 2.5.1 for testing this script.
You can take a look here http://mrhaki.blogspot.com/2014/05/grails-goodness-run-groovy-scripts-in.html for some available props/configs in scripts.

Gradle project / task dependency & JavaScript lib in war

We have just started using Gradle and do have a few (noob) questions - hopefully someone can shed some light on those issues :)
We're using Angular and Grails to build our web-app. We want to be as modular as possible and hence put all the Angular-related artifacts (mainly *.js and *.html files) in a separate project in our Gradle multiproject build.
Our project structure is as follows:
- root
-- build.gradle
-- settings.gradle
|-- web-grails (grails project)
|----- build.gradle
|-- web-js-html (angular / js / html sources)
|----- build.gradle
As a start, we simply want to package web-js-html project accordingly. What we've come up so far (other suggestions very welcome) is to apply a webjars structure to it, i.e. have a .jar file with the required resources under /META-INF/resources. Online, we found the following config that seems to work just fine:
// file :web-js-grails/build.gradle
apply plugin: 'java'
ext {
webjarconfig = [
staticHTMLFilesDir : "${projectDir}/src/main/webfrontend",
baseDir: "META-INF/resources/",
subDir : "webjars/" + deployed_app_name
]
}
configurations {
webjar
}
task webjar(type: Jar, dependsOn: 'jar') {
from(fileTree(webjarconfig.staticHTMLFilesDir)) {
into webjarconfig.baseDir + webjarconfig.subDir
}
outputs.file archivePath
}
artifacts {
webjar(webjar.archivePath) {
type 'jar'
builtBy webjar
}
}
By invoking 'gradle webjar', the jar gets created with the files in the correct place.
Question 1:
What I would have expected is that this jar also gets properly created if I invoke 'gradle build'. As far as I understand, 'gradle build' is a task defined by the java plugin which, at some point, invokes the 'jar' task. Once that 'jar' task is done, I would expect the webjar task to be invoked. But it's not, so clearly I'm missing something. Does it follow that webjar only ever gets executed if explicitly invoked (either from command-line or from within the build.gradle file)?
Now, we would like the webjar to be included in the web-grails war-file. The config of :web-grails/build.gradle is as follows:
apply plugin: "grails"
repositories {
mavenLocal()
maven { url artifactory_url }
}
buildscript {
repositories {
mavenLocal()
maven { url artifactory_url }
}
dependencies {
classpath 'org.grails:grails-gradle-plugin:2.0.1-SNB1'
}
}
grails {
grailsVersion = '2.3.8'
groovyVersion = '2.3.0'
}
dependencies {
bootstrap 'org.grails.plugins:tomcat:7.0.50'
compile project(':web-js-html')
}
After try-and-error and quite a bit of reading, I arrived at this (possibly wrong) conclusion: when I invoke 'gradle build' on :web-grails, then (I assume) :build will also be invoked on the referenced :web-js-html project. I say this because the jar gets re-created in the build/lib folder, but obviously not using the webjar-task. Hence, the resulting jar only contains the MANIFEST.MF only.
Question 2:
Do I use Gradle correctly in that case and am I only overseeing a little thing or is this whole approach questionable? How can I get the :web-js-html jar into the war properly?
Thank you for your help in advance!
Your part where you define the new artifact doesn't make any sense for me. Change
artifacts {
webjar(webjar.archivePath) {
type 'jar'
builtBy webjar
}
}
to
artifacts {
webjar webjar
}
Maybe you should rename either your configuration or your task. However the first webjar is your configuration and the second one your task which creates the new jar.
Note that this will create a new artifact, so you have to give it a different name with
task webjar(type: Jar, dependsOn: 'jar') {
baseName = 'newJar'
from(fileTree(webjarconfig.staticHTMLFilesDir)) {
into webjarconfig.baseDir + webjarconfig.subDir
}
outputs.file archivePath
}
But I think you don't want to create a second jar, but change the original one. In that case your don't have to write a new task, but configure the default jar task like this:
jar {
from(fileTree(webjarconfig.staticHTMLFilesDir)) {
into webjarconfig.baseDir + webjarconfig.subDir
}
outputs.file archivePath
}

Resources