Jenkins plugin - how to get currently executing job? - jenkins

I am building a jenkins pipeline plugin (methods to be invoked from a pipeline) and need to get retrieve information about the currently running job, which invoked my methods.
There are a couple of questions I found talking about it, for example here - Jenkins Plugin How to get Job information.
Yet I can't figure out how to use this information. I do have access to the Jenkins instance, but don't have any info about the current project, job, build, etc. How can I get hold of that info?
Note, this is a pipeline steps plugin, there is no perform method in it.

Ok, after search, I finally found the answer in the most obvious of all places - documentation for writing pipeline steps plugins and the corresponding API documentation.
The way to do it is from the Execution class. Inside it, just call getContext(), which returns StepContext, which then has .get method to get the rest of the things you need:
public class MyExecution extends SynchronousNonBlockingStepExecution<ReturnType> {
...
#Override
protected ReturnType run() throws Exception {
try {
StepContext context = getContex();
// get currently used workspace path
FilePath path = context.get(FilePath.class);
//get current run
Run run = context.get(Run.class);
// ... and so on ...
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
...
}

Related

No such field found: field java.lang.String sinput error when accessing cppcheck plugin classes

]I am a junior dev trying to lear about Jenkins, I have been learning on my own for a couple of months. Currently I have a pipeline (just for learning purposes) which runs static analysis on a folder, and then publish it, I have been able to send a report through email using jelly templates, from there I realized it is posbile to instantiate the classes of a plugin to use its methods so I went to the cppcheck javadoc and did some trial and error so I can get some values of my report and then do something else with them, so I had something like this in my pipeline:
pipeline {
agent any
stages {
stage('analysis') {
steps {
script{
bat'cppcheck "E:/My_project/Source/" --xml --xml-version=2 . 2> cppcheck.xml'
}
}
}
stage('Test'){
steps {
script {
publishCppcheck pattern:'cppcheck.xml'
for (action in currentBuild.rawBuild.getActions()) {
def name = action.getClass().getName()
if (name == 'org.jenkinsci.plugins.cppcheck.CppcheckBuildAction') {
def cppcheckaction = action
def totalErrors = cppcheckaction.getResult().report.getNumberTotal()
println totalErrors
def warnings = cppcheckaction.getResult().statistics.getNumberWarningSeverity()
println warnings
}
}
}
}
}
}
}
which output is:
[Pipeline] echo
102
[Pipeline] echo
4
My logic (wrongly) tells me that if I can access to the report and statistics classes like that and uses their methods getNumberTotal() and getNumberWarningSeverity() respectively, therefore I should be able to also access the DiffState class in the same way and use the valueOf() method to get an enum of the new errors. But adding this to my pipeline:
def nueva = cppcheckaction.getResult().diffState.valueOf(NEW)
println nueva
Gives me an error:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: No such field found: field org.jenkinsci.plugins.cppcheck.CppcheckBuildAction diffState
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.unclassifiedField(SandboxInterceptor.java:425)
at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:409)
...
I can see in the javadoc there is a diffState class with a valueOf() method, but I cannot access to it is therre any other way to get the new errors between the last build and the current one?
I see 2 issues that could be causing this:
CppcheckResult doesn't have a member variable diffState so you can't access it obviously
If you check the javadoc of CppcheckResult the class does have:
private CppcheckReport report;
public CppcheckStatistics getReport()
and
private CppcheckStatistics statistics;
public CppcheckStatistics getStatistics()
there is no member (and getter method) for diffState so maybe try to call:
/**
* Get differences between current and previous statistics.
*
* #return the differences
*/
public CppcheckStatistics getDiff(){
my suggestion: cppcheckaction.getResult().getDiff().valueOf(NEW). Furthermore CppcheckWorkspaceFile does have a method getDiffState().
Please have a look at the script approval of your Jenkins (see here).
The syntax error might appear because Jenkins (Groovy Sandbox) blocks the execution of an (for the Jenkins) "unknown" and potential dangerous method.
Jenkins settings - Script Approval - Approve your blocked method

Determine Failed Stage in Jenkins Scripted Pipeline

I am looking for a generic way to determine the name of the failed stage at the end of a Jenkins scripted Pipeline.
Please note that this is different than Determine Failed Stage in Jenkins Declaritive Pipeline which is about declarative pipeline.
Also note that use of try/catch inside each stage is out of question because it would make the pipeline script impossible to read. This is because we have like 10-15 stages which are stored in multiple files and they are compiled using JJB to create the final pipeline script. They are already complex so I need a clean approach on finding which stage failed.
U could also create a custom step in a shared library, a super_stage
Quick example:
// vars/super_stage.groovy
def call(name, body) {
try {
stage(name) {
body()
}
} catch(e) {
register_failed_stage(name, e)
throw e
}
}
In that way you can 'reuse' the same exception handler.
In your scripted pipeline you would then use it like:
super_stage("mystage01") {
//do stuff
}
Source
Use a GraphListener:
def listener = new GraphListener.Synchronous(){
#NonCPS
void onNewHead(FlowNode node){
if (node instanceof StepStartNode){
// before steps execution
}else if (node instanceof StepEndNode){
// after steps execution
}
}
def execution = (FlowExecution) currentBuild.getRawBuild().getExecution()
execution.addListener(listener)
You are going to need a few helper functions in order to make it work, for example StepStartNode and StepEndNode gets called twice so you have to filter the one with the label. Also variables like env are available inside the listener so you can store anything in there to be picked up later.
This answer is pretty generic but I've found that is useful in many of the stackoverflow questions regarding doing something before/after some stage (or in all).
You cannot try/catch exceptions inside the pipeline as this approach is not a wrapper for the stage but just a listener that gets executed once per each line instruction but you can just record the stage at the begining and at the end check currentBuild.result to see if the stage failed. You can do pretty much anything at this point.
At some point with the FlowExecution you have access to the pipeline script, I don't know if it's writtable at that point but it would be awesome to rewrite the pipeline to actually try/catch the stages. If you do something in this line please let me know ;)

How to handle exceptions in a Jenkins Job DSL seed job?

If I had a Git repository full of Job DSL groovy scripts and a typical seed job e.g.:
job('seed') {
//... scm, triggers etc.
steps {
dsl {
external 'jobs/**/*.groovy'
}
}
//... more config etc.
}
what happens if just one of the job dsl scripts throws an exception for some reason, for example:
job('deliberate-fail') {
throw new Exception("Arrrgggghhh")
}
Is it possible to handle this exception in the seed job or will the whole seed job fail?
If all but one would work - is it possible for the seed job to record an UNSTABLE result rather than FAILURE?
I don't really want one bad apple to spoil the bunch.
Based on Opal's suggestion to use a try-catch, I modifed the job to capture the exception and print an error to the console.
job('deliberate-fail') {
try {
throw new Exception("Arrrgggghhh")
} catch (Exception ex){
println("deliberate-fail job is [UNSTABLE]")
}
}
As I am currently using the Job DSL plugin (and not a Jenkins Pipeline script), I don't think Opal's suggestion to use "currentBuild.result = 'UNSTABLE'" was available to me. After a little digging I found I could use the Text-Finder plugin to search the console for the "[UNSTABLE]" error and change the seed job state accordingly.
job('seed-job') {
steps {
dsl {
external '**/*_jobdsl.groovy'
}
}
publishers {
textFinder(/[UNSTABLE]/, '', true, false, true)
}
}
A bit convoluted but it seems to work!

What is the effect of #NonCPS in a Jenkins pipeline script

I have a pipeline script in Jenkins.
I used to get this exception:
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException:
Scripts not permitted to use method groovy.json.JsonSlurperClassic
parseText java.lang.String
I looked the exception up and I found some indications that I should annotate the method where theexception occurs with #NonCPS. I did this, without really understanding what this does.
After that however, an Exception that I was throwing in that method was no longer caught by a try clause.
So what's the idea behind #NonCPS? What are the effects of using it?
The exception that you are seeing is due to script security and sandboxing. Basically, by default, when you run a pipeline script, it runs in a sandbox which only allow usage of certain methods and classes. There are ways to whitelist operations, check the link above.
The #NonCPS annotation is useful when you have methods which use objects which aren't serializable. Normally, all objects that you create in your pipeline script must be serializable (the reason for this is that Jenkins must be able to serialize the state of the script so that it can be paused and stored on disk).
When you put #NonCPS on a method, Jenkins will execute the entire method in one go without the ability to pause. Also, you're not allowed to reference any pipeline steps or CPS transformed methods from within an #NonCPS annotated method. More information about this can be found here.
As for the exception handling: Not 100% sure what you are experiencing; I've tried the following and it works as expected:
#NonCPS
def myFunction() {
throw new RuntimeException();
}
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
and
#NonCPS
def myFunction() {
throw new RuntimeException();
}
def mySecondFunction() {
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
}
mySecondFunction();
and finally:
#NonCPS
def myFunction() {
throw new RuntimeException();
}
#NonCPS
def mySecondFunction() {
try {
myFunction();
} catch (Exception e) {
echo "Caught";
}
}
mySecondFunction();
All print "Caught" as expected.

How can a jenkins plugin get the value of a configuration setting?

I'm creating a Jenkins plugin which is a post-build action. I want the plugin to read the value of the "Root POM" field in the job configuration page. I've been looking through the Javadocs for hudson.model.AbstractBuild and trying getBuildVariables(), getEnvironment() etc. but I don't see anything relevant.
I guess as a last resort I could configure my plugin to prompt the user for the root pom, but the problem is that management wants a plugin that can be deployed automatically on every build without any action on the user's part.
Do you mean you want a plugin to read the configuration of another plugin (the maven one)? If so, I believe you should use something like
Jenkins.getInstance().getDescriptor(RequiredDesc.class);
Your required class might be hudson/maven/Maven3Builder depending on what you are trying to do.
Discussion
Update
I was wrong. This seems to work for me:
if (build instanceof hudson.maven.MavenModuleSetBuild) {
try {
hudson.maven.MavenModuleSetBuild b = (hudson.maven.MavenModuleSetBuild) build;
hudson.EnvVars envVars = b.getEnvironment(listener);
String rootPOM = b.getProject().getRootPOM(envVars);
listener.getLogger().println("rootPOM: " + rootPOM);
} catch (Exception e) {
listener.getLogger().println("ERROR: " + e.getMessage());
}
}

Resources