Getting version info and build data from grails war file - grails

In my application the user has the option to upload a war file to update the software.
I want to get some version information from the war file, before I deploy it to my server. How can I do this?
This information would be useful for me:
def jversion=[
"buildDate": grailsApplication.metadata["build.date"],
"version": grailsApplication.metadata["app.version"],
"branch": grailsApplication.metadata["GIT_BRANCH"],
"buildNumber": grailsApplication.metadata["build.number"],
"gitCommit": grailsApplication.metadata["GIT_COMMIT"]
]
What information can I get from the war file and how?
Best regards,
Peter

For this purpose you can add a script to the grails application that adds this information to a file whenever the user builds the war. Create a new script file under ./scripts in grails app with name _Events.groovy. Here you can hook into different grails events that gets triggered when an app starts or war gets built.
You can use eventCreateWarStart event to log the information whenever war gets built. Below is some sample code that can help you get started. It fetches the current branch name and commit id from local git and stores the data to a file named application.properties.
eventCreateWarStart = { warName, stagingDir ->
addBuildInfo("${stagingDir}/application.properties")
}
private void addBuildInfo(String propertyFile) {
def jVersion = [
"appName" : grailsApp.metadata['app.name'],
"version" : grailsApp.metadata["app.version"],
"buildDate" : new Date(),
"branch" : getBranch().trim(),
"Commit" : getRevision().trim(),
"buildNumber": System.getProperty("build.number", "CUSTOM"),
]
File file = new File(propertyFile)
file.text = ""
jVersion.each {
key, value ->
file.text += "${key}:\t${value}\n"
}
}
def getBranch() {
Process process = "git rev-parse --abbrev-ref HEAD".execute()
process.waitFor()
return process.text ?: 'UNKNOWN'
}
def getRevision() {
Process process = "git log --oneline --no-abbrev-commit -1".execute()
process.waitFor()
return process.text ?: 'UNKNOWN'
}
There is a grails plugin also that claims to fetch build properties from Hudson/Jenkins, if they are being used for building the war.

Grails 3.0.9, in GSP, you can get info from META-INF file. Try these
${grails.util.Metadata.current.getApplicationVersion()}
${grails.util.Metadata.current.getEnvironment()}
${grails.util.Metadata.current.getApplicationName()}
But i don't know how to get build date info.

For grails 3 you can use buildProperties task to add any custom information to war such as build date, verion info, git revision etc.
buildProperties {
inputs.property("info.app.build.date", new Date().format('yyyy-MM-dd HH:mm:ss'))
}
See this article for how to do the same http://nimavat.me/blog/grails3-add-custom-build-info-to-war

Related

How to retrieve the current dependencies in a Grails 3.2 script

Context
I created a Grails 3.2.11 script with the next command:
grails create-script script-test
The above command generated the file: script-test.groovy.
Then, I need to verify if a jar file dependency is present at the current grails project.
At Grails 2.4 you could do that with a grailsSettings.runtimeDependencies call:
def verifyJarDependency(String dependencyName) {
//Gets all Grails Application dependencies.
def dependenciesInstance = grailsSettings.runtimeDependencies
//Defines the result variable
def result = false
//Adds references to all classes used in Grails Application.
dependenciesInstance?.each { dependencyFile ->
//Gets the file name
String fileName = dependencyFile.name
//Verifies if the actual file contains the Dependency file string
if (fileName.contains(dependencyName)) {
//There is a Jar file with the Dependency string.
println "The Dependency file found is: $dependencyFile"
result = true
}
}
//If there is no Dependency jar file it returns false.
return result
}
For example if you execute the next piece of code inside a grails 2.4 script file:
target(buildPlugin: "Retrieves all the jar files which this instance is using") {
//Gets all Grails Application dependencies.
def dependenciesInstance = grailsSettings.runtimeDependencies
dependenciesInstance?.each { dependencyFile ->
//There is a Jar file with the Dependency string.
println dependencyFile
}
}
setDefaultTarget(buildPlugin)
You will get an output like next:
/home/username/.sdkman/candidates/grails/2.4.3/lib/org.grails/grails-datastore-simple/jars/grails-datastore-simple-3.1.2.RELEASE.jar
/home/username/.sdkman/candidates/grails/2.4.3/lib/org.grails/grails-datastore-gorm/jars/grails-datastore-gorm-3.1.2.RELEASE.jar
/home/username/.sdkman/candidates/grails/2.4.3/dist/grails-plugin-converters-2.4.3.jar
/home/username/.sdkman/candidates/grails/2.4.3/dist/grails-plugin-mimetypes-2.4.3.jar
/home/username/.sdkman/candidates/grails/2.4.3/lib/com.h2database/h2/jars/h2-1.3.176.jar
/home/username/.sdkman/candidates/grails/2.4.3/lib/log4j/log4j/jars/log4j-1.2.17.jar
/home/username/.sdkman/candidates/grails/2.4.3/dist/grails-resources-2.4.3.jar
This information will be used later for Proguard in order to obfuscate the jar file.
Question
How I can retrieve the current Grails 3.2 project dependencies inside a custom script?
You can execute "gradle dependencies" in custom script:
description "Project dependencies", "grails list-dependencies"
println gradle.dependencies()
and parse output

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.

