Groovy: Seems like a scope issue but cannot find where, can you? - groovy

I wrote some functions to help me writing Jenkins pipelines.
The following functions, are responsible to returning a shell command output:
def gen_uuid(){
randomUUID() as String
}
def sh_out(cmd){
String uuid = gen_uuid()
sh """( ${cmd} )> ${uuid}"""
String out = readFile(uuid).trim()
sh "set +x ; rm ${uuid}"
return out
}
That shown, here's another function:
Map get_started_by(){
withCredentials([ // Use Jenkins credentials ID of artifactory
[$class: 'UsernamePasswordMultiBinding', credentialsId: '0b8d591a-f4ce-XXXX-XXXX-faecb504d3d0', usernameVariable: 'J_USER', passwordVariable: 'J_PASS'],
]){
List startedBy = sh_out("""
set +x; curl -u ${J_USER}:${J_PASS} '${env.BUILD_URL}api/json' 2>/dev/null | \
python -mjson.tool | \
awk -F'"' '/(userId|userName)/{print \$4}'
""").split(/(\n)/)
return [
userId: startedBy[0],
userName: startedBy[1]
]
}
}
Which returns the userId and userName of the user who issue the job run.
Then, my problem is in this function:
def run_in_stage_func(String stage_name, Closure command, String sendTo){
String started_by = get_started_by()
String ulink = "<#${started_by['userId']}>"
String jlink = "(<${env.BUILD_URL}|Open>)"
println "============================================================"
stage (stage_name) {
try {
command()
if (currentBuild.result == 'FAILURE') {
error "Build failed, see log for further details."
}
println "============================================================"
} catch (Exception ex) {
def except = "${ex}"
slackSend channel: channel, color: 'danger', teamDomain: null, token: null,
message: " :${ulink} *Failed to build ${env.JOB_NAME}*! :x: ${jlink} (<!here|here>)"
echo "Pipeline failed at stage: ${stage_name}"
throw ex
}
}
}
When I run the job, I get the following error:
groovy.lang.MissingPropertyException: No such property: userId for class: java.lang.String
What could be the reason that the line ' String ulink = "<#${started_by['userId']}>" ' - is not working as intended?

You cast the result of get_started_by() to String explicitly. To fix it, change your code to
def started_by = get_started_by()
or
Map started_by = get_started_by()

Related

Cannot create annotated git tag from groovy script

