/*
This file is part of Delivery Pipeline Plugin.
Delivery Pipeline Plugin is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Delivery Pipeline Plugin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Delivery Pipeline Plugin.
If not, see <http://www.gnu.org/licenses/>.
*/
package se.diabol.jenkins.workflow;
import static se.diabol.jenkins.pipeline.DeliveryPipelineView.DEFAULT_THEME;
import hudson.Extension;
import hudson.model.Descriptor;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.model.TopLevelItem;
import hudson.model.View;
import hudson.model.ViewDescriptor;
import hudson.model.ViewGroup;
import hudson.util.FormValidation;
import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import se.diabol.jenkins.pipeline.domain.Change;
import se.diabol.jenkins.pipeline.domain.PipelineException;
import se.diabol.jenkins.pipeline.util.JenkinsUtil;
import se.diabol.jenkins.pipeline.util.PipelineUtils;
import se.diabol.jenkins.pipeline.util.ProjectUtil;
import se.diabol.jenkins.workflow.model.Component;
import se.diabol.jenkins.workflow.model.Pipeline;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.servlet.ServletException;
public class WorkflowPipelineView extends View {
public static final int DEFAULT_INTERVAL = 2;
public static final int DEFAULT_NO_OF_PIPELINES = 3;
private static final int MAX_NO_OF_PIPELINES = 50;
private int updateInterval = DEFAULT_INTERVAL;
private int noOfPipelines = DEFAULT_NO_OF_PIPELINES;
private int noOfColumns = 1;
private boolean allowPipelineStart = false;
private boolean showChanges = false;
private String theme = DEFAULT_THEME;
private String project;
private String description = null;
private transient String error;
@DataBoundConstructor
public WorkflowPipelineView(String name) {
super(name);
}
public WorkflowPipelineView(String name, ViewGroup owner) {
super(name, owner);
}
public int getNoOfColumns() {
return noOfColumns;
}
public void setNoOfColumns(int noOfColumns) {
this.noOfColumns = noOfColumns;
}
public int getUpdateInterval() {
//This occurs when the plugin has been updated and as long as the view has not been updated
//Jenkins will set the default value to 0
if (updateInterval == 0) {
updateInterval = DEFAULT_INTERVAL;
}
return updateInterval;
}
public void setUpdateInterval(int updateInterval) {
this.updateInterval = updateInterval;
}
public int getNoOfPipelines() {
return noOfPipelines;
}
public void setNoOfPipelines(int noOfPipelines) {
this.noOfPipelines = noOfPipelines;
}
@Exported
public boolean isAllowPipelineStart() {
return allowPipelineStart;
}
public void setAllowPipelineStart(boolean allowPipelineStart) {
this.allowPipelineStart = allowPipelineStart;
}
public boolean isShowChanges() {
return showChanges;
}
public void setShowChanges(boolean showChanges) {
this.showChanges = showChanges;
}
public String getTheme() {
return this.theme == null ? DEFAULT_THEME : this.theme;
}
public void setTheme(String theme) {
this.theme = theme;
}
public String getProject() {
return project;
}
public void setProject(String project) {
this.project = project;
}
@Exported
public String getLastUpdated() {
return PipelineUtils.formatTimestamp(System.currentTimeMillis());
}
@Override
@Exported
public String getDescription() {
if (super.description == null) {
setDescription(this.description);
}
return super.description;
}
public void setDescription(String description) {
super.description = description;
this.description = description;
}
@Exported
public String getError() {
return error;
}
@Exported
public List<Component> getPipelines() {
try {
if (project == null) {
return Collections.emptyList();
}
WorkflowJob job = getWorkflowJob(project);
List<Pipeline> pipelines = resolvePipelines(job);
Component component = new Component(job.getName(), job, pipelines);
this.error = null;
return Collections.singletonList(component);
} catch (PipelineException e) {
error = e.getMessage();
return Collections.emptyList();
}
}
private List<Pipeline> resolvePipelines(WorkflowJob job) throws PipelineException {
List<Pipeline> pipelines = new ArrayList<>();
if (job.getBuilds() == null) {
return pipelines;
}
Iterator<WorkflowRun> it = job.getBuilds().iterator();
for (int i = 0; i < noOfPipelines && it.hasNext(); i++) {
WorkflowRun build = it.next();
Pipeline pipeline = resolvePipeline(job, build);
pipelines.add(pipeline);
}
return pipelines;
}
private Pipeline resolvePipeline(WorkflowJob job, WorkflowRun build) throws PipelineException {
Pipeline pipeline = Pipeline.resolve(job, build);
if (showChanges) {
pipeline.setChanges(getChangelog(build));
}
return pipeline;
}
private WorkflowJob getWorkflowJob(final String projectName) throws PipelineException {
WorkflowJob job = jenkins().getItem(projectName, jenkins(), WorkflowJob.class);
if (job == null) {
throw new PipelineException("Failed to resolve job with name: " + projectName);
}
return job;
}
private List<Change> getChangelog(WorkflowRun build) {
return Change.getChanges(build.getChangeSets());
}
@Override
public Collection<TopLevelItem> getItems() {
return (Collection) getOwnerItemGroup().getItems();
}
@Override
public boolean contains(TopLevelItem item) {
return getItems().contains(item);
}
@Override
protected void submit(StaplerRequest req) throws IOException, ServletException, Descriptor.FormException {
req.bindJSON(this, req.getSubmittedForm());
}
@Override
public Item doCreateItem(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if (!isDefault()) {
return getOwner().getPrimaryView().doCreateItem(req, rsp);
} else {
return jenkins().doCreateItem(req, rsp);
}
}
@Extension
public static class DescriptorImpl extends ViewDescriptor {
public ListBoxModel doFillNoOfColumnsItems(@AncestorInPath ItemGroup<?> context) {
ListBoxModel options = new ListBoxModel();
options.add("1", "1");
options.add("2", "2");
options.add("3", "3");
return options;
}
public ListBoxModel doFillProjectItems(@AncestorInPath ItemGroup<?> context) {
return ProjectUtil.fillAllProjects(context, WorkflowJob.class);
}
public ListBoxModel doFillNoOfPipelinesItems(@AncestorInPath ItemGroup<?> context) {
ListBoxModel options = new ListBoxModel();
for (int i = 1; i <= MAX_NO_OF_PIPELINES; i++) {
String opt = String.valueOf(i);
options.add(opt, opt);
}
return options;
}
public FormValidation doCheckUpdateInterval(@QueryParameter String value) {
int valueAsInt;
try {
valueAsInt = Integer.parseInt(value);
} catch (NumberFormatException e) {
return FormValidation.error(e, "Value must be an integer");
}
if (valueAsInt <= 0) {
return FormValidation.error("Value must be greater than 0");
}
return FormValidation.ok();
}
@Nonnull
@Override
public String getDisplayName() {
return "Delivery Pipeline View for Jenkins Pipelines";
}
}
private static Jenkins jenkins() {
return JenkinsUtil.getInstance();
}
}