Skip to content

Commit

Permalink
[JENKINS-46785] Strategy to enable / disable publishers in pipelines
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyrille Le Clerc committed Feb 26, 2018
2 parents 586c9f7 + 7e44b1a commit 1d06172
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 145 deletions.
@@ -1,32 +1,16 @@
package org.jenkinsci.plugins.pipeline.maven;

import hudson.DescriptorExtensionList;
import hudson.ExtensionPoint;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.TaskListener;
import jenkins.model.Jenkins;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean2;
import org.apache.commons.beanutils.PropertyUtils;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.kohsuke.stapler.DataBoundSetter;
import org.w3c.dom.Element;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

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

Expand Down Expand Up @@ -105,125 +89,5 @@ public int compareTo(DescriptorImpl o) {
}
}

/**
* <p>Build the list of {@link MavenPublisher}s that should be invoked for the build execution of the given {@link TaskListener}
* with the desired configuration.
* </p>
* <p>
* The desired configuration is based on:
* </p>
* <ul>
* <li>The default configuration of the publishers</li>
* <li>The global configuration of the publishers defined in the "Global Tools Configuration' section</li>
* <li>The configuration specified in the {@code withMaven(options=[...])} step</li>
* </ul>
*
* @param configuredPublishers
* @param listener
*/
@Nonnull
public static List<MavenPublisher> buildPublishersList(@Nonnull List<MavenPublisher> configuredPublishers, @Nonnull TaskListener listener) {

// configuration passed as parameter of "withMaven(options=[...]){}"
// mavenPublisher.descriptor.id -> mavenPublisher
Map<String, MavenPublisher> configuredPublishersById = new HashMap<>();
for (MavenPublisher mavenPublisher : configuredPublishers) {
if (mavenPublisher == null) {
// skip null publisher options injected by Jenkins pipeline, probably caused by a missing plugin
} else {
configuredPublishersById.put(mavenPublisher.getDescriptor().getId(), mavenPublisher);
}
}

// configuration defined globally
Map<String, MavenPublisher> globallyConfiguredPublishersById = new HashMap<>();
GlobalPipelineMavenConfig globalPipelineMavenConfig = GlobalPipelineMavenConfig.get();

List<MavenPublisher> globallyConfiguredPublishers = globalPipelineMavenConfig == null ? Collections.<MavenPublisher>emptyList() : globalPipelineMavenConfig.getPublisherOptions();
if (globallyConfiguredPublishers == null) {
globallyConfiguredPublishers = Collections.emptyList();
}
for (MavenPublisher mavenPublisher : globallyConfiguredPublishers) {
globallyConfiguredPublishersById.put(mavenPublisher.getDescriptor().getId(), mavenPublisher);
}


// mavenPublisher.descriptor.id -> mavenPublisher
Map<String, MavenPublisher> defaultPublishersById = new HashMap<>();
DescriptorExtensionList<MavenPublisher, Descriptor<MavenPublisher>> descriptorList = Jenkins.getInstance().getDescriptorList(MavenPublisher.class);
for (Descriptor<MavenPublisher> descriptor : descriptorList) {
try {
defaultPublishersById.put(descriptor.getId(), descriptor.clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
PrintWriter error = listener.error("[withMaven] Exception instantiation default config for Maven Publisher '" + descriptor.getDisplayName() + "' / " + descriptor.getId() + ": " + e);
e.printStackTrace(error);
error.close();
LOGGER.log(Level.WARNING, "Exception instantiating " + descriptor.clazz + ": " + e, e);
e.printStackTrace();
}
}


if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] Maven Publishers with configuration provided by the pipeline: " + configuredPublishersById.values());
listener.getLogger().println("[withMaven] Maven Publishers with configuration defined globally: " + globallyConfiguredPublishersById.values());
listener.getLogger().println("[withMaven] Maven Publishers with default configuration: " + defaultPublishersById.values());
}

