Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #12 from jglick/Snippetizer-JENKINS-31831
[FIXED JENKINS-31831] Make Snippetizer UI be displayed in a top-level page
  • Loading branch information
jglick committed May 23, 2016
2 parents 891a1bd + 811fcf9 commit 55f8ca7
Show file tree
Hide file tree
Showing 16 changed files with 277 additions and 205 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Expand Up @@ -69,7 +69,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.1-SNAPSHOT</version>
<version>2.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
Expand Down
Expand Up @@ -37,23 +37,12 @@
import org.kohsuke.stapler.DataBoundConstructor;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import jenkins.model.Jenkins;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;

import org.apache.commons.lang.StringUtils;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.codehaus.groovy.control.ProcessingUnit;
import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
import org.codehaus.groovy.syntax.SyntaxException;
import org.jenkinsci.plugins.scriptsecurity.scripts.ApprovalContext;
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
import org.jenkinsci.plugins.scriptsecurity.scripts.languages.GroovyLanguage;
Expand Down Expand Up @@ -119,8 +108,6 @@ public CpsFlowExecution create(FlowExecutionOwner owner, TaskListener listener,
@Extension
public static class DescriptorImpl extends FlowDefinitionDescriptor {

@Inject public Snippetizer snippetizer;

@Override
public String getDisplayName() {
return "Pipeline script";
Expand Down
Expand Up @@ -41,7 +41,6 @@
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import javax.inject.Inject;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.cps.persistence.PersistIn;
import static org.jenkinsci.plugins.workflow.cps.persistence.PersistenceContext.JOB;
Expand Down Expand Up @@ -133,8 +132,6 @@ private String getFilePathSuffix() {

@Extension public static class DescriptorImpl extends FlowDefinitionDescriptor {

@Inject public Snippetizer snippetizer;

@Override public String getDisplayName() {
return "Pipeline script from SCM";
}
Expand Down
76 changes: 50 additions & 26 deletions src/main/java/org/jenkinsci/plugins/workflow/cps/Snippetizer.java
Expand Up @@ -26,10 +26,15 @@

import hudson.Extension;
import hudson.Functions;
import hudson.model.Action;
import hudson.model.Descriptor;
import hudson.model.DescriptorByNameOwner;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.RootAction;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
Expand All @@ -38,8 +43,8 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.SourceVersion;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import jenkins.model.TransientActionFactory;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
Expand All @@ -51,13 +56,11 @@
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

/**
* Takes a {@link Step} as configured through the UI and tries to produce equivalent Groovy code.
* Render using: {@code <st:include it="${theSnippetizerInstance}" page="block.jelly"/>}
*/
@Extension public class Snippetizer implements RootAction {
@Extension public class Snippetizer implements RootAction, DescriptorByNameOwner {

static String object2Groovy(Object o) throws UnsupportedOperationException {
Class<? extends Object> clazz = o.getClass();
Expand Down Expand Up @@ -159,7 +162,7 @@ private static void render(StringBuilder b, Object value) {
}
}

private static final String ACTION_URL = "workflow-cps-snippetizer";
public static final String ACTION_URL = "pipeline-syntax";

@Override public String getUrlName() {
return ACTION_URL;
Expand All @@ -170,9 +173,14 @@ private static void render(StringBuilder b, Object value) {
}

@Override public String getDisplayName() {
// Do not want to add to main Jenkins sidepanel.
return null;
}

@Override public Descriptor getDescriptorByName(String id) {
return Jenkins.getActiveInstance().getDescriptorByName(id);
}

@Restricted(DoNotUse.class)
public Collection<? extends StepDescriptor> getStepDescriptors(boolean advanced) {
TreeSet<StepDescriptor> t = new TreeSet<StepDescriptor>(new StepDescriptorComparator());
Expand All @@ -190,14 +198,6 @@ public Iterable<GlobalVariable> getGlobalVariables() {
return GlobalVariable.ALL;
}

@Restricted(NoExternalUse.class)
public static final String HELP_URL = ACTION_URL + "/help";

@Restricted(DoNotUse.class)
public void doHelp(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
rsp.serveLocalizedFile(req, Snippetizer.class.getResource("Snippetizer/help.html"));
}

@Restricted(NoExternalUse.class)
public static final String GENERATE_URL = ACTION_URL + "/generateSnippet";

Expand Down Expand Up @@ -227,18 +227,6 @@ public HttpResponse doGenerateSnippet(StaplerRequest req, @QueryParameter String
}
}

@Restricted(NoExternalUse.class)
public static final String GDSL_URL = ACTION_URL + "/gdsl";

@Restricted(NoExternalUse.class)
public static final String DSLD_URL = ACTION_URL + "/dsld";

@Restricted(NoExternalUse.class)
public static final String DSL_REF_URL = ACTION_URL + "/dslReference";

@Restricted(NoExternalUse.class)
public static final String DSL_HELP_URL = ACTION_URL + "/dslHelp";

private static class StepDescriptorComparator implements Comparator<StepDescriptor>, Serializable {
@Override
public int compare(StepDescriptor o1, StepDescriptor o2) {
Expand All @@ -247,4 +235,40 @@ public int compare(StepDescriptor o1, StepDescriptor o2) {
private static final long serialVersionUID = 1L;
}

@Restricted(DoNotUse.class)
@Extension public static class PerJobAdder extends TransientActionFactory<Job> {

@Override public Class<Job> type() {
return Job.class;
}

@Override public Collection<? extends Action> createFor(Job target) {
// TODO probably want an API for FlowExecutionContainer or something
if (target.getClass().getName().equals("org.jenkinsci.plugins.workflow.job.WorkflowJob") && target.hasPermission(Item.EXTENDED_READ)) {
return Collections.singleton(new LocalAction());
} else {
return Collections.emptySet();
}
}

}

/**
* May be added to various contexts to offer the Pipeline Groovy link where it is appropriate.
* To use, define a {@link TransientActionFactory} of some kind of {@link Item}.
* If the target {@link Item#hasPermission} {@link Item#EXTENDED_READ},
* return one {@link LocalAction}. Otherwise return an empty set.
*/
public static class LocalAction extends Snippetizer {

@Override public String getDisplayName() {
return "Pipeline Syntax";
}

public String getIconClassName() {
return "icon-help";
}

}

}
Expand Up @@ -30,5 +30,4 @@
<f:entry field="sandbox">
<f:checkbox title="${%Use Groovy Sandbox}" default="true"/>
</f:entry>
<st:include it="${descriptor.snippetizer}" page="block.jelly"/>
</j:jelly>
@@ -1,29 +1,4 @@
<p>
Groovy script defining this Pipeline. A quick example:
</p>
<pre>node {
sh 'echo hello world'
}</pre>
<p>
You can write any <a href="http://groovy-lang.org/learn.html" target="_blank">Groovy code</a> you need, using Java API calls plus the GDK.
You can also call <a href="http://javadoc.jenkins-ci.org/" target="_blank">Jenkins APIs</a> and APIs in Jenkins plugins.
(If <b>Use Groovy Sandbox</b> is checked, or you are not an administrator, not all APIs will be available.)
</p>
<p>
There are many special build steps available, like <code>node</code> and <code>sh</code> in the example.
Use the <b>Snippet Generator</b> to see them all and see how they should be configured.
Step parameters are given as key-value pairs; if there is just one mandatory parameter the name may be omitted, so
</p>
<pre>stage 'Build'</pre>
<p>
is a shortcut for
</p>
<pre>stage name: 'Build'</pre>
<p>
but if you specify multiple parameters they must all be named:
</p>
<pre>stage name: 'Build', concurrency: 1</pre>
<p>
There are also some predefined variables.
Use the <b>Snippet Generator</b> to see them all with usage information.
Groovy script defining this Pipeline.
Use the <b>Pipeline Groovy</b> link for details.
</p>
Expand Up @@ -29,5 +29,4 @@ THE SOFTWARE.
<f:entry field="scriptPath" title="${%Script Path}">
<f:textbox default="Jenkinsfile"/>
</f:entry>
<st:include it="${descriptor.snippetizer}" page="block.jelly"/>
</j:jelly>
Expand Up @@ -23,77 +23,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->

<!-- Deprecated, but kept for compatibility -->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:f="/lib/form" xmlns:l="/lib/layout" xmlns:d="jelly:define" xmlns:local="local">
<d:taglib uri="local">
<d:tag name="listSteps">
<j:forEach var="stepDescriptor" items="${stepDescriptors}">
<f:dropdownListBlock title="${stepDescriptor.functionName}: ${stepDescriptor.displayName}" staplerClass="${stepDescriptor.clazz.name}" lazy="stepDescriptor,it">
<l:ajax>
<j:set var="instance" value="${null}"/>
<j:set var="descriptor" value="${stepDescriptor}"/>
<j:set var="help" value="${descriptor.helpFile}"/>
<j:if test="${help != null}">
<tr>
<td colspan="3"/>
<f:helpLink url="${help}"/>
</tr>
<f:helpArea/>
</j:if>
<st:include from="${descriptor}" page="${descriptor.configPage}" optional="true">
<f:block>
${%This step has not yet defined any visual configuration.}
</f:block>
</st:include>
</l:ajax>
</f:dropdownListBlock>
</j:forEach>
</d:tag>
</d:taglib>
<f:entry title="Pipeline DSL Reference" help="/${it.DSL_HELP_URL}"/>
<f:optionalBlock name="snippetizer" title="Snippet Generator" help="/${it.HELP_URL}">
<f:section title="Steps"/>
<!-- Similar to f:dropdownDescriptorSelector, but adds fallback content to block, and JENKINS-25130 adds per-selection help: -->
<f:dropdownList name="prototype" title="Sample Step">
<local:listSteps stepDescriptors="${it.getStepDescriptors(false)}"/>
<f:dropdownListBlock title="${%— Advanced/Deprecated —}"/>
<local:listSteps stepDescriptors="${it.getStepDescriptors(true)}"/>
</f:dropdownList>
<j:set var="id" value="${h.generateId()}"/>
<f:block>
<input type="button" value="Generate Groovy" onclick="handlePrototype_${id}(); return false" class="submit-button primary"/>
<f:textarea id="prototypeText_${id}" readonly="true" style="margin-top: 10px" />
<script>
function handlePrototype_${id}() {
buildFormTree(document.forms.config);
// TODO JSON.stringify fails in some circumstances: https://gist.github.com/jglick/70ec4b15c1f628fdf2e9 due to Array.prototype.toJSON
var json = Object.toJSON(JSON.parse(${configFormJS ?: 'document.forms.config'}.elements.json.value).${definitionJS ?: 'definition'}.snippetizer.prototype);
if (!json) {
return; // just a separator
}
new Ajax.Request('${rootURL}/${it.GENERATE_URL}', {
method: 'POST',
parameters: {json: json},
onSuccess: function(r) {
document.getElementById('prototypeText_${id}').value = r.responseText;
}
});
}
</script>
</f:block>
<f:section title="Global variables"/>
<f:dropdownList name="var" title="Variable">
<j:forEach var="var" items="${it.globalVariables}">
<f:dropdownListBlock title="${var.name}" lazy="var,it">
<l:ajax>
<f:block>
<st:include it="${var}" page="help" optional="true">
${%This variable has not supplied a description.}
</st:include>
</f:block>
</l:ajax>
</f:dropdownListBlock>
</j:forEach>
</f:dropdownList>
</f:optionalBlock>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:block>
<a href="${rootURL}/${it.ACTION_URL}" target="_blank">${%Pipeline Syntax}</a>
</f:block>
</j:jelly>

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 55f8ca7

Please sign in to comment.