Add command to Grails build process

I am using the grails-cdn-asset-pipline plugin. I've gone through the installation and configuration steps on GitHub and I reach the usage section which says
Add this command to your build process (usually before war generation and deployment).
// If all the settings are defined in your Config.groovy
grails asset-cdn-push
// Or
grails asset-cdn-push --provider=S3 --directory=my-bucket --gzip=true --storage-path=some-prefix --expires=365 --region=eu-west-1 --access-key=$MY_S3_ACCESS_KEY --secret-key=$MY_S3_SECRET_KEY
Where in my project do I put this command?
Is it something that I can do within the context of my project, or do I need to keep it separate in another build process and run it in an environment like Jenkins?
In _Events.groovy, I tried to invoke the script in the eventCreateWarStart, but I am having no luck there. (Code taken from this question)
eventCreateWarStart = { warName, stagingDir ->
def pluginManager = PluginManagerHolder.pluginManager
def plugin = pluginManager.getGrailsPlugin("cdn-asset-pipline")
def pluginDir = plugin.descriptor.file.parentFile
Map<String, String> env = System.getenv()
final processBuilder = new ProcessBuilder()
processBuilder.directory(new File("${cdnAssetPipelinePluginDir}/scripts"))
processBuilder.command([env['GRAILS_HOME']+"/bin/grails","cdn-asset-push"])
println processBuilder.directory()
Process proc = processBuilder.start()
proc.consumeProcessOutput(out, err)
proc.waitFor()
}
This link explains the run-script functionality which was merged into Grails 1.3.6. But I ran into the same problem of not knowing where to run it automatically.

How to access grails plugins directory in cloudbees from jenkins

I would like to execute a pre-build (grails) script from jenkins to replace a file in the plugins directory with a file in my SCM.
#!/bin/bash
PLUGINS_ORIG_DIR="plugins"
PLUGINS_DEST_DIR="/home/<my_user_name>/.grails/2.1.1/projects/judo/plugins"
cp -r $PLUGINS_ORIG_DIR/lang-selector-0.3/* $PLUGINS_DEST_DIR
But the script fails because the $PLUGINS_DEST_DIR cannot be found. Which should be the path or which is the best way to accomplish this?
Thank you.
[EDIT]
I have also tried to create an pre-war event, but it does not work either:
/**
* Copy modified resources to plugins directory, before packing the WAR
*/
eventCreateWarStart = { warName, stagingDir ->
def buildSettings = BuildSettingsHolder.getSettings()
def projectPluginsDir = buildSettings.getProperty("projectPluginsDir")
def baseDir = buildSettings.getProperty("baseDir")
ant.copy(todir:"${projectPluginsDir}/lang-selector-0.3", overwrite:true) {
fileset(dir:"${basedir}/plugins/lang-selector-0.3", includes:"**")
}
ant.copy(todir:"${projectPluginsDir}/jquery-datatables-1.7.5", overwrite:true) {
fileset(dir:"${basedir}/plugins/jquery-datatables-1.7.5", includes:"**")
}
}
as did you set your cloudbees account name ?
then you're wrong, should use /home/jenkins or just $HOME, as builds run on general purpose slaves as "jenkins" user
I have solved it by copying files to ${stagingDir}, instead of ${projectPluginsDir}

How do I clear my Jenkins/Hudson build history?