// TODO FILTER
List<MavenPublisher> results = new ArrayList<>();
for (Map.Entry<String, MavenPublisher> entry : defaultPublishersById.entrySet()) {
String publisherId = entry.getKey();
MavenPublisher publisher = buildConfiguredMavenPublisher(
configuredPublishersById.get(publisherId),
globallyConfiguredPublishersById.get(publisherId),
entry.getValue(),
listener);
results.add(publisher);
}
Collections.sort(results);
return results;
}

public static MavenPublisher buildConfiguredMavenPublisher(@Nullable MavenPublisher pipelinePublisher, @Nullable MavenPublisher globallyConfiguredPublisher, @Nonnull MavenPublisher defaultPublisher, @Nonnull TaskListener listener) {

MavenPublisher result;
String logMessage;

if (pipelinePublisher == null && globallyConfiguredPublisher == null) {
result = defaultPublisher;
logMessage = "default";
} else if (pipelinePublisher == null && globallyConfiguredPublisher != null) {
result = globallyConfiguredPublisher;
logMessage = "globally";
} else if (pipelinePublisher != null && globallyConfiguredPublisher == null) {
result = pipelinePublisher;
logMessage = "pipeline";
} else if (pipelinePublisher != null && globallyConfiguredPublisher != null) {
// workaround FindBugs "Bug kind and pattern: NP - NP_NULL_ON_SOME_PATH"
// check pipelinePublisher and globallyConfiguredPublisher are non null even if it is useless

result = pipelinePublisher;
logMessage = "pipeline";
listener.getLogger().println("[withMaven] WARNING merging publisher configuration defined in the 'Global Tool Configuration' and at the pipeline level is not yet supported." +
" Use pipeline level configuration for '" + result.getDescriptor().getDisplayName() + "'");
//
// PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(defaultPublisher);
// for(PropertyDescriptor propertyDescriptor: propertyDescriptors) {
// Method readMethod = propertyDescriptor.getReadMethod();
// Method writeMethod = propertyDescriptor.getWriteMethod();
//
// Object defaultValue = readMethod.invoke(defaultPublisher);
// Object globallyDefinedValue = readMethod.invoke(globallyConfiguredPublisher);
// Object pipelineValue = readMethod.invoke(pipelinePublisher);
// }
} else {
throw new IllegalStateException("Should not happen, workaround for Findbugs NP_NULL_ON_SOME_PATH above");
}

if (LOGGER.isLoggable(Level.FINE))
listener.getLogger().println("[withMaven] Use " + logMessage + " defined publisher for '" + result.getDescriptor().getDisplayName() + "'");
return result;

}
}
@@ -0,0 +1,197 @@
package org.jenkinsci.plugins.pipeline.maven;

import hudson.DescriptorExtensionList;
import hudson.model.Descriptor;
import hudson.model.TaskListener;
import jenkins.model.Jenkins;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

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

