Skip to content

Commit

Permalink
Merge branch 'master' into JENKINS-34064
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed May 31, 2016
2 parents a41aa9b + 632bc78 commit 272fc16
Show file tree
Hide file tree
Showing 18 changed files with 298 additions and 216 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -33,7 +33,7 @@
</parent>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.3-SNAPSHOT</version>
<version>2.5-SNAPSHOT</version>
<packaging>hpi</packaging>
<name>Pipeline: Groovy</name>
<url>https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Groovy+Plugin</url>
Expand Down Expand Up @@ -69,7 +69,7 @@
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-step-api</artifactId>
<version>1.15</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
Expand Up @@ -152,7 +152,7 @@ public class CpsStepContext extends DefaultStepContext { // TODO add XStream cla
*
* This is the implicit closure block passed to the step invocation.
*/
private BodyReference body;
private @CheckForNull BodyReference body;

private final int threadId;

Expand All @@ -174,12 +174,12 @@ public class CpsStepContext extends DefaultStepContext { // TODO add XStream cla
private transient volatile boolean loadingThreadGroup;

@CpsVmThreadOnly
CpsStepContext(StepDescriptor step, CpsThread thread, FlowExecutionOwner executionRef, FlowNode node, Closure body) {
CpsStepContext(StepDescriptor step, CpsThread thread, FlowExecutionOwner executionRef, FlowNode node, @CheckForNull Closure body) {
this.threadId = thread.id;
this.executionRef = executionRef;
this.id = node.getId();
this.node = node;
this.body = thread.group.export(body);
this.body = body != null ? thread.group.export(body) : null;
this.stepDescriptorId = step.getId();
}

Expand Down Expand Up @@ -269,14 +269,19 @@ public String getDisplayName() {
}
}

@Override public boolean hasBody() {
return body != null;
}

@Override
public CpsBodyInvoker newBodyInvoker() {
if (body == null) {
throw new IllegalStateException("There is no body to invoke");
}
return newBodyInvoker(body);
}

public CpsBodyInvoker newBodyInvoker(BodyReference body) {
if (body==null)
throw new IllegalStateException("There's no body to invoke");
public @Nonnull CpsBodyInvoker newBodyInvoker(@Nonnull BodyReference body) {
return new CpsBodyInvoker(this,body);
}

Expand Down
Expand Up @@ -60,6 +60,7 @@
import java.util.logging.Logger;

import static java.util.logging.Level.*;
import javax.annotation.Nonnull;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import static org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.*;
import static org.jenkinsci.plugins.workflow.cps.persistence.PersistenceContext.*;
Expand Down Expand Up @@ -149,17 +150,15 @@ public CpsThread getThread(int id) {
}

@CpsVmThreadOnly("root")
public BodyReference export(Closure body) {
public @Nonnull BodyReference export(@Nonnull Closure body) {
assertVmThread();
if (body==null) return null;
int id = iota++;
closures.put(id, body);
return new StaticBodyReference(id,body);
}

@CpsVmThreadOnly("root")
public BodyReference export(final Script body) {
if (body==null) return null;
public @Nonnull BodyReference export(@Nonnull final Script body) {
return export(new Closure(null) {
@Override
public Object call() {
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,7 @@
<f:entry field="sandbox">
<f:checkbox title="${%Use Groovy Sandbox}" default="true"/>
</f:entry>
<st:include it="${descriptor.snippetizer}" page="block.jelly"/>
<f:block>
<a href="pipeline-syntax" target="_blank">${%Pipeline Syntax}</a>
</f:block>
</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,7 @@ THE SOFTWARE.
<f:entry field="scriptPath" title="${%Script Path}">
<f:textbox default="Jenkinsfile"/>
</f:entry>
<st:include it="${descriptor.snippetizer}" page="block.jelly"/>
<f:block>
<a href="pipeline-syntax" target="_blank">${%Pipeline Syntax}</a>
</f:block>
</j:jelly>

0 comments on commit 272fc16

Please sign in to comment.