Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
[JENKINS-26126] Merging #279.
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Jan 15, 2016
2 parents 118b89e + cdfd913 commit 109c985
Show file tree
Hide file tree
Showing 10 changed files with 581 additions and 140 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Expand Up @@ -6,7 +6,7 @@ Only noting significant user changes, not internal code cleanups and minor bug f

* [JENKINS-30055](https://issues.jenkins-ci.org/browse/JENKINS-30055): poor performance and file handle leaks when running a script with an enormous number of steps in quick succession
* [JENKINS-28649](https://issues.jenkins-ci.org/browse/JENKINS-28649): `@ExportedBean` errors when serving some REST API requests.
* [JENKINS-26126](https://issues.jenkins-ci.org/browse/JENKINS-26126) (partial): introspect Workflow steps to generate static reference documentation (link from _Snippet Generator_). Planned to be used for IDE auto-completion as well.
* [JENKINS-26126](https://issues.jenkins-ci.org/browse/JENKINS-26126): introspect Workflow steps to generate static reference documentation (link from _Snippet Generator_); formatting rough, in progress. Initial GDSL for IntelliJ IDEA code completion also available. DSLD for Eclipse in testing.
* [JENKINS-31614](https://issues.jenkins-ci.org/browse/JENKINS-31614): avoiding various deadlocks involving `Queue`.
* [JENKINS-31897](https://issues.jenkins-ci.org/browse/JENKINS-31897): parameters with default values may now be omitted from the `parameters` option to the `build` step.
* [JENKINS-31909](https://issues.jenkins-ci.org/browse/JENKINS-31909): form validation warning about Groovy syntax errors was broken in 1.11; [JENKINS-32067](https://issues.jenkins-ci.org/browse/JENKINS-32067) show these warnings right in the editor.
Expand Down
108 changes: 9 additions & 99 deletions cps/src/main/java/org/jenkinsci/plugins/workflow/cps/Snippetizer.java
Expand Up @@ -28,7 +28,6 @@
import hudson.Functions;
import hudson.model.RootAction;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Collection;
import java.util.Comparator;
Expand All @@ -39,13 +38,11 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.lang.model.SourceVersion;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.structs.DescribableHelper;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.accmod.restrictions.NoExternalUse;
Expand Down Expand Up @@ -231,103 +228,16 @@ public HttpResponse doGenerateSnippet(StaplerRequest req, @QueryParameter String
}

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

@Restricted(DoNotUse.class)
public void doStatic(StaplerRequest req, StaplerResponse rsp) throws Exception {
rsp.setContentType("text/html;charset=UTF-8");
PrintWriter pw = rsp.getWriter();
pw.println("<html><head><title>Jenkins Pipeline Reference</title></head><body>");
pw.println("<h1>Steps</h1>");
for (StepDescriptor d : getStepDescriptors(false)) {
generateStepHelp(d, pw);
}
pw.println("<h1>Advanced/Deprecated Steps</h1>");
for (StepDescriptor d : getStepDescriptors(true)) {
generateStepHelp(d, pw);
}
pw.println("<h1>Variables</h1>");
for (GlobalVariable v : getGlobalVariables()) {
pw.println("<h2><code>" + v.getName() + "</code></h2>");
RequestDispatcher rd = req.getView(v, "help");
if (rd != null) {
pw.println("(help for variables not currently supported here)");
/* TODO RequestDispatcher.include sends that content, but then closes the stream and prevents further output from appearing.
Also ${rootURL} etc. are not set, but no idea what JellyContext to pass to Functions.initPageVariables
Not clear how to fix these issues except by rewriting all of this to be driven from a static.jelly page.
Also need to use new PrintWriter(new OutputStreamWriter(rsp.getOutputStream(), "UTF-8")) and pw.flush() at the end
(cannot use getWriter since RequestDispatcher.include will call getOutputStream).
rd.include(req, rsp);
*/
} else {
pw.println("(no help)");
}
}
pw.println("</body></html>");
}
private static void generateStepHelp(StepDescriptor d, PrintWriter pw) throws Exception {
pw.println("<h2><code>" + d.getFunctionName() + "</code>: " + d.getDisplayName() + "</h2>");
try {
generateHelp(DescribableHelper.schemaFor(d.clazz), pw, 3);
} catch (Exception x) {
pw.println("<pre><code>" + /*Util.escape(Functions.printThrowable(x))*/x + "</code></pre>");
}
}
private static void generateHelp(DescribableHelper.Schema schema, PrintWriter pw, int headerLevel) throws Exception {
String help = schema.getHelp(null);
if (help != null) {
pw.println(help);
} // TODO else could use RequestDispatcher (as in Descriptor.doHelp) to serve template-based help
for (String attr : schema.mandatoryParameters()) {
pw.println("<h" + headerLevel + "><code>" + attr + "</code></h" + headerLevel + ">");
generateAttrHelp(schema, attr, pw, headerLevel);
}
for (String attr : schema.parameters().keySet()) {
if (schema.mandatoryParameters().contains(attr)) {
continue;
}
pw.println("<h" + headerLevel + "><code>" + attr + "</code> (optional)</h" + headerLevel + ">");
generateAttrHelp(schema, attr, pw, headerLevel);
}
}
private static void generateAttrHelp(DescribableHelper.Schema schema, String attr, PrintWriter pw, int headerLevel) throws Exception {
String help = schema.getHelp(attr);
if (help != null) {
pw.println(help);
}
DescribableHelper.ParameterType type = schema.parameters().get(attr);
describeType(type, pw, headerLevel);
}
private static void describeType(DescribableHelper.ParameterType type, PrintWriter pw, int headerLevel) throws Exception {
int nextHeaderLevel = Math.min(6, headerLevel + 1);
if (type instanceof DescribableHelper.AtomicType) {
pw.println("<p><strong>Type:</strong>" + type + "</p>");
} else if (type instanceof DescribableHelper.EnumType) {
pw.println("<p><strong>Values:</strong></p><ul>");
for (String v : ((DescribableHelper.EnumType) type).getValues()) {
pw.println("<li><code>" + v + "</code></li>");
}
pw.println("</ul>");
} else if (type instanceof DescribableHelper.ArrayType) {
pw.println("<p><strong>Array/List</strong></p>");
describeType(((DescribableHelper.ArrayType) type).getElementType(), pw, headerLevel);
} else if (type instanceof DescribableHelper.HomogeneousObjectType) {
pw.println("<p><strong>Nested object</strong></p>");
generateHelp(((DescribableHelper.HomogeneousObjectType) type).getType(), pw, nextHeaderLevel);
} else if (type instanceof DescribableHelper.HeterogeneousObjectType) {
pw.println("<p><strong>Nested choice of objects</strong></p><ul>");
for (Map.Entry<String,DescribableHelper.Schema> entry : ((DescribableHelper.HeterogeneousObjectType) type).getTypes().entrySet()) {
pw.println("<li><code>" + DescribableHelper.CLAZZ + ": '" + entry.getKey() + "'</code></li>");
generateHelp(entry.getValue(), pw, nextHeaderLevel);
}
pw.println("</ul>");
} else if (type instanceof DescribableHelper.ErrorType) {
Exception x = ((DescribableHelper.ErrorType) type).getError();
pw.println("<pre><code>" + /*Util.escape(Functions.printThrowable(x))*/x + "</code></pre>");
} else {
assert false : type;
}
}
@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
Expand Down
Expand Up @@ -50,6 +50,7 @@ THE SOFTWARE.
</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: -->
Expand Down Expand Up @@ -94,8 +95,5 @@ THE SOFTWARE.
</f:dropdownListBlock>
</j:forEach>
</f:dropdownList>
<f:block>
<a href="${rootURL}/${it.STATIC_URL}" target="_blank">Static documentation</a>
</f:block>
</f:optionalBlock>
</j:jelly>
@@ -0,0 +1,29 @@
package org.jenkinsci.plugins.workflow.cps.Snippetizer

import org.jenkinsci.plugins.workflow.cps.Snippetizer


def st = namespace("jelly:stapler")

p {
a(href: "${rootURL}/${Snippetizer.DSL_REF_URL}", target: "_blank") {
raw(_("Click here for the reference in a new window."))
}

}

p {
a(href: "${rootURL}/${Snippetizer.GDSL_URL}", target: "_blank") {
raw(_("Click here for IntelliJ GDSL."))
}

}

/* Commenting out DSLD until it's fixed.
p {
a(href: "${rootURL}/${Snippetizer.DSLD_URL}", target: "_blank") {
raw(_("Click here for Eclipse DSLD."))
}
}
*/
@@ -0,0 +1,31 @@
package org.jenkinsci.plugins.workflow.cps.Snippetizer

import org.jenkinsci.plugins.workflow.cps.Snippetizer


def l = namespace(lib.LayoutTagLib)
def st = namespace("jelly:stapler")

l.layout(title:_("Jenkins Pipeline Reference")) {

st.include(page: "sidepanel", it: app)
l.main_panel {
p {
a(href: "${rootURL}/${Snippetizer.GDSL_URL}", target: "_blank") {
raw(_("Click here for IntelliJ GDSL."))
}

}

/* Commenting out DSLD until it's fixed.
p {
a(href: "${rootURL}/${Snippetizer.DSLD_URL}", target: "_blank") {
raw(_("Click here for Eclipse DSLD."))
}
}
*/
st.include(page: "dslReferenceContent")
}
}

@@ -0,0 +1,151 @@
package org.jenkinsci.plugins.workflow.cps.Snippetizer

import org.jenkinsci.plugins.workflow.cps.GlobalVariable
import org.jenkinsci.plugins.workflow.cps.Snippetizer
import org.jenkinsci.plugins.workflow.steps.StepDescriptor
import org.jenkinsci.plugins.workflow.structs.DescribableHelper

import javax.servlet.RequestDispatcher

Snippetizer snippetizer = my;

def l = namespace(lib.LayoutTagLib)
def st = namespace("jelly:stapler")


h1(_("Steps"))
for (StepDescriptor d : snippetizer.getStepDescriptors(false)) {
generateStepHelp(d);
}

h1(_("Advanced/Deprecated Steps"))
for (StepDescriptor d : snippetizer.getStepDescriptors(true)) {
generateStepHelp(d);
}

h1(_("Variables"))

for (GlobalVariable v : snippetizer.getGlobalVariables()) {
h2 {
code(v.getName())
}
RequestDispatcher rd = request.getView(v, "help");
div(class:"help", style:"display: block") {
if (rd != null) {
st.include(page: "help", it: v)
} else {
raw("(no help)")
}
}
}


def generateStepHelp(StepDescriptor d) throws Exception {
return {
h2 {
code(d.getFunctionName())
raw(": ${d.getDisplayName()}")
}
try {
generateHelp(DescribableHelper.schemaFor(d.clazz), 3);
} catch (Exception x) {
pre {
code(x)
}
}
}.call()
}

def generateHelp(DescribableHelper.Schema schema, int headerLevel) throws Exception {
return {
String help = schema.getHelp(null);
if (help != null && !help.equals("")) {
div(class:"help", style:"display: block") {
raw(help)
}
} // TODO else could use RequestDispatcher (as in Descriptor.doHelp) to serve template-based help
for (String attr : schema.mandatoryParameters()) {
"h${headerLevel}" {
code(attr)
}
generateAttrHelp(schema, attr, headerLevel);
}
for (String attr : schema.parameters().keySet()) {
if (schema.mandatoryParameters().contains(attr)) {
continue;
}
"h${headerLevel}" {
code(attr)
raw(" (optional)")
}
generateAttrHelp(schema, attr, headerLevel);
}
}.call()
}

def generateAttrHelp(DescribableHelper.Schema schema, String attr, int headerLevel) throws Exception {
return {
String help = schema.getHelp(attr);
if (help != null && !help.equals("")) {
div(class:"help", style:"display: block") {
raw(help)
}
}
DescribableHelper.ParameterType type = schema.parameters().get(attr);
describeType(type, headerLevel);
}.call()
}

def describeType(DescribableHelper.ParameterType type, int headerLevel) throws Exception {
return {
int nextHeaderLevel = Math.min(6, headerLevel + 1);
if (type instanceof DescribableHelper.AtomicType) {
p {
strong(_("Type:"))
text(type)
}
} else if (type instanceof DescribableHelper.EnumType) {
p {
strong(_("Values:"))
}

ul {
for (String v : ((DescribableHelper.EnumType) type).getValues()) {
li {
code(v)
}
}
}
} else if (type instanceof DescribableHelper.ArrayType) {
p {
strong(_("Array/List"))
}
describeType(((DescribableHelper.ArrayType) type).getElementType(), headerLevel)
} else if (type instanceof DescribableHelper.HomogeneousObjectType) {
p {
strong(_("Nested object"))
}
generateHelp(((DescribableHelper.HomogeneousObjectType) type).getSchemaType(), nextHeaderLevel);
} else if (type instanceof DescribableHelper.HeterogeneousObjectType) {
p {
strong(_("Nested choice of objects"))
}

ul {
for (Map.Entry<String, DescribableHelper.Schema> entry : ((DescribableHelper.HeterogeneousObjectType) type).getTypes().entrySet()) {
li {
code(DescribableHelper.CLAZZ + ": '" + entry.getKey() + "'")
}
generateHelp(entry.getValue(), nextHeaderLevel);
}
}
} else if (type instanceof DescribableHelper.ErrorType) {
Exception x = ((DescribableHelper.ErrorType) type).getError();
pre {
code(x)
}
} else {
assert false: type;
}
}.call()
}

0 comments on commit 109c985

Please sign in to comment.