/**
* @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
*/
public enum MavenPublisherStrategy {

IMPLICIT("Implicit") {
/**
* <p>Build the list of {@link MavenPublisher}s that should be invoked for the build execution of the given {@link TaskListener}
* with the desired configuration.
* </p>
* <p>
* The desired configuration is based on:
* </p>
* <ul>
* <li>The default configuration of the publishers</li>
* <li>The global configuration of the publishers defined in the "Global Tools Configuration' section</li>
* <li>The configuration specified in the {@code withMaven(options=[...])} step</li>
* </ul>
* @param configuredPublishers
* @param listener
*/
@Nonnull
public List<MavenPublisher> buildPublishersList(@Nonnull List<MavenPublisher> configuredPublishers, @Nonnull TaskListener listener) {

// configuration passed as parameter of "withMaven(options=[...]){}"
// mavenPublisher.descriptor.id -> mavenPublisher
Map<String, MavenPublisher> configuredPublishersById = new HashMap<>();
for (MavenPublisher mavenPublisher : configuredPublishers) {
if (mavenPublisher == null) {
// skip null publisher options injected by Jenkins pipeline, probably caused by a missing plugin
} else {
configuredPublishersById.put(mavenPublisher.getDescriptor().getId(), mavenPublisher);
}
}

// configuration defined globally
Map<String, MavenPublisher> globallyConfiguredPublishersById = new HashMap<>();
GlobalPipelineMavenConfig globalPipelineMavenConfig = GlobalPipelineMavenConfig.get();

List<MavenPublisher> globallyConfiguredPublishers = globalPipelineMavenConfig == null ? Collections.<MavenPublisher>emptyList() : globalPipelineMavenConfig.getPublisherOptions();
if (globallyConfiguredPublishers == null) {
globallyConfiguredPublishers = Collections.emptyList();
}
for (MavenPublisher mavenPublisher : globallyConfiguredPublishers) {
globallyConfiguredPublishersById.put(mavenPublisher.getDescriptor().getId(), mavenPublisher);
}


// mavenPublisher.descriptor.id -> mavenPublisher
Map<String, MavenPublisher> defaultPublishersById = new HashMap<>();
DescriptorExtensionList<MavenPublisher, Descriptor<MavenPublisher>> descriptorList = Jenkins.getInstance().getDescriptorList(MavenPublisher.class);
for (Descriptor<MavenPublisher> descriptor : descriptorList) {
try {
defaultPublishersById.put(descriptor.getId(), descriptor.clazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
PrintWriter error = listener.error("[withMaven] Exception instantiation default config for Maven Publisher '" + descriptor.getDisplayName() + "' / " + descriptor.getId() + ": " + e);
e.printStackTrace(error);
error.close();
LOGGER.log(Level.WARNING, "Exception instantiating " + descriptor.clazz + ": " + e, e);
e.printStackTrace();
}
}


if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] Maven Publishers with configuration provided by the pipeline: " + configuredPublishersById.values());
listener.getLogger().println("[withMaven] Maven Publishers with configuration defined globally: " + globallyConfiguredPublishersById.values());
listener.getLogger().println("[withMaven] Maven Publishers with default configuration: " + defaultPublishersById.values());
}

// TODO FILTER
List<MavenPublisher> results = new ArrayList<>();
for (Map.Entry<String, MavenPublisher> entry : defaultPublishersById.entrySet()) {
String publisherId = entry.getKey();
MavenPublisher publisher = buildConfiguredMavenPublisher(
configuredPublishersById.get(publisherId),
globallyConfiguredPublishersById.get(publisherId),
entry.getValue(),
listener);
results.add(publisher);
}
Collections.sort(results);
return results;
}
},

EXPLICIT("Explicit") {
@Nonnull
@Override
public List<MavenPublisher> buildPublishersList
(@Nonnull List<MavenPublisher> configuredPublishers, @Nonnull TaskListener listener) {

// filter null entries caused by missing plugins
List<MavenPublisher> result = new ArrayList<>();
for(MavenPublisher publisher: configuredPublishers) {
if (publisher != null) {
result.add(publisher);
}
}

if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] Maven Publishers: " + result);
}
return result;
}
};
final String description;

MavenPublisherStrategy(String description) {
this.description = description;
}

public MavenPublisher buildConfiguredMavenPublisher(@Nullable MavenPublisher pipelinePublisher, @Nullable MavenPublisher globallyConfiguredPublisher, @Nonnull MavenPublisher defaultPublisher, @Nonnull TaskListener listener) {

MavenPublisher result;
String logMessage;

if (pipelinePublisher == null && globallyConfiguredPublisher == null) {
result = defaultPublisher;
logMessage = "default";
} else if (pipelinePublisher == null && globallyConfiguredPublisher != null) {
result = globallyConfiguredPublisher;
logMessage = "globally";
} else if (pipelinePublisher != null && globallyConfiguredPublisher == null) {
result = pipelinePublisher;
logMessage = "pipeline";
} else if (pipelinePublisher != null && globallyConfiguredPublisher != null) {
// workaround FindBugs "Bug kind and pattern: NP - NP_NULL_ON_SOME_PATH"
// check pipelinePublisher and globallyConfiguredPublisher are non null even if it is useless

result = pipelinePublisher;
logMessage = "pipeline";
listener.getLogger().println("[withMaven] WARNING merging publisher configuration defined in the 'Global Tool Configuration' and at the pipeline level is not yet supported." +
" Use pipeline level configuration for '" + result.getDescriptor().getDisplayName() + "'");
//
// PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(defaultPublisher);
// for(PropertyDescriptor propertyDescriptor: propertyDescriptors) {
// Method readMethod = propertyDescriptor.getReadMethod();
// Method writeMethod = propertyDescriptor.getWriteMethod();
//
// Object defaultValue = readMethod.invoke(defaultPublisher);
// Object globallyDefinedValue = readMethod.invoke(globallyConfiguredPublisher);
// Object pipelineValue = readMethod.invoke(pipelinePublisher);
// }
} else {
throw new IllegalStateException("Should not happen, workaround for Findbugs NP_NULL_ON_SOME_PATH above");
}

if (LOGGER.isLoggable(Level.FINE))
listener.getLogger().println("[withMaven] Use " + logMessage + " defined publisher for '" + result.getDescriptor().getDisplayName() + "'");
return result;

}

public String getDescription() {
return description;
}

/**
* <p>Build the list of {@link MavenPublisher}s that should be invoked for the build execution of the given {@link TaskListener}
* with the desired configuration.
* </p>
* <p>
* The desired configuration is based on:
* </p>
* <ul>
* <li>The default configuration of the publishers</li>
* <li>The global configuration of the publishers defined in the "Global Tools Configuration' section</li>
* <li>The configuration specified in the {@code withMaven(options=[...])} step</li>
* </ul>
* @param configuredPublishers
* @param listener
*/
@Nonnull
public abstract List<MavenPublisher> buildPublishersList(@Nonnull List<MavenPublisher> configuredPublishers, @Nonnull TaskListener listener);

private final static Logger LOGGER = Logger.getLogger(MavenPublisherStrategy.class.getName());
}
Expand Up @@ -59,7 +59,8 @@ public class MavenSpyLogProcessor implements Serializable {

private static final Logger LOGGER = Logger.getLogger(MavenSpyLogProcessor.class.getName());

public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder, List<MavenPublisher> options) throws IOException, InterruptedException {
public void processMavenSpyLogs(@Nonnull StepContext context, @Nonnull FilePath mavenSpyLogFolder, @Nonnull List<MavenPublisher> options,
@Nonnull MavenPublisherStrategy publisherStrategy) throws IOException, InterruptedException {
FilePath[] mavenSpyLogsList = mavenSpyLogFolder.list("maven-spy-*.log");
LOGGER.log(Level.FINE, "Found {0} maven execution reports in {1}", new Object[]{mavenSpyLogsList.length, mavenSpyLogFolder});

Expand All @@ -80,7 +81,7 @@ public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder,
for (FilePath mavenSpyLogs : mavenSpyLogsList) {
try {
if (LOGGER.isLoggable(Level.FINE)) {
listener.getLogger().println("[withMaven] Evaluate Maven Spy logs: " + mavenSpyLogs.getRemote());
listener.getLogger().println("[withMaven] Evaluate Maven Spy logs: " + mavenSpyLogs.getRemote());
}
InputStream mavenSpyLogsInputStream = mavenSpyLogs.read();
if (mavenSpyLogsInputStream == null) {
Expand All @@ -95,7 +96,10 @@ public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder,

Element mavenSpyLogsElt = documentBuilder.parse(mavenSpyLogsInputStream).getDocumentElement();

List<MavenPublisher> mavenPublishers = MavenPublisher.buildPublishersList(options, listener);
if (LOGGER.isLoggable(Level.FINE)){
listener.getLogger().println("[withMaven] Maven Publisher Strategy: " + publisherStrategy.getDescription());
}
List<MavenPublisher> mavenPublishers = publisherStrategy.buildPublishersList(options, listener);
for (MavenPublisher mavenPublisher : mavenPublishers) {
String skipFileName = mavenPublisher.getDescriptor().getSkipFileName();
if (Boolean.TRUE.equals(mavenPublisher.isDisabled())) {
Expand Down

0 comments on commit 1d06172

Please sign in to comment.