Skip to content

Commit

Permalink
Merge pull request #250 from abayer/directive-generator
Browse files Browse the repository at this point in the history
[JENKINS-49893] Add a Declarative directive snippet generator
  • Loading branch information
abayer committed Apr 5, 2018
2 parents fa8ed8b + 34485e8 commit a9fbdce
Show file tree
Hide file tree
Showing 110 changed files with 4,797 additions and 53 deletions.
Expand Up @@ -135,13 +135,17 @@ public String toGroovy() {
* @return An indented string of Groovy, suitable for use in a Jenkinsfile.
*/
public String toPrettyGroovy() {
return toIndentedGroovy(toGroovy());
}

public static String toIndentedGroovy(@Nonnull String orig) {
StringBuilder result = new StringBuilder();

int indentCount = 0;
boolean tripleSingleQuotedString = false;

boolean first = true;
for (String r : toGroovy().split("\n")) {
for (String r : orig.split("\n")) {
if (first) {
first = false;
} else {
Expand All @@ -153,12 +157,12 @@ public String toPrettyGroovy() {
if (r.startsWith("}") || r.startsWith(")") || r.startsWith("]")) {
indentCount--;
}

result.append(indent(indentCount)).append(r);
if (!StringUtils.isEmpty(r)) {
result.append(indent(indentCount)).append(r);
}
if (r.endsWith("{") || r.endsWith("(") || r.endsWith("[")) {
indentCount++;
}

}

int index = r.indexOf("\'\'\'");
Expand Down Expand Up @@ -200,7 +204,7 @@ public void removeSourceLocation() {
}
}

private String indent(int count) {
private static String indent(int count) {
return StringUtils.repeat(" ", count);
}

Expand Down
Expand Up @@ -30,6 +30,8 @@
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;


public class Any extends DeclarativeAgent<Any> {

Expand All @@ -39,5 +41,10 @@ public Any() {

@Extension(ordinal = -900) @Symbol("any")
public static class DescriptorImpl extends DeclarativeAgentDescriptor<Any> {
@Override
@Nonnull
public String getDisplayName() {
return "Run on any agent";
}
}
}
Expand Up @@ -25,11 +25,15 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl;

import hudson.Extension;
import hudson.Util;
import hudson.util.FormValidation;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.AbstractDockerAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import javax.annotation.Nonnull;

Expand Down Expand Up @@ -57,5 +61,18 @@ public boolean isAlwaysPull() {

@Extension(ordinal = 1000) @Symbol("docker")
public static class DescriptorImpl extends DeclarativeAgentDescriptor<DockerPipeline> {
@Override
@Nonnull
public String getDisplayName() {
return "Run inside a Docker container";
}

public FormValidation doCheckImage(@QueryParameter String image) {
if (StringUtils.isEmpty(Util.fixEmptyAndTrim(image))) {
return FormValidation.error("Image is required.");
} else {
return FormValidation.ok();
}
}
}
}
Expand Up @@ -106,5 +106,10 @@ public String getDockerfileAsString() {

@Extension(ordinal = 999) @Symbol("dockerfile")
public static class DescriptorImpl extends DeclarativeAgentDescriptor<DockerPipelineFromDockerfile> {
@Override
@Nonnull
public String getDisplayName() {
return "Build a Dockerfile and run in a container using that image";
}
}
}
Expand Up @@ -25,13 +25,18 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.agent.impl;

import hudson.Extension;
import hudson.Util;
import hudson.util.FormValidation;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class Label extends DeclarativeAgent<Label> {
Expand Down Expand Up @@ -60,5 +65,18 @@ public void setCustomWorkspace(String customWorkspace) {

@Extension(ordinal = -800) @Symbol({"label","node"})
public static class DescriptorImpl extends DeclarativeAgentDescriptor<Label> {
@Override
@Nonnull
public String getDisplayName() {
return "Run on an agent matching a label";
}

public FormValidation doCheckLabel(@QueryParameter String label) {
if (StringUtils.isEmpty(Util.fixEmptyAndTrim(label))) {
return FormValidation.error("Label is required.");
} else {
return FormValidation.ok();
}
}
}
}
Expand Up @@ -30,6 +30,8 @@
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;


public class None extends DeclarativeAgent<None> {

Expand All @@ -40,5 +42,10 @@ public None() {

@Extension(ordinal = -1000) @Symbol("none")
public static class DescriptorImpl extends DeclarativeAgentDescriptor<None> {
@Override
@Nonnull
public String getDisplayName() {
return "Don't run on an agent";
}
}
}
@@ -0,0 +1,43 @@
/*
* The MIT License
*
* Copyright (c) 2018, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.modeldefinition.generator;

import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;

public abstract class AbstractDirective<T extends AbstractDirective<T>> extends AbstractDescribableImpl<T> {
public final String toGroovy(boolean indent) {
Descriptor d = getDescriptor();
if (d instanceof DirectiveDescriptor) {
if (indent) {
return ((DirectiveDescriptor) d).toIndentedGroovy(this);
} else {
return ((DirectiveDescriptor) d).toGroovy(this);
}
} else {
return "// Not a valid descriptor";
}
}
}
@@ -0,0 +1,117 @@
/*
* The MIT License
*
* Copyright (c) 2018, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.pipeline.modeldefinition.generator;

import hudson.Extension;
import hudson.model.Descriptor;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent;
import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor;
import org.jenkinsci.plugins.structs.SymbolLookup;
import org.jenkinsci.plugins.structs.describable.DescribableModel;
import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable;
import org.jenkinsci.plugins.workflow.cps.Snippetizer;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;

public class AgentDirective extends AbstractDirective<AgentDirective> {
private DeclarativeAgent agent;

@DataBoundConstructor
public AgentDirective(DeclarativeAgent agent) {
this.agent = agent;
}

public DeclarativeAgent getAgent() {
return agent;
}

@Extension
public static class DescriptorImpl extends DirectiveDescriptor<AgentDirective> {
@Override
@Nonnull
public String getName() {
return "agent";
}

@Override
@Nonnull
public String getDisplayName() {
return "Agent";
}

@Override
@Nonnull
public List<Descriptor> getDescriptors() {
List<Descriptor> descriptors = new ArrayList<>();
for (DeclarativeAgentDescriptor td : DeclarativeAgentDescriptor.all()) {
if (!SymbolLookup.getSymbolValue(td).isEmpty()) {
descriptors.add(td);
}
}

return descriptors;
}

@Override
@Nonnull
public String toGroovy(@Nonnull AgentDirective directive) {
if (directive.agent != null) {
DeclarativeAgentDescriptor desc = directive.agent.getDescriptor();

UninstantiatedDescribable ud = UninstantiatedDescribable.from(directive.agent);
DescribableModel model = ud.getModel();
if (model != null) {
StringBuilder result = new StringBuilder();
if (DeclarativeAgentDescriptor.zeroArgModels().containsKey(desc.getName())) {
// agent none or agent any
result.append("agent ").append(desc.getName());
} else {
result.append("agent {\n");
if (DeclarativeAgentDescriptor.noRequiredArgsModels().containsKey(desc.getName()) &&
ud.getArguments().entrySet().stream().allMatch(e -> e.getValue() == null
|| (e.getValue() instanceof String && e.getValue().equals("")))) {
// agent { dockerfile true }
result.append(desc.getName()).append(" true\n");
} else if (model.hasSingleRequiredParameter() && ud.getArguments().size() == 1) {
// agent { label 'foo' } or agent { docker 'image' }
result.append(Snippetizer.object2Groovy(ud)).append("\n");
} else {
// Multiple arguments etc
result.append(desc.getName()).append(" ").append(DirectiveGenerator.mapToClosure(ud.getArguments()));
}
result.append("}");
}
result.append("\n");
return result.toString();
}
}

return "// No valid agent defined\n";
}
}
}

0 comments on commit a9fbdce

Please sign in to comment.