Jenkins plugin descriptor with nested parameters - jenkins

I'm writing descriptors for somebody else's pipeline steps jenkins plugin. Most steps are straight forward, e.g.
mySimpleStep(param1: value1, param2: value2)
However one of the steps requires a parameter, which is a map of two other values, so the actual call syntax is the following:
myOtherStep(param1: value1, param2: [sub1: value2, sub2: value3])
I can't fathom how to specify the parameters in the config.jelly file for the step and/or update the actual Step class so that the call syntax is created correctly. How can I do that?
(param2 class does have its own #DataBoundConstructor if it matter)
Do note that this is somebody else's plugin, I am in no position to change the actual plugin.

After almost giving up, I stumbled upon the answer while looking at the source code of Microsoft Azure Storage plugin. Here are the steps I needed to do.
Ensure that the class of param2 implements Step and add Description inner class to it. It also needs to have a #DataBoundConstructor
Create a separate descriptor directory for the class in resources with its own config.jelly and help-*.html files
Change the config.jelly of myOtherStep to have something like this:
<f:section title="General">
<f:entry field="value1" title="First param" description="Simple parameter">
<f:textbox/>
</f:entry>
<f:property field="value2">
<st:include page="config.jelly"/>
</f:property>
</f:section>
Now the config.jelly class for the complex parameter will be included - and everything works as expected.

Related

Global shared variables in Jenkins Groovy pipelines

It seems like it's really difficult to be able to store a bunch of variables for use in shared code in Jenkins/Groovy scripted pipelines. I've tried a bunch of methods and none of them seem to give the desired result.
This method looked the most promising, but the values all came back as null in the calling pipeline. Get Global Variables in jenkins pipeline.
My codes is something lie
import org.blabla.JobHelper
println("env.NO_PROXY: -->${env.NO_PROXY}<--")
And in the JobHelper.groovy file, I've defined
package org.blabla.project
env.NO_PROXY = 'localhost,127.0.0.1,169.254.169.254'
the names have been changed a bit to protect the innocent, but you get the idea.
the script just prints null for the value.
Is there a simple way (or indeed any way) that I can pull in a bunch of variables from a shared library file? This feels like it should be a really simple exercise, but after spending many hours searching I'm none the wiser.
In general, env is only available once the pipeline has started, but groovy scripts are resolved much earlier.
I'm using static class members as global variables. Applied to your code sample, it would look like this:
JobHelper.groovy
package org.blabla.project
# Class must be named like the file that contains it.
class JobHelper {
static String getNO_PROXY() { 'localhost,127.0.0.1,169.254.169.254' }
}
Elsewhere:
import org.blabla.project
println("NO_PROXY: -->${JobHelper.NO_PROXY}<--")
Note that Groovy automatically generates properties from get*() and set*() methods, so we can use the short form instead of having to write JobHelper.getNO_PROXY().

How to use dockerhub-notification-plugin in Jenkins scripted pipeline?

I want to trigger a pipeline when a new image is pushed to docker hub.
I installed dockerhub-notification-plugin.
If I use web UI it's possible to specify the docker hub repo:
I tried to use pipeline snippet generator, but it is not working correctly: if I specify a repo it's ignored in generated code.
For example:
generates code:
properties([pipelineTriggers([[$class: 'DockerHubTrigger', options: []]])])
As you can see there is no docker hub repo specified in the generated code.
The correct way to do this is to write your properties like below:
properties([
pipelineTriggers([[$class: 'DockerHubTrigger', options: [[$class: 'TriggerOnSpecifiedImageNames', repoNames: ["YOUR_REPO_NAME"].toSet()]]]])
])
First notice the additional parenthesis around options value. This is due to the way how groovy scripts are evaluated in jenkins.
But why set?
According to the javadoc TriggerOnSpecifiedImageNames class has three constructors: without parameters, with varargs of strings and with collection. But groovy will use reflection to instantiate this class, which means that the default constructor will be called and later respective properties will be applied. And this brings us to the toSet() because as you can see in javadoc there is a setter for repo names property which looks like follow: setRepoNames(Set<String> repoNames).

$class syntax in jenkins scripted dsl

Often when looking at scripted jenkins pipeline code, I see this pattern...
step([$class: 'GitHubSetCommitStatusBuilder',
statusMessage: [content: 'Pipeline Started']])
I have not had any luck finding documentation on this technique and would love it if someone could explain what this is doing and when/why it is useful. I believe this is a way to instantiate and populate the members of an underlying groovy class - but more detail would be appreciated.
Also is this documented anywhere?
Here is a reference that briefly explains the syntax. Basically, you are providing the step() function a map of arguments in the form of name-value pairs. The first argument which is especially denoted by the name $class tells the function which class (plugin) to instantiate.
It also seems that this syntax is being deprecated in favor of shorter syntax, also explained in the same link.
​
I am also struggling with this syntax, but unfortunately have not found any doc yet.
I guess this syntax is used to doing the instance initialization.
All step classes implement interface BuildStep. After script loaded, all step instances initiated, then their perform method are invoked during build procedure.
All above is my conjecture.
There's also another quick reference from "Pipeline: Basic Steps" github repo documentation :
https://github.com/jenkinsci/workflow-basic-steps-plugin/blob/master/CORE-STEPS.md
Syntax
As an example, you can write a Pipeline script:
node {
sh 'make something'
step([$class: 'ArtifactArchiver', artifacts: 'something'])
}
Here we are running the standard Archive the artifacts post-build action (hudson.tasks.ArtifactArchiver), and configuring the Files to archive property (artifacts) to archive our file something produced in an earlier step. The easiest way to see what class and field names to use is to use the Snippet Generator feature in the Pipeline configuration page.
See the compatibility list for the list of currently supported steps.
Simplified syntax
Build steps and post-build actions in Jenkins core as of 2.2, and in some plugins according to their individual changelogs, have defined symbols which allow for a more concise syntax. Snippet Generator will offer this when available. For example, the above script may also be written:
node {
sh 'make something'
archiveArtifacts 'something'
}
NB: The Simplified syntax, makes reference to Plugin Steps parameters

Are there any jenkins plugins for choice parameters that amend themselves after a new entry?

Are there any jenkins plugins for choice parameters that amend themselves after a new entry if the build succeeds?
Basically, something like this:
-Choice Parameter-
Name: All_Choices_So_Far
Choices:
First_Default_Choice
${New_Choice}
So when someone picks ${New_Choice} and fills out the New_Choice field, whatever goes there then does the following, conceptually, not syntactically, as I don't know what the actual syntax would be:
Build Succeeded? If so then,
All_Choices_So_Far.Choices.add(${New_Choice})
All_Choices_So_Far.Choices = All_Choices_So_Far.Choices.uniq
Apply & Save Config
I don't actually care about the workflow being that specific, but that's roughly the functionality that I'm looking for.
Although I've yet to make a Jenkins plugin myself, if this doesn't already exist, I would be interested in how to pursue it.

Jenkins plugin checkbox: GUI value differs from config.xml

I'm making a change to a Jenkins plugin (https://wiki.jenkins-ci.org/display/JENKINS/Stash+pullrequest+builder+plugin), as I would like to add a couple of options to it.
However, having added two new checkboxes to the config.jelly, they don't appear to work correctly in the job config through the GUI.
config.jelly:
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
...
<f:advanced>
...
<f:entry title="Report build started to Stash?" field="reportBuildStartedToStash">
<f:checkbox default="true"/>
</f:entry>
<f:entry title="Report build result to Stash?" field="reportBuildStatusToStash">
<f:checkbox default="true"/>
</f:advanced>
</j:jelly>
If I add this plugin to a job like so:
Then the two checkboxes default to "true" as expected. This is reflected in the config.xml:
<triggers>
<stashpullrequestbuilder.stashpullrequestbuilder.StashBuildTrigger plugin="stash-pullrequest-builder#1.3.1-SNAPSHOT">
...
<reportBuildStartedToStash>true</reportBuildStartedToStash>
<reportBuildStatusToStash>true</reportBuildStatusToStash>
</stashpullrequestbuilder.stashpullrequestbuilder.StashBuildTrigger>
</triggers>
However, if I untick these checkboxes, save the changes, and reload the Job Configuration page, the checkboxes will appear to be ticked again.
Could this be a problem with my .jelly config? I can't understand how or why.
For reference, my branch is here: https://github.com/blaffoy/stash-pullrequest-builder-plugin/tree/optional-messages-to-stash
This issue appears to be same as raised here, but the solution suggested doesn't fix the problem for me. That is, to replace <f:checkbox default="true"/> with <f:checkbox/>
This will work by prefixing the getter methods in StashBuildTrigger.java with "is" e.g.
public boolean isReportBuildStartedToStash()
public boolean isDeleteBuildStartedToStash()
public boolean isReportBuildStatusToStash()
I think this would also work with a "get" prefix but I havent tested it. As per the jenkins plugin doc:
Define getters for the configuration fields, or make the fields
"public final". This allows Jelly script to read the values to
populate the configuration page.
A few things to notice: the 'get' was automatically stripped from the
method name, and the first letter of the remaining method name was
lower-cased. I'd recommend using the Java convention for naming
methods (e.g. starting getters with 'get' and using CamelCase) so that
Jelly can always find the methods.
Example java naming conventions:
Use the prefixes get and set for getter and setter methods. ... If the
method returns a boolean value, use is or has as the prefix for the
method name.

Resources