I recently updated the configuration of one of my hudson builds. The build history is out of sync. Is there a way to clear my build history?
Please and thank you
Use the script console (Manage Jenkins > Script Console) and something like this script to bulk delete a job's build history https://github.com/jenkinsci/jenkins-scripts/blob/master/scriptler/bulkDeleteBuilds.groovy
That script assumes you want to only delete a range of builds. To delete all builds for a given job, use this (tested):
// change this variable to match the name of the job whose builds you want to delete
def jobName = "Your Job Name"
def job = Jenkins.instance.getItem(jobName)
job.getBuilds().each { it.delete() }
// uncomment these lines to reset the build number to 1:
//job.nextBuildNumber = 1
//job.save()
This answer is for Jenkins
Go to your Jenkins home page → Manage Jenkins → Script Console
Run the following script there. Change copy_folder to your project name
Code:
def jobName = "copy_folder"
def job = Jenkins.instance.getItem(jobName)
job.getBuilds().each { it.delete() }
job.nextBuildNumber = 1
job.save()
My post
If you click Manage Hudson / Reload Configuration From Disk, Hudson will reload all the build history data.
If the data on disk is messed up, you'll need to go to your %HUDSON_HOME%\jobs\<projectname> directory and restore the build directories as they're supposed to be. Then reload config data.
If you're simply asking how to remove all build history, you can just delete the builds one by one via the UI if there are just a few, or go to the %HUDSON_HOME%\jobs\<projectname> directory and delete all the subdirectories there -- they correspond to the builds.
Afterwards restart the service for the changes to take effect.
Here is another option: delete the builds with cURL.
$ curl -X POST http://jenkins-host.tld:8080/jenkins/job/myJob/[1-56]/doDeleteAll
The above deletes build #1 to #56 for job myJob.
If authentication is enabled on the Jenkins instance, a user name and API token must be provided like this:
$ curl -u userName:apiToken -X POST http://jenkins-host.tld:8080/jenkins/job/myJob/[1-56]/doDeleteAll
The API token must be fetched from the /me/configure page in Jenkins. Just click on the "Show API Token..." button to display both the user name and the API token.
Edit: one might have to replace doDeleteAll by doDelete in the URLs above to make this work, depending on the configuration or the version of Jenkins used.
Here is how to delete ALL BUILDS FOR ALL JOBS...... using the Jenkins Scripting.
def jobs = Jenkins.instance.projects.collect { it }
jobs.each { job -> job.getBuilds().each { it.delete() }}
You could modify the project configuration temporarily to save only the last 1 build, reload the configuration (which should trash the old builds), then change the configuration setting again to your desired value.
If you want to clear the build history of MultiBranchProject (e.g. pipeline),
go to your Jenkins home page → Manage Jenkins → Script Console and run the following script:
def projectName = "ProjectName"
def project = Jenkins.instance.getItem(projectName)
def jobs = project.getItems().each {
def job = it
job.getBuilds().each { it.delete() }
job.nextBuildNumber = 1
job.save()
}
This one is the best option available.
Jenkins.instance.getAllItems(AbstractProject.class).each {it -> Jenkins.instance.getItemByFullName(it.fullName).builds.findAll { it.number > 0 }.each { it.delete() } }
This code will delete all Jenkins Job build history.
Using Script Console.
In case the jobs are grouped it's possible to either give it a full name with forward slashes:
getItemByFullName("folder_name/job_name")
job.getBuilds().each { it.delete() }
job.nextBuildNumber = 1
job.save()
or traverse the hierarchy like this:
def folder = Jenkins.instance.getItem("folder_name")
def job = folder.getItem("job_name")
job.getBuilds().each { it.delete() }
job.nextBuildNumber = 1
job.save()
Deleting directly from file system is not safe. You can run the below script to delete all builds from all jobs ( recursively ).
def numberOfBuildsToKeep = 10
Jenkins.instance.getAllItems(AbstractItem.class).each {
if( it.class.toString() != "class com.cloudbees.hudson.plugins.folder.Folder" && it.class.toString() != "class org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject") {
println it.name
builds = it.getBuilds()
for(int i = numberOfBuildsToKeep; i < builds.size(); i++) {
builds.get(i).delete()
println "Deleted" + builds.get(i)
}
}
}
Go to "Manage Jenkins" > "Script Console"
Run below:
def jobName = "build_name"
def job = Jenkins.instance.getItem(jobName)
job.getBuilds().each { it.delete() }
job.save()
Another easy way to clean builds is by adding the Discard Old Plugin at the end of your jobs. Set a maximum number of builds to save and then run the job again:
https://wiki.jenkins-ci.org/display/JENKINS/Discard+Old+Build+plugin
Go to the %HUDSON_HOME%\jobs\<projectname> remove builds dir and remove lastStable, lastSuccessful links, and remove nextBuildNumber file.
After doing above steps go to below link from UI
Jenkins-> Manage Jenkins -> Reload Configuration from Disk
It will do as you need
If using the Script Console method then try using the following instead to take into account if jobs are being grouped into folder containers.
def jobName = "Your Job Name"
def job = Jenkins.instance.getItemByFullName(jobName)
or
def jobName = "My Folder/Your Job Name
def job = Jenkins.instance.getItemByFullName(jobName)
Navigate to: %JENKINS_HOME%\jobs\jobName
Open the file "nextBuildNumber" and change the number. After that reload Jenkins configuration. Note: "nextBuildNumber" file contains the next build no that will be used by Jenkins.
Tested on jenkins 2.293 over linux. It will remove all the build logs but not the corellative build number
cd /var/lib/jenkins/jobs
find . -name "builds" -exec rm -rf {} \;
Be careful with this command because it executes a rm -rf on each find result. You could exec this first to validate if the result are only the builds folder of you jobs
find . -name "builds"
If you are looking for a solution where you have job inside a Folder you can use getItemByFullName function. It also supports white space in folder and job name.
def jobName = "folder_name/job_name"
def job = Jenkins.instance.getItemByFullName(jobName)
job.getBuilds().each { it.delete() }
job.nextBuildNumber = 1
job.save()

Resources