Using:
Groovy Version: 3.0.8 JVM: 11.0.10 Vendor: Oracle Corporation OS: Linux
I have this script:
def shellCommand(String cmd) {
def process = cmd.execute()
def output = new StringWriter(), error = new StringWriter()
process.waitForProcessOutput(output, error)
println "exit value=${process.exitValue()}"
println "OUT: $output"
println "ERR: $error"
}
def gitRelease() {
def cmd001 = "git tag -a -m \"Release 0.0.777\" 0.0.45"
shellCommand(cmd001)
}
gitRelease()
When I run it from command line I get below error:
$ groovy myScript.groovy
exit value=128
OUT:
ERR: fatal: Failed to resolve '0.0.45' as a valid ref.
Same error if I try with slashy string:
def cmd001 = /git tag -a -m "Release 0.0.777" 0.0.45/
If I instead run git directly it works:
$ git tag -a -m "Release 0.0.777" 0.0.45
$ git tag
0.0.45
Creating a simple tag from the above groovy script works:
def gitRelease() {
//def cmd001 = "git tag -a -m \"Release 0.0.777\" 0.0.45"
def cmd001 = "git tag 0.0.46"
shellCommand(cmd001)
}
gives:
$ groovy myScript.groovy
exit value=0
OUT:
ERR:
Any suggestions?
The String.execute method often gives issues in unexpected places
There's another List.execute method that gives much more expected results
def cmd1 = ["git", "tag", "-a", "-m", "Release 0.0.777", "0.0.45"]
You should also change
def shellCommand(String cmd) {
To
def shellCommand(List cmd) {

Unable to test BaselinePipelineTest withCredentialInterceptor

I'm trying to test a function in my Jenkins Script Pipeline.
void doSomeDockerThings(){
withCredentials([[$class: 'UsernamePasswordMultiBinding', creditialsId: 'my_creds', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
sh "docker login -u $USERNAME -p '$PASSWORD' $DTR"
}
}
From a Groovy Unit Test that extends BasePipelineTest, I have the following test method.
#Test
void testDoSomeDockerThings(){
helper.registerAllowedMethod( "withCredentials", [LinkedHasMap.class, Closure.class], null );
helper.registerAllowedMethod( "sh", [String.class], {c -> return c; })
binding.setVariable( "USERNAME", "user" );
binding.setVariable( "PASSWORD", "password" );
binding.setVariable( "DTR", "Docker_Trusted_Registry" );
}
What I want to do is now write an assert that this command executed with what I expected. Since that is in a Closer of withCredentials how can I verify this?
sh "docker login -u $USERNAME -p '$PASSWORD' $DTR"
I've also tried using the withCredentialsInterceptor and I can't seem to get the syntax right for what it expects. If anyone has an example of that I would appreciate it.
helper.registerAllowedMethod( "withCredentials", [LinkedHasMap.class, Closure.class], withCredentialsInterceptor );
I'm really struggling to find documentation or examples are how to do this.

Jenkinsfile with email in loop fails on first iteration

Latest Jenkins used.
edit: pastebin of full Java exception - https://pastebin.com/zZDNj18E
Goal: loop through all nodes, check for offline, email alert for each offline node.
(tried emailext alerts as well, could not use "offline")
Failed: My jenkinsfile runs perfectly with no email.
With email in the for loop or separately defined in a function, the job crashes after the first email is sent.
[Pipeline] End of Pipeline an exception which occurred: in field hudson.model.Slave.launcher in object hudson.slaves.DumbSlave#ae938e61 .... and many more
My jenkinsfile:
pipeline {
agent{
label 'master'
}
options {
// Enable timestamps in log
timestamps()
skipDefaultCheckout()
timeout(time: 4, unit: 'MINUTES')
}
stages {
stage('Monitor') {
steps{
script{
def offlineSlaves = []
for (aSlave in hudson.model.Hudson.instance.slaves) {
def thisSlave = aSlave.name
echo 'Name: ' + thisSlave + ' is being checked.'
if ( aSlave.getComputer().isOffline().toString() == 'true') {
slaveState = 'OFFLINE'
echo 'Name: ' + thisSlave + ' is ' + slaveState + ' !'
emailext (
mimeType: 'text/html',
body: "${env.JOB_NAME} found an OFFLINE node: ${name} ",
subject: "Jenkins ERROR: Build Node ${name} is OFFLINE " ,
to: 'jfisher#xxx')
}
}
}
}
}
}
post {
failure {
emailext (
body: 'Monitor Nodes Jenkins Job failed !',
presendScript: '$DEFAULT_PRESEND_SCRIPT',
recipientProviders: [requestor(),culprits()],
subject: 'Monitor Nodes Jenkins Failed',
to: 'jfisher#intouchhealth.com')
}
}
}
The problem with this code is the getComputer() part. In the pipeline you should only use Serializable and the SlaveComputer returned from getComputer() isn't.
https://javadoc.jenkins.io/hudson/slaves/SlaveComputer.html
What you should do is move this part to a function annotated with NonCPS
#NonCPS
def shallTrigger() {
for (aSlave in hudson.model.Hudson.instance.slaves) {
def thisSlave = aSlave.name
echo 'Name: ' + thisSlave + ' is being checked.'
if ( aSlave.getComputer().isOffline().toString() == 'true') {
slaveState = 'OFFLINE'
echo 'Name: ' + thisSlave + ' is ' + slaveState + ' !'
emailext (
mimeType: 'text/html',
body: "${env.JOB_NAME} found an OFFLINE node: ${name} ",
subject: "Jenkins ERROR: Build Node ${name} is OFFLINE " ,
to: 'jfisher#xxx')
}
}
}

How to use if else condition in Gradle

Can someone tell me how could I write the if else condition in the gradle script
I mean i have two different types of zip files one is LiceseGenerator-4.0.0.58 and other one is CLI-4.0.0.60.My deployment script is working fine but I am using the shell script to do this and I want everything in gradle instead of doing it in the shell script.I want when I am deploying the LicenseGenerator it should deploy in differnet way and if it is CLI then it should deploy in other way.Currently deployall task is doing everyting.If I put if else condition how could I call the task.Please let me know if need any other information
Below is my script
// ------ Tell the script to get dependencies from artifactory ------
buildscript {
repositories {
maven {
url "http://ct.ts.th.com:8/artifactory/libs-snapshot"
}
}
// ------ Tell the script to get dependencies from artifactory ------
dependencies {
classpath ([ "com.trn.cm:cmplugin:1.1.118" ])
}
}
apply plugin: 'com.trn.cm.cmgplugin'
/**
* The folloing -D parameters are required to run this task
* - deployLayer = one of acceptance, latest, production, test
*/
//------------------------------------------------------------------------------------------
// Read the properties file and take the value as per the enviornment.
//
//------------------------------------------------------------------------------------------
if(!System.properties.deployLayer) throw new Exception ("deployLayer must be set")
def thePropFile = file("config/${System.properties.deployLayer}.properties")
if(!thePropFile.exists()) throw new Exception("Cannot load the specified environment properties from ${thePropFile}")
println "Deploying ${System.properties.jobName}.${System.properties.buildNumber} to ${System.properties.deployLayer}"
// load the deploy properties from the file
def deployProperties = new Properties()
thePropFile.withInputStream {
stream -> deployProperties.load(stream)
}
// set them in the build environment
project.ext {
deployProps = deployProperties
deployRoot = deployProperties["${System.properties.jobName}.deployroot"]
deployFolder = deployProperties["${System.properties.jobName}.foldername"]
deployPostInstallSteps = deployProperties["${System.properties.jobName}.postInstallSteps"]
}
task deleteGraphicsAssets(type: Delete, dependsOn: deploy) {
def dirName = "${deployRoot}"
delete dirName
doLast {
file(dirName).mkdirs()
}
}
task myCustomTask(dependsOn: deleteGraphicsAssets) << {
copy {
from 'deploymentfiles'
into "${deployRoot}"
}
}
task cleanTempDir(type: Delete, dependsOn: myCustomTask) {
delete fileTree(dir: "build/artifacts", exclude: "*.zip")
}
task unzipArtifact(dependsOn: cleanTempDir) << {
file("${buildDir}/artifacts").eachFile() {
println "Deploying ${it}"
// ant.mkdir(dir: "${deployRoot}/${deployFolder}")
ant.unzip(src: it, dest: "${deployRoot}")
}
}
task setPerms( type: Exec, dependsOn: unzipArtifact) {
workingDir "${deployRoot}"
executable "bash"
args "-c", "dos2unix analyticsEngine.sh"
args "-c", "chmod u+x analyticsEngine.sh && ./analyticsEngine.sh"
}
task deployAll(dependsOn: setPerms){}
I used in below way it is working fine
// ------ Tell the script to get dependencies from artifactory ------
buildscript {
repositories {
maven {
url "http://c.t.th.com:8/artifactory/libs-snapshot"
}
}
// ------ Tell the script to get dependencies from artifactory ------
dependencies {
classpath ([ "c.t.c:cmgin:1.1.118" ])
}
}
apply plugin: 'com.t.c.cmlugin'
/**
* The folloing -D parameters are required to run this task
* - deployLayer = one of acceptance, latest, production, test
*/
//------------------------------------------------------------------------------------------
// Read the properties file and take the value as per the enviornment.
//
//------------------------------------------------------------------------------------------
if(!System.properties.deployLayer) throw new Exception ("deployLayer must be set")
def thePropFile = file("config/${System.properties.deployLayer}.properties")
if(!thePropFile.exists()) throw new Exception("Cannot load the specified environment properties from ${thePropFile}")
println "Deploying ${System.properties.jobName}.${System.properties.buildNumber} to ${System.properties.deployLayer}"
// load the deploy properties from the file
def deployProperties = new Properties()
thePropFile.withInputStream {
stream -> deployProperties.load(stream)
}
// set them in the build environment
project.ext {
deployProps = deployProperties
deployRoot = deployProperties["${System.properties.jobName}.deployroot"]
deploydir = deployProperties["${System.properties.jobName}.deploydir"]
deployFolder = deployProperties["${System.properties.jobName}.foldername"]
deployPostInstallSteps = deployProperties["${System.properties.jobName}.postInstallSteps"]
}
task deleteGraphicsAssets(type: Delete, dependsOn: deploy) {
def dirName = "${deployRoot}"
delete dirName
doLast {
file(dirName).mkdirs()
}
}
task copyartifactZip << {
copy {
from "${deployRoot}"
into "${deploydir}/"
}
}
task copyLicenseZip << {
copy {
from "${deployRoot}"
into "${deploydir}/${deployFolder}"
}
}
task myCustomTask(dependsOn: deleteGraphicsAssets) << {
copy {
from 'deploymentfiles'
into "${deployRoot}"
}
}
task unzipArtifact(dependsOn: myCustomTask) << {
def theZip = file("${buildDir}/artifacts").listFiles().find { it.name.endsWith('.zip') }
println "Unzipping ${theZip} the artifact to: ${deployRoot}"
ant.unzip(src: theZip, dest: "${deployRoot}", overwrite: true)
}
task setPerms(type:Exec, dependsOn: unzipArtifact) {
workingDir "${deployRoot}"
executable "bash"
args "-c", "chmod -fR 755 *"
}
def dirName = "${deploydir}/${deployFolder}"
task zipDeployment(type: GradleBuild, dependsOn: setPerms) { GradleBuild gBuild ->
def env = System.getenv()
def jobName=env['jobName']
if (jobName.equals("LicenseGenerator")) {
delete dirName
file(dirName).mkdirs()
gBuild.tasks = ['copyLicenseZip']
} else {
gBuild.tasks = ['copyartifactZip']
}
}
task deployAll(dependsOn: zipDeployment){}
It's usually a bad practice to have if/else logic in the build script because it adds complexity and sometimes causes surprising and unexpected results. Since you have very different artifacts, it's advisable to have two different tasks for that, instead of one-for-all deployAll. And you should call corresponding task when you are in different environments.

Using Groovy's CliBuilder to get option error when the option is not first argument

my question is :
when i pass the script with :
groovy MyScript.groovy -o mtest -f filetest
the script can get the -o option.
but when i change the place of the option.
groovy MyScript.groovy -f filetest -o mtest
it can't get the option of -o
why?
do i miss something?
the groovy code is:
def cli = new CliBuilder()
cli.with {
usage: 'Self'
h longOpt:'help', 'U should input a analyze script with -o dataFileName!'
o longOpt:'output', 'The file which should be analyzed.', args:1, required:true
f longOpt:'file', 'File'
}
def opt = cli.parse(args)
def action
if( args.length == 0) {
cli.usage()
return
}
if( opt.h ) {
cli.usage()
return
}
println(args);
println(opt);
println(opt.o);
groovy MyScript.groovy -f filetest -o mtest
print result is :
[-f, filetest,-o,mtest]
groovy.util.OptionAccessor#66b51404
false
groovy MyScript.groovy -o mtest -f filetest
print result is :
[-o,mtest,-f, filetest]
groovy.util.OptionAccessor#66b51404
mtest
Think you need to specify args on your -f option as well (as it takes an argument) ie:
def cli = new CliBuilder().with {
usage: 'Self'
h longOpt:'help', 'U should input a analyze script with -o dataFileName!'
o longOpt:'output', 'The file which should be analyzed.', args:1, required:true
f longOpt:'file', 'File', args:1
it
}
def opt = cli.parse( args )
if( opt ) {
println args
println opt
println opt.o
}

Resources