Is it possible to execute Jenkins jobs from Powershell or Bash or Groovy scripts? - bash

I have separated quite small Jenkins jobs.
Now I need to configure another job which depending on the selected by the user parameters (selected probably through checkboxes?) will execute some of them.
I would like to start them from Powershell or Bash or Groovy script. Is it possible?

If you are using Groovy in a Postbuild/pipeline step, you can start the job via the Jenkins API.
For example, something like this for parameterless builds:
import hudson.model.*
Jenkins.instance.getItem("My Job").scheduleBuild(5)
And something like this for parameterized builds:
import hudson.model.*
Jenkins.instance.getItem("My Job").scheduleBuild( 5, new Cause.UpstreamCause( currentBuild ), new ParametersAction([ new StringParameterValue( "My Parameter Name", "My Parameter Value" ) ]));
You can also use the Jenkins Rest API for the rest. For example, by hitting the following url:
Parameterless:
curl -X POST JENKINS_URL/job/JOB_NAME/build
Parameterized:
curl -X POST JENKINS_URL/job/JOB_NAME/buildWithParameters?MyParameterName=MyParameterValue

sample:
import hudson.model.*
def actions=[]
def plist=[ ];
["ok":"ok","label":"master"].each {k,v->
plist << new hudson.model.StringParameterValue(k,"$v","");
}
actions.add(new hudson.model.ParametersAction(plist));
def future = Jenkins.instance.getItemByFullName("samples/testPipeline").scheduleBuild2(0,actions as hudson.model.Action[] );
future.get().getLog()

Related

Jenkins Pipeline throws "syntax error: bad substitution" when Passing in Parameter

I have a Terraform project that I was trying to use Jenkin's Custom Checkbox plugin (Custom Checkbox Parameter) with so that I can build separate applications dynamically using the same IaC, however, I'm getting the following error when passing in the name parameter for that plugin into the Terraform plan and apply commands.
syntax error: bad substitution
The idea for all this is just to click on "select all" or each individual app and run the build, and this will create the IaC for the given application(s).
I have a terraform plan that I am running as a smoke test to verify the parameters above are being passed in correctly before running the apply. This looks like the following:
sh 'terraform plan -var-file="terraform-dev.tfvars" -var "app_name=[${params[${please-work}]}]" -input=false'
The documentation for the plugin states that you can reference the items checked by using this format: "${params['please-work']}" which is what I've done above. That said, one caveat to this is that Im having to set the values in quotes for this to work since the variables are being set in the Terraform using list(string).
NOTE: I have tested that all this works if I just hardcode the app names with the escapes as following:
sh 'terraform plan -var-file="terraform-dev.tfvars" -var "app_name=[\\"app-1\\",\\"app-2\\"]" -input=false'
Again, what I need is for this to work with the -var "app_name=[${params[${please-work}]}]" without throwing that error.
If needed, here is the setup for the JSON that the plugin is using:
Additionally, I can see the values are being set the way I need them to be set when running the echo of echo "${params['please-work']}" on the initial build step. So these are coming back as "app-1", "app-2"
Again, all but that one bit is working and I've tried various ways to escape the needed strings to get this work and I need insight on a path forward. This would be greatly appreciated.
You are casting the script argument in your sh step method as a literal string, and therefore it will not interpolate the pipeline variable of type object params within the Groovy pipeline interpreter. You also are passing the variable value for the app_name with [] syntax (attempted list constructor?), which is not syntactically valid for shell, Terraform, or JSON, but is for Jenkins Pipeline and Groovy with undesired behavior (unclear what is desired here). Finally, please-work is a literal string and not a Jenkins Pipeline or Groovy variable, and since params is technically an object and not a Map, you must use the . syntax and not the [] syntax for accessors. You must update with:
sh(label: 'Execute Terraform Plan', script: "terraform plan -var-file='terraform-dev.tfvars' -var 'app_name=${params.please-work}' -input=false")
If another issue arises after fixing all of this, then it would be recommended to convert the plugin usage to the pipeline with a parameters directive, and also to probably remove the unusual characters e.g. - from the parameter name.
Thanks for helping me think through this, Matt. I was able to resolve the issue with the following shell script in the declarative pipeline:
sh "terraform plan -var-file='terraform-dev.tfvars' -var 'app_name=[${params['please-work']}]' -input=false"
This is working now.

List Branches With Active Choices Using Groovy Script

I'm trying to list github branches for a parameterized Jenkins job using active choice plugin. I tried the following groovy scripts, but to no avail - no branch is retrieved/listed on the drop down.
1.
def process = ("ssh-agent bash -c 'ssh-add /home/ubuntu/.ssh/id_rsa; git ls-remote -t -h git#github.com:username/repository.git'").execute()
return process.text.readLines().collect {
it.split()[1].replaceAll('refs/heads/', '').replaceAll('refs/tags/', '').replaceAll("\\^\\{\\}", '')
}
2.
def command = 'ssh-add /home/ubuntu/.ssh/id_rsa; git ls-remote -t -h git#github.com:username/repository.git'
def process = ["ssh-agent", "bash", "-c", command].execute()
return process.text.readLines().collect {
it.split()[1].replaceAll('refs/heads/', '').replaceAll('refs/tags/', '').replaceAll("\\^\\{\\}", '')
}
3.
def process = ["ssh-agent", "bash", "-c", "ssh-add", "/home/ubuntu/.ssh/id_rsa", "git", "ls-remote", "-t", "-h", "git#github.com:username/repository.git"].execute()
return process.text.readLines().collect {
it.split()[1].replaceAll('refs/heads/', '').replaceAll('refs/tags/', '').replaceAll("\\^\\{\\}", '')
}
I suspect this has something to do with how groovy parses nested shell commands, but I am not sure.
Your help would be much appreciated.
Update
I tried script 2 in groovysh as suggested by #cfrick and it works perfectly fine. It seems that the issue has something to do with my Jenkins version because extensible choice parameter plugin is having the same issue with my groovy script.
I decided to do a work around for my needs and dropped this whole thing.

How to access an environment variable that I defined from the last successful build

I am trying to access an environment variable that I defined in the job 'A' from another job 'B'
job 'A' defines it by -
evn.upload_loaction = "loc"
In job B I am trying to access the last successful build of JOb A and get that variable -
def item = Jenkins.instance.getItem("deploy-artifact-pipeline")
def dev_deployed_build=item.getLastSuccessfulBuild()
def envVars= dev_deployed_build.getEnvVars()
echo envVars['upload_loaction'] // prints null
echo envVars['BUILD_NUMBER'] // prints 21
My custom variable is not recognized but generic ones like build_number is available.
When I trigger Job A as downstream job then I can access using -
def jLz = build (job: 'deploy-artifact-pipeline')
echo jLz.buildVariables.PROCESSOR_UPLOAD_LOCATION // prints loc
Can someone help me with this? Or is there a better way to store and access that variable from a previous build ?
EnvInject plugin and Pipeline job doesn't go hand in hand, if some environment variable needs to be stored I usually prefer that job to be a free style job where you can send your env variable to be stored at the end of the build and that can be accessed for the specific build with "injectedEnvVars/api/json"
An alternate approach that I handled was to create an artifact file with the required information. And in the other Jenkins pipeline I download the artifact file and read the information in it.

Execute JDBC teststep using Groovy in SOAPUI

I am trying to execute JDBC teststep using Groovy in SOAPUI
def Proj = null
def workspace = testRunner.testCase.testSuite.project.getWorkspace();
Proj= workspace.getProjectByName("<ProjectName>")
def ProjTestCase = Proj.testSuites["["<TestSuiteName>"].testCases["
<TestCaseName>"]
def DBTestStep = ProjTestCase.getTestStepByName("Get_10_DBValues")
def runner = null
runner = DBTestStep.run(testRunner, context)
log.info(DBTestStep)
runner = null
I gives me following output
INFO:com.eviware.soapui.impl.wsdl.teststeps.JdbcRequestTestStep#7f34840b
See below line to run your soap UI project using cmd or store into batch file and run batch file
cd C:\Users\%username%\SmartBear\SoapUI-Pro-5.1.2\bin\
cmd.exe /C testrunner.bat -a -j -f"<localPathForReportStoring>" -R"JUnit-Style HTML Report" -EDefault "<LocalPath>\ProjectName.xml"
Referring to your comment,
Since your groovy script is within the project>testsuite>testCase, you cannot trigger your groovy script alone, externally.
However, you can enable only your groovy script and disable all other test steps.
When you run the project/testcase, your groovy will execute and call other steps explicitly. SOAPUI will not run disabled steps by its own.
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext
def DBTestStep = ProjTestCase.getTestStepByName("Get_10_DBValues")
def runner = new WsdlTestRunContext(DBTestStep)
DBTestStep.run(testRunner, runner )

How to mark a build unstable in Jenkins when running shell scripts

In a project I'm working on, we are using shell scripts to execute different tasks. Some are sh/bash scripts that run rsync, and some are PHP scripts. One of the PHP scripts is running some integration tests that output to JUnit XML, code coverage reports, and similar.
Jenkins is able to mark the jobs as successful / failed based on exit status. In PHP, the script exits with 1 if it has detected that the tests failed during the run. The other shell scripts run commands and use the exit codes from those to mark a build as failed.
// :: End of PHP script:
// If any tests have failed, fail the build
if ($build_error) exit(1);
In Jenkins Terminology, an unstable build is defined as:
A build is unstable if it was built successfully and one or more publishers report it unstable. For example if the JUnit publisher is configured and a test fails then the build will be marked unstable.
How can I get Jenkins to mark a build as unstable instead of only success / failed when running shell scripts?
Modern Jenkins versions (since 2.26, October 2016) solved this: it's just an advanced option for the Execute shell build step!
You can just choose and set an arbitrary exit value; if it matches, the build will be unstable. Just pick a value which is unlikely to be launched by a real process in your build.
It can be done without printing magic strings and using TextFinder. Here's some info on it.
Basically you need a .jar file from http://yourserver.com/cli available in shell scripts, then you can use the following command to mark a build unstable:
java -jar jenkins-cli.jar set-build-result unstable
To mark build unstable on error, you can use:
failing_cmd cmd_args || java -jar jenkins-cli.jar set-build-result unstable
The problem is that jenkins-cli.jar has to be available from shell script. You can either put it in easy-to-access path, or download in via job's shell script:
wget ${JENKINS_URL}jnlpJars/jenkins-cli.jar
Use the Text-finder plugin.
Instead of exiting with status 1 (which would fail the build), do:
if ($build_error) print("TESTS FAILED!");
Than in the post-build actions enable the Text Finder, set the regular expression to match the message you printed (TESTS FAILED!) and check the "Unstable if found" checkbox under that entry.
You should use Jenkinsfile to wrap your build script and simply mark the current build as UNSTABLE by using currentBuild.result = "UNSTABLE".
stage {
status = /* your build command goes here */
if (status === "MARK-AS-UNSTABLE") {
currentBuild.result = "UNSTABLE"
}
}
you should also be able to use groovy and do what textfinder did
marking a build as un-stable with groovy post-build plugin
if(manager.logContains("Could not login to FTP server")) {
manager.addWarningBadge("FTP Login Failure")
manager.createSummary("warning.gif").appendText("<h1>Failed to login to remote FTP Server!</h1>", false, false, false, "red")
manager.buildUnstable()
}
Also see Groovy Postbuild Plugin
In my job script, I have the following statements (this job only runs on the Jenkins master):
# This is the condition test I use to set the build status as UNSTABLE
if [ ${PERCENTAGE} -gt 80 -a ${PERCENTAGE} -lt 90 ]; then
echo WARNING: disc usage percentage above 80%
# Download the Jenkins CLI JAR:
curl -o jenkins-cli.jar ${JENKINS_URL}/jnlpJars/jenkins-cli.jar
# Set build status to unstable
java -jar jenkins-cli.jar -s ${JENKINS_URL}/ set-build-result unstable
fi
You can see this and a lot more information about setting build statuses on the Jenkins wiki: https://wiki.jenkins-ci.org/display/JENKINS/Jenkins+CLI
Configure PHP build to produce xml junit report
<phpunit bootstrap="tests/bootstrap.php" colors="true" >
<logging>
<log type="junit" target="build/junit.xml"
logIncompleteSkipped="false" title="Test Results"/>
</logging>
....
</phpunit>
Finish build script with status 0
...
exit 0;
Add post-build action Publish JUnit test result report for Test report XMLs. This plugin will change Stable build to Unstable when test are failing.
**/build/junit.xml
Add Jenkins Text Finder plugin with console output scanning and unchecked options. This plugin fail whole build on fatal error.
PHP Fatal error:
Duplicating my answer from here because I spent some time looking for this:
This is now possible in newer versions of Jenkins, you can do something like this:
#!/usr/bin/env groovy
properties([
parameters([string(name: 'foo', defaultValue: 'bar', description: 'Fails job if not bar (unstable if bar)')]),
])
stage('Stage 1') {
node('parent'){
def ret = sh(
returnStatus: true, // This is the key bit!
script: '''if [ "$foo" = bar ]; then exit 2; else exit 1; fi'''
)
// ret can be any number/range, does not have to be 2.
if (ret == 2) {
currentBuild.result = 'UNSTABLE'
} else if (ret != 0) {
currentBuild.result = 'FAILURE'
// If you do not manually error the status will be set to "failed", but the
// pipeline will still run the next stage.
error("Stage 1 failed with exit code ${ret}")
}
}
}
The Pipeline Syntax generator shows you this in the advanced tab:
I find the most flexible way to do this is by reading a file in the groovy post build plugin.
import hudson.FilePath
import java.io.InputStream
def build = Thread.currentThread().executable
String unstable = null
if(build.workspace.isRemote()) {
channel = build.workspace.channel;
fp = new FilePath(channel, build.workspace.toString() + "/build.properties")
InputStream is = fp.read()
unstable = is.text.trim()
} else {
fp = new FilePath(new File(build.workspace.toString() + "/build.properties"))
InputStream is = fp.read()
unstable = is.text.trim()
}
manager.listener.logger.println("Build status file: " + unstable)
if (unstable.equalsIgnoreCase('true')) {
manager.listener.logger.println('setting build to unstable')
manager.buildUnstable()
}
If the file contents are 'true' the build will be set to unstable. This will work on the local master and on any slaves you run the job on, and for any kind of scripts that can write to disk.
I thought I would post another answer for people that might be looking for something similar.
In our build job we have cases where we would want the build to continue, but be marked as unstable. For ours it's relating to version numbers.
So, I wanted to set a condition on the build and set the build to unstable if that condition is met.
I used the Conditional step (single) option as a build step.
Then I used Execute system Groovy script as the build step that would run when that condition is met.
I used Groovy Command and set the script the following
import hudson.model.*
def build = Thread.currentThread().executable
build.#result = hudson.model.Result.UNSTABLE
return
That seems to work quite well.
I stumbled upon the solution here
http://tech.akom.net/archives/112-Marking-Jenkins-build-UNSTABLE-from-environment-inject-groovy-script.html
In addition to all others answers, jenkins also allows the use of the unstable() method (which is in my opinion clearer).
This method can be used with a message parameter which describe why the build is unstable.
In addition of this, you can use the returnStatus of your shell script (bat or sh) to enable this.
For example:
def status = bat(script: "<your command here>", returnStatus: true)
if (status != 0) {
unstable("unstable build because script failed")
}
Of course, you can make something with more granularity depending on your needs and the return status.
Furthermore, for raising error, you can also use warnError() in place of unstable(). It will indicate your build as failed instead of unstable, but the syntax is same.
The TextFinder is good only if the job status hasn't been changed from SUCCESS to FAILED or ABORTED.
For such cases, use a groovy script in the PostBuild step:
errpattern = ~/TEXT-TO-LOOK-FOR-IN-JENKINS-BUILD-OUTPUT.*/;
manager.build.logFile.eachLine{ line ->
errmatcher=errpattern.matcher(line)
if (errmatcher.find()) {
manager.build.#result = hudson.model.Result.NEW-STATUS-TO-SET
}
}
See more details in a post I've wrote about it:
http://www.tikalk.com/devops/JenkinsJobStatusChange/
As a lighter alternative to the existing answers, you can set the build result with a simple HTTP POST to access the Groovy script console REST API:
curl -X POST \
--silent \
--user "$YOUR_CREDENTIALS" \
--data-urlencode "script=Jenkins.instance.getItemByFullName( '$JOB_NAME' ).getBuildByNumber( $BUILD_NUMBER ).setResult( hudson.model.Result.UNSTABLE )" $JENKINS_URL/scriptText
Advantages:
no need to download and run a huge jar file
no kludges for setting and reading some global state (console text, files in workspace)
no plugins required (besides Groovy)
no need to configure an extra build step that is superfluous in the PASSED or FAILURE cases.
For this solution, your environment must meet these conditions:
Jenkins REST API can be accessed from slave
Slave must have access to credentials that allows to access the Jenkins Groovy script REST API.
If you want to use a declarative approach I suggest you to use code like this.
pipeline {
stages {
// create separate stage only for problematic command
stage("build") {
steps {
sh "command"
}
post {
failure {
// set status
unstable 'rsync was unsuccessful'
}
always {
echo "Do something at the end of stage"
}
}
}
}
post {
always {
echo "Do something at the end of pipeline"
}
}
}
In case you want to keep everything in one stage use catchError
pipeline {
stages {
// create separate stage only for problematic command
stage("build") {
steps {
catchError(stageResult: 'UNSTABLE') {
sh "command"
}
sh "other command"
}
}
}
}
One easy way to set a build as unstable, is in your "execute shell" block, run exit 13
You can just call "exit 1", and the build will fail at that point and not continue. I wound up making a passthrough make function to handle it for me, and call safemake instead of make for building:
function safemake {
make "$#"
if [ "$?" -ne 0 ]; then
echo "ERROR: BUILD FAILED"
exit 1
else
echo "BUILD SUCCEEDED"
fi
}

Resources