Skip to content

Commit

Permalink
[JENKINS-40765] add pipeline support (#22)
Browse files Browse the repository at this point in the history
* [JENKINS-40765] add pipeline support

* fix after code review

* remove guice

* allow to schedule release when build is in queue

rewrite SafeParametersAction in order to use parent's parameters field
that allows to benefit on correct execution of ParametersAction#shouldSchedule method

* acquire the lock only when needed

* fix after code review
  • Loading branch information
estarter authored and oleg-nenashev committed Mar 19, 2017
1 parent a06e566 commit 1f9b17f
Show file tree
Hide file tree
Showing 12 changed files with 673 additions and 33 deletions.
42 changes: 39 additions & 3 deletions pom.xml
Expand Up @@ -9,13 +9,13 @@
<artifactId>release</artifactId>
<packaging>hpi</packaging>
<name>Jenkins Release Plugin</name>
<version>2.6.2-SNAPSHOT</version>
<version>2.7-SNAPSHOT</version>
<description>Adds the ability to wrap your job with pre- and post- build steps which are only executed when a manual release build is triggered.</description>
<url>http://wiki.jenkins-ci.org/display/JENKINS/Release+Plugin</url>

<properties>
<jenkins.version>1.609.3</jenkins.version>
<java.level>6</java.level>
<!-- https://issues.jenkins-ci.org/browse/JENKINS-29692 https://issues.jenkins-ci.org/browse/JENKINS-28781 -->
<jenkins.version>1.642.4</jenkins.version>
</properties>

<scm>
Expand Down Expand Up @@ -78,6 +78,42 @@
<version>1.22</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-step-api</artifactId>
<version>2.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-support</artifactId>
<version>2.6</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-cps</artifactId>
<version>2.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>2.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ant</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-basic-steps</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
</dependencies>


Expand Down
36 changes: 6 additions & 30 deletions src/main/java/hudson/plugins/release/SafeParametersAction.java
@@ -1,7 +1,6 @@
package hudson.plugins.release;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.kohsuke.accmod.Restricted;
Expand All @@ -18,38 +17,15 @@
@Restricted(NoExternalUse.class)
public class SafeParametersAction extends ParametersAction {

private final List<ParameterValue> parameters;

/**
* At this point the list of parameter values is guaranteed to be safe, which is
* parameter defined either at top level or release wrapper level.
*
* @param parameters parameters allowed by the job and parameters allowed by release-specific parameters definition
* @since 2.7 - public constructor
*/
SafeParametersAction(List<ParameterValue> parameters) {
this.parameters = parameters;
}

/**
* Returns all parameters allowed by the job (defined as regular job parameters) and
* the parameters allowed by release-specific parameters definition.
*/
@Override
public List<ParameterValue> getParameters() {
return Collections.unmodifiableList(parameters);
}

/**
* Returns the parameter if defined as a regular parameters or it is a release-specific parameter defined
* by the release wrapper.
*/
@Override
public ParameterValue getParameter(String name) {
for (ParameterValue p : parameters) {
if (p == null) continue;
if (p.getName().equals(name)) {
return p;
}
}
return null;
public SafeParametersAction(List<ParameterValue> parameters) {
super(parameters);
}

@Extension
Expand All @@ -59,7 +35,7 @@ public static final class SafeParametersActionEnvironmentContributor extends Env
public void buildEnvironmentFor(Run r, EnvVars envs, TaskListener listener) throws IOException, InterruptedException {
SafeParametersAction action = r.getAction(SafeParametersAction.class);
if (action != null) {
for(ParameterValue p : action.getParameters()) {
for (ParameterValue p : action.getParameters()) {
envs.put(p.getName(), String.valueOf(p.getValue()));
}
}
Expand Down
@@ -0,0 +1,28 @@
package hudson.plugins.release.pipeline;

import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import hudson.AbortException;
import hudson.Extension;
import hudson.model.Queue;
import hudson.model.queue.QueueListener;

/**
* copied from org.jenkinsci.plugins.workflow.support.steps.build.BuildQueueListener
* @since 2.7
*/
@Extension
@Restricted(NoExternalUse.class)
public class ReleaseQueueListener extends QueueListener {
@Override
public void onLeft(Queue.LeftItem li) {
if(li.isCancelled()){
for (ReleaseTriggerAction.Trigger trigger : ReleaseTriggerAction.triggersFor(li)) {
trigger.context.onFailure(new AbortException("Pipeline Release plugin: build of " + li.task.getFullDisplayName() + " was cancelled"));
}
}
}


}
160 changes: 160 additions & 0 deletions src/main/java/hudson/plugins/release/pipeline/ReleaseStep.java
@@ -0,0 +1,160 @@
package hudson.plugins.release.pipeline;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.annotation.Nonnull;

import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.util.StaplerReferer;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

import com.google.common.collect.ImmutableSet;

import hudson.Extension;
import hudson.model.AutoCompletionCandidates;
import hudson.model.BuildableItem;
import hudson.model.BuildableItemWithBuildWrappers;
import hudson.model.ItemGroup;
import hudson.model.Job;
import hudson.model.ParameterDefinition;
import hudson.model.ParameterValue;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.release.ReleaseWrapper;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
* Pipeline step implementation for Release plugin.
*
* @author Alexey Merezhin
* @since 2.7
*/
public class ReleaseStep extends Step {
private static final Logger LOGGER = Logger.getLogger(ReleaseStep.class.getName());

private String job;
@Nonnull
private List<ParameterValue> parameters;

@DataBoundConstructor
public ReleaseStep(String job) {
this.job = job;
parameters = new ArrayList<>();
}

public String getJob() {
return job;
}

public void setJob(String job) {
this.job = job;
}

public List<ParameterValue> getParameters() {
return parameters;
}

@DataBoundSetter
public void setParameters(List<ParameterValue> parameters) {
if (parameters == null) {
this.parameters = new ArrayList<>();
} else {
this.parameters = parameters;
}
}

@Override
public StepExecution start(StepContext stepContext) throws Exception {
return new ReleaseStepExecution(stepContext, this);
}

@Extension
public static class DescriptorImpl extends StepDescriptor {

@Override
public String getFunctionName() {
return "release";
}

@Override
public String getDisplayName() {
return Messages.ReleaseStep_DisplayName();
}

@Override
public Set<Class<?>> getRequiredContext() {
return ImmutableSet.of(Run.class, TaskListener.class);
}

@Override
public Step newInstance(StaplerRequest req, JSONObject formData) throws FormException {
ReleaseStep step = (ReleaseStep) super.newInstance(req, formData);
// Cf. ParametersDefinitionProperty._doBuild:
Object parameter = formData.get("parameter");
JSONArray params = parameter != null ? JSONArray.fromObject(parameter) : null;
if (params != null) {
Jenkins jenkins = Jenkins.getInstance();
Job<?,?> context = StaplerReferer.findItemFromRequest(Job.class);
Job<?,?> job = jenkins != null ? jenkins.getItem(step.getJob(), context, Job.class) : null;

BuildableItem project = Jenkins.getActiveInstance()
.getItem(step.getJob(), context, BuildableItem.class);

if (project == null) {
throw new IllegalArgumentException("Can't find project " + step.getJob());
} else if (project instanceof BuildableItemWithBuildWrappers) {
ReleaseWrapper wrapper = ((BuildableItemWithBuildWrappers) project).getBuildWrappersList()
.get(ReleaseWrapper.class);
if (wrapper == null) {
throw new FormException("Job doesn't have release plugin configuration", "job");
}
List<ParameterDefinition> parameterDefinitions = wrapper.getParameterDefinitions();
if (parameterDefinitions != null) {
List<ParameterValue> values = new ArrayList<>();
for (Object o : params) {
if (o instanceof JSONObject) {
JSONObject jo = (JSONObject) o;
String name = jo.getString("name");
for (ParameterDefinition pd : parameterDefinitions) {
if (name.equals(pd.getName())) {
ParameterValue parameterValue = pd.createValue(req, jo);
values.add(parameterValue);
}
}
}
}
step.setParameters(values);
}
} else {
throw new FormException("Wrong job type: " + project.getClass().getName(), "job");
}
}
return step;
}

public AutoCompletionCandidates doAutoCompleteJob(@AncestorInPath ItemGroup<?> context, @QueryParameter String value) {
return AutoCompletionCandidates.ofJobNames(ParameterizedJobMixIn.ParameterizedJob.class, value, context);
}

@Restricted(DoNotUse.class) // for use from config.jelly / parameters.groovy
public String getContext() {
Job<?,?> job = StaplerReferer.findItemFromRequest(Job.class);
return job != null ? job.getFullName() : null;
}
}
}

0 comments on commit 1f9b17f

Please sign in to comment.