Skip to content

Commit

Permalink
Merge pull request #5 from ikedam/feature/JENKINS-17875_VariablesInGr…
Browse files Browse the repository at this point in the history
…oovy

[JENKINS-17875] Referencing project from System Groovy Script.
  • Loading branch information
ikedam committed Dec 8, 2013
2 parents fbbbd48 + 9bf438c commit df8a84e
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 28 deletions.
Expand Up @@ -27,9 +27,11 @@
import java.util.List;
import java.util.regex.Pattern;

import jenkins.model.Jenkins;
import hudson.Extension;
import hudson.DescriptorExtensionList;
import hudson.model.Descriptor;
import hudson.model.Describable;
import hudson.model.ParameterValue;
import hudson.model.StringParameterValue;
import hudson.model.SimpleParameterDefinition;
Expand Down Expand Up @@ -79,6 +81,73 @@ public static Pattern getNamePattern()
@Extension
public static class DescriptorImpl extends ParameterDescriptor
{
/**
* Create a new instance of {@link SystemGroovyChoiceListProvider} from user inputs.
*
* @param req
* @param formData
* @return
* @throws hudson.model.Descriptor.FormException
* @see hudson.model.Descriptor#newInstance(org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject)
*/
@Override
public ExtensibleChoiceParameterDefinition newInstance(StaplerRequest req,
JSONObject formData)
throws hudson.model.Descriptor.FormException
{
return new ExtensibleChoiceParameterDefinition(
formData.getString("name"),
bindJSONWithDescriptor(req, formData, "choiceListProvider", ChoiceListProvider.class),
formData.getBoolean("editable"),
formData.getString("description")
);
}

/**
* Create a new {@link Describable} object from user inputs.
*
* @param req
* @param formData
* @param fieldName
* @param clazz
* @return
* @throws hudson.model.Descriptor.FormException
*/
private <T extends Describable<?>> T bindJSONWithDescriptor(
StaplerRequest req,
JSONObject formData,
String fieldName,
Class<T> clazz
) throws hudson.model.Descriptor.FormException {
formData = formData.getJSONObject(fieldName);
if (formData == null || formData.isNullObject()) {
return null;
}
if (!formData.has("stapler-class")) {
throw new FormException("No stapler-class is specified", fieldName);
}
String staplerClazzName = formData.getString("stapler-class");
if (staplerClazzName == null) {
throw new FormException("No stapler-class is specified", fieldName);
}
try {
@SuppressWarnings("unchecked")
Class<? extends T> staplerClass = (Class<? extends T>)Jenkins.getInstance().getPluginManager().uberClassLoader.loadClass(staplerClazzName);
Descriptor<?> d = Jenkins.getInstance().getDescriptorOrDie(staplerClass);

@SuppressWarnings("unchecked")
T instance = (T)d.newInstance(req, formData);

return instance;
} catch(ClassNotFoundException e) {
throw new FormException(
String.format("Failed to instantiate %s", staplerClazzName),
e,
fieldName
);
}
}

/**
* Returns the string to be shown in a job configuration page, in the dropdown of &quot;Add Parameter&quot;.
*
Expand Down
Expand Up @@ -26,6 +26,7 @@
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.Descriptor;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
Expand All @@ -37,11 +38,14 @@
import java.util.logging.Logger;

import jenkins.model.Jenkins;
import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;

/**
* A choice provider whose choices are determined by a Groovy script.
Expand All @@ -67,6 +71,28 @@ public class SystemGroovyChoiceListProvider extends ChoiceListProvider implement
@Extension
public static class DescriptorImpl extends Descriptor<ChoiceListProvider>
{
/**
* Create a new instance of {@link SystemGroovyChoiceListProvider} from user inputs.
*
* @param req
* @param formData
* @return
* @throws hudson.model.Descriptor.FormException
* @see hudson.model.Descriptor#newInstance(org.kohsuke.stapler.StaplerRequest, net.sf.json.JSONObject)
*/
@Override
public SystemGroovyChoiceListProvider newInstance(StaplerRequest req, JSONObject formData)
throws hudson.model.Descriptor.FormException
{
SystemGroovyChoiceListProvider provider = (SystemGroovyChoiceListProvider)super.newInstance(req, formData);
if(provider.isUsePredefinedVariables())
{
// set project only when variables is requested.
provider.setProject(req.findAncestorObject(AbstractProject.class));
}
return provider;
}

/**
* the display name shown in the dropdown to select a choice provider.
*
Expand All @@ -82,18 +108,26 @@ public String getDisplayName()
/**
* Returns the selection of a default choice.
*
* @param choiceListText
* @param req null passed in tests.
* @param scriptText
* @return the selection of a default choice
*/
public ListBoxModel doFillDefaultChoiceItems(@QueryParameter String scriptText)
public ListBoxModel doFillDefaultChoiceItems(StaplerRequest req, @QueryParameter String scriptText, @QueryParameter boolean usePredefinedVariables)
{
ListBoxModel ret = new ListBoxModel();
ret.add(Messages.ExtensibleChoiceParameterDefinition_NoDefaultChoice(), NoDefaultChoice);

List<String> choices = null;
AbstractProject<?,?> project = null;

if(usePredefinedVariables && req != null)
{
project = req.findAncestorObject(AbstractProject.class);
}

try
{
choices = runScript(scriptText);
choices = runScript(scriptText, usePredefinedVariables, project);
}
catch(Exception e)
{
Expand All @@ -111,12 +145,19 @@ public ListBoxModel doFillDefaultChoiceItems(@QueryParameter String scriptText)
return ret;
}

public FormValidation doTest(@QueryParameter String scriptText)
public FormValidation doTest(StaplerRequest req, @QueryParameter String scriptText, @QueryParameter boolean usePredefinedVariables)
{
List<String> choices = null;
AbstractProject<?,?> project = null;

if(usePredefinedVariables && req != null)
{
project = req.findAncestorObject(AbstractProject.class);
}

try
{
choices = runScript(scriptText);
choices = runScript(scriptText, usePredefinedVariables, project);
}
catch(Exception e)
{
Expand All @@ -142,9 +183,19 @@ public FormValidation doTest(@QueryParameter String scriptText)
public List<String> getChoiceList()
{
List<String> ret = null;
AbstractProject<?,?> project = getProject();
if(isUsePredefinedVariables() && project == null)
{
// try to retrieve from current request.
if(Stapler.getCurrentRequest() != null)
{
project = Stapler.getCurrentRequest().findAncestorObject(AbstractProject.class);
}
}

try
{
ret = runScript(getScriptText());
ret = runScript(getScriptText(), isUsePredefinedVariables(), project);
}
catch(Exception e)
{
Expand All @@ -153,7 +204,7 @@ public List<String> getChoiceList()
return (ret != null)?ret:new ArrayList<String>(0);
}

private static List<String> runScript(String scriptText) {
private static List<String> runScript(String scriptText, boolean usePredefinedVariables, AbstractProject<?,?> project) {
CompilerConfiguration compilerConfig = new CompilerConfiguration();

// see RemotingDiagnostics.Script
Expand All @@ -163,8 +214,14 @@ private static List<String> runScript(String scriptText) {
cl = Thread.currentThread().getContextClassLoader();
}

Binding binding = new Binding();
if(usePredefinedVariables)
{
binding.setVariable("jenkins", Jenkins.getInstance());
binding.setVariable("project", project);
}
GroovyShell shell =
new GroovyShell(cl, new Binding(), compilerConfig);
new GroovyShell(cl, binding, compilerConfig);

Object out = shell.evaluate(scriptText);
if(out == null)
Expand All @@ -184,7 +241,7 @@ private static List<String> runScript(String scriptText) {
}
return ret;
}
private String scriptText;
private final String scriptText;

/**
* The list of choices, joined into a string.
Expand All @@ -198,7 +255,7 @@ public String getScriptText()
return scriptText;
}

private String defaultChoice = null;
private final String defaultChoice;

/**
* Returns the default choice.
Expand All @@ -220,11 +277,55 @@ public String getDefaultChoice()
* and no constructor is used.
*
* @param scriptText the text where choices are written in each line.
* @param defaultChoice
* @param usePredefinedVariables
*/
@DataBoundConstructor
public SystemGroovyChoiceListProvider(String scriptText, String defaultChoice)
public SystemGroovyChoiceListProvider(String scriptText, String defaultChoice, boolean usePredefinedVariables)
{
this.scriptText = scriptText;
this.defaultChoice = (!NoDefaultChoice.equals(defaultChoice))?defaultChoice:null;
this.usePredefinedVariables = usePredefinedVariables;
}

public SystemGroovyChoiceListProvider(String scriptText, String defaultChoice)
{
this(scriptText, defaultChoice, false);
}

private final boolean usePredefinedVariables;

/**
* @return whether to use predefined variables
*/
public boolean isUsePredefinedVariables()
{
return usePredefinedVariables;
}

/**
* The project of this is configured in.
* This will be stored in job configuration XML like
* &lt;project class=&quot;project&quot; reference=&quot;../../../../../..&quot; /&gt;
*/
private AbstractProject<?,?> project;

/**
* @param project
*/
protected void setProject(AbstractProject<?,?> project)
{
this.project = project;
}

/**
* Return the project where this is configured.
* This is set only when {@link SystemGroovyChoiceListProvider#isUsePredefinedVariables()} is true.
*
* @return project
*/
protected AbstractProject<?,?> getProject()
{
return project;
}
}
Expand Up @@ -25,11 +25,14 @@ THE SOFTWARE.
<f:entry title="${%Groovy System Script}" field="scriptText">
<f:expandableTextbox />
</f:entry>
<f:entry field="usePredefinedVariables">
<f:checkbox title="${%Use predefined variables in scripts}" />
</f:entry>
<f:validateButton
method="test"
title="${%Run the Script Now}"
progress="${%Running...}"
with="scriptText"
with="scriptText,usePredefinedVariables"
/>
<f:entry title="${%Default Choice}" field="defaultChoice" help="/plugin/extensible-choice-parameter/help/defaultChoice.html">
<f:select />
Expand Down
Expand Up @@ -28,4 +28,7 @@ Default\ Choice=\u30c7\u30d5\u30a9\u30eb\u30c8\u5024
Run\ the\ Script\ Now=\u30b9\u30af\u30ea\u30d7\u30c8\u306e\u30c6\u30b9\u30c8\u5b9f\u884c
# Running...=処理中
Running...=\u51e6\u7406\u4e2d
# Use\ predefined\ variables\ in\ scripts=予約変数を使用する
Use\ predefined\ variables\ in\ scripts=\u4e88\u7d04\u5909\u6570\u3092\u4f7f\u7528\u3059\u308b


@@ -0,0 +1,9 @@
<div>
<p>Enable following pre-defined variables:</p>
<dl>
<dt>jenkins</dt>
<dd>The instance of <a href="http://javadoc.jenkins-ci.org/jenkins/model/Jenkins.html">Jenkins</a>. Just a short cut for Jenkins.getInstance().</dd>
<dt>project</dt>
<dd>The project. An instance of <a href="http://javadoc.jenkins-ci.org/hudson/model/AbstractProject.html">AbstractProject</a>.</dd>
</dl>
</div>
@@ -0,0 +1,9 @@
<div>
<p>スクリプト内で以下の変数が使用できるようになります。</p>
<dl>
<dt>jenkins</dt>
<dd><a href="http://javadoc.jenkins-ci.org/jenkins/model/Jenkins.html">Jenkins</a> のインスタンス。 Jenkins.getInstance() のショートカットです。</dd>
<dt>project</dt>
<dd>対象のプロジェクト。<a href="http://javadoc.jenkins-ci.org/hudson/model/AbstractProject.html">AbstractProject</a> のインスタンス。</dd>
</dl>
</div>

0 comments on commit df8a84e

Please sign in to comment.