From 0af6b9a42faaaedfb74506f431041798f8583ea1 Mon Sep 17 00:00:00 2001 From: Cyrille Le Clerc Date: Thu, 18 May 2017 21:06:10 -0700 Subject: [PATCH] =?UTF-8?q?WIP=20[JENKINS-43596]=20add=20=E2=80=9Coptions?= =?UTF-8?q?=E2=80=9D=20to=20configure=20withMaven(){=E2=80=A6}=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [JENKINS-43596] add “options” to configure withMaven(){…}. The first usage is to disable publishers * [JENKINS-43596] Add comments * [JENKINS-43596] add “options” to configure withMaven(){…}. The first usage is to disable publishers. Rename Reporters into Publishers --- jenkins-plugin/pom.xml | 12 +- .../pipeline/maven/MavenPublisher.java | 148 ++++++++++ .../pipeline/maven/MavenSpyLogProcessor.java | 66 ++--- .../pipeline/maven/ResultsReporter.java | 24 -- .../plugins/pipeline/maven/WithMavenStep.java | 63 ++-- .../maven/WithMavenStepExecution.java | 15 +- .../publishers/FindbugsAnalysisPublisher.java | 272 ++++++++++++++++++ .../GeneratedArtifactsPublisher.java} | 40 ++- .../JenkinsMavenEventSpyLogsPublisher.java} | 8 +- .../JunitTestsPublisher.java} | 43 ++- .../TasksScannerPublisher.java} | 40 ++- .../reporters/FindbugsAnalysisReporter.java | 239 --------------- .../WithMavenStep/MavenReporter/config.jelly | 35 +++ .../maven/WithMavenStep/config.groovy | 34 --- .../pipeline/maven/WithMavenStep/config.jelly | 61 ++++ .../maven/WithMavenStep/help-jdk.html | 2 +- .../maven/WithMavenStep/help-maven.html | 2 +- .../FindbugsAnalysisPublisher/config.jelly | 35 +++ .../GeneratedArtifactsPublisher/config.jelly | 35 +++ .../JunitTestsPublisher/config.jelly | 35 +++ .../TasksScannerPublisher/config.jelly | 35 +++ .../pipeline/maven/MavenPublisherTest.java | 47 +++ .../maven/WithMavenStepOnMasterTest.java | 86 +++++- .../GeneratedArtifactsReporterTest.java | 4 +- 24 files changed, 991 insertions(+), 390 deletions(-) create mode 100644 jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisher.java delete mode 100644 jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/ResultsReporter.java create mode 100644 jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher.java rename jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/{reporters/GeneratedArtifactsReporter.java => publishers/GeneratedArtifactsPublisher.java} (93%) rename jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/{reporters/JenkinsMavenEventSpyLogsReporter.java => publishers/JenkinsMavenEventSpyLogsPublisher.java} (90%) rename jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/{reporters/JunitTestsReporter.java => publishers/JunitTestsPublisher.java} (92%) rename jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/{reporters/TasksScannerReporter.java => publishers/TasksScannerPublisher.java} (85%) delete mode 100644 jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/FindbugsAnalysisReporter.java create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/MavenReporter/config.jelly delete mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.groovy create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.jelly create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher/config.jelly create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher/config.jelly create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher/config.jelly create mode 100644 jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/TasksScannerPublisher/config.jelly create mode 100644 jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisherTest.java rename jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/{reporters => publishers}/GeneratedArtifactsReporterTest.java (97%) diff --git a/jenkins-plugin/pom.xml b/jenkins-plugin/pom.xml index 7ae17f5b..46d6834b 100644 --- a/jenkins-plugin/pom.xml +++ b/jenkins-plugin/pom.xml @@ -74,8 +74,8 @@ 2.7.4 - 2.0.8 - 3.1.0 + 2.1.1 + 3.3.0 @@ -96,7 +96,7 @@ org.jenkins-ci.plugins config-file-provider - 2.15.1 + 2.15.7 org.jenkins-ci.plugins @@ -108,7 +108,7 @@ org.jvnet.hudson.plugins findbugs - 4.69 + 4.70 true @@ -124,7 +124,7 @@ org.jvnet.hudson.plugins tasks - 4.50 + 4.51 true @@ -189,7 +189,7 @@ org.jenkins-ci.plugins ssh-slaves - 1.13 + 1.17 test diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisher.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisher.java new file mode 100644 index 00000000..3720139d --- /dev/null +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisher.java @@ -0,0 +1,148 @@ +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.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.stapler.DataBoundSetter; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; +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; + +/** + * Experimental interface, likely to change in the future. + * + * @author Cyrille Le Clerc + */ +public abstract class MavenPublisher extends AbstractDescribableImpl implements ExtensionPoint, Comparable, Serializable { + + private final static Logger LOGGER = Logger.getLogger(MavenPublisher.class.getName()); + + @CheckForNull + private Boolean disabled; + + @CheckForNull + public Boolean isDisabled() { + return disabled; + } + + @DataBoundSetter + public void setDisabled(@Nullable Boolean disabled) { + this.disabled = disabled; + } + + /** + * @param context + * @param mavenSpyLogsElt maven spy report. WARNING experimental structure for the moment, subject to change. + * @throws IOException + * @throws InterruptedException + */ + public abstract void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsElt) throws IOException, InterruptedException; + + @Override + public DescriptorImpl getDescriptor() { + return (DescriptorImpl) super.getDescriptor(); + } + + @Override + public int compareTo(MavenPublisher o) { + return this.getDescriptor().compareTo(o.getDescriptor()); + } + + @Override + public String toString() { + return getClass().getName() + "[" + + "disabled=" + disabled + + ']'; + } + + public static abstract class DescriptorImpl extends Descriptor implements Comparable { + /** + * + * @return the ordinal of this reporter to execute publishers in predictable order + * @see #compareTo(MavenPublisher) + */ + public int ordinal() { + return 100; + } + + /** + * Name of the marker file used to skip the maven reporter + * + * @return name of the marker file. {@code null} if no marker file is defined for this reporter + */ + @Nullable + abstract public String getSkipFileName(); + + + @Override + public int compareTo(DescriptorImpl o) { + int compare = Integer.compare(this.ordinal(), o.ordinal()); + + if (compare == 0) { + compare = this.getId().compareTo(o.getId()); + } + + return compare; + } + } + + @Nonnull + public static List buildReportersList(@Nonnull List configuredReporters, @Nonnull TaskListener listener){ + + // mavenReporter.descriptor.id -> mavenReporter + Map configuredReportersById = new HashMap<>(); + for (MavenPublisher mavenPublisher : configuredReporters) { + if (mavenPublisher == null) { + // skipp null reporter injected by Jenkins pipeline for an unknown reason + } else { + configuredReportersById.put(mavenPublisher.getDescriptor().getId(), mavenPublisher); + } + } + + // mavenReporter.descriptor.id -> mavenRepoer + Map defaultReportersById = new HashMap<>(); + DescriptorExtensionList> descriptorList = Jenkins.getInstance().getDescriptorList(MavenPublisher.class); + for (Descriptor descriptor:descriptorList) { + if (configuredReportersById.containsKey(descriptor.getId())) { + // skip, already provided with a configuration + } else { + try { + defaultReportersById.put(descriptor.getId(), descriptor.clazz.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + PrintWriter error = listener.error("[withMaven] Exception instantiation default config for Maven Reporter '" + 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 Reporters with configuration provided by the pipeline: " + configuredReportersById); + listener.getLogger().println("[withMaven] Maven Reporters with default configuration: " + defaultReportersById); + } + + List results = new ArrayList<>(); + results.addAll(configuredReportersById.values()); + results.addAll(defaultReportersById.values()); + Collections.sort(results); + return results; + } +} \ No newline at end of file diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenSpyLogProcessor.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenSpyLogProcessor.java index cde84d07..1e3fe924 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenSpyLogProcessor.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/MavenSpyLogProcessor.java @@ -29,11 +29,8 @@ import hudson.model.StreamBuildListener; import hudson.model.TaskListener; import jenkins.model.InterruptedBuildAction; -import org.jenkinsci.plugins.pipeline.maven.reporters.FindbugsAnalysisReporter; -import org.jenkinsci.plugins.pipeline.maven.reporters.GeneratedArtifactsReporter; -import org.jenkinsci.plugins.pipeline.maven.reporters.JunitTestsReporter; -import org.jenkinsci.plugins.pipeline.maven.reporters.JenkinsMavenEventSpyLogsReporter; -import org.jenkinsci.plugins.pipeline.maven.reporters.TasksScannerReporter; +import org.apache.commons.lang.StringUtils; +import org.jenkinsci.plugins.pipeline.maven.publishers.JenkinsMavenEventSpyLogsPublisher; import org.jenkinsci.plugins.workflow.steps.StepContext; import org.w3c.dom.Element; import org.xml.sax.SAXException; @@ -43,6 +40,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.Serializable; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -59,7 +57,7 @@ public class MavenSpyLogProcessor implements Serializable { private static final Logger LOGGER = Logger.getLogger(MavenSpyLogProcessor.class.getName()); - public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder) throws IOException, InterruptedException { + public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder, List options) 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}); @@ -90,43 +88,33 @@ public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder) FilePath archiveJenkinsMavenEventSpyLogs = workspace.child(".archive-jenkins-maven-event-spy-logs"); if (archiveJenkinsMavenEventSpyLogs.exists()) { LOGGER.log(Level.FINE, "Archive Jenkins Maven Event Spy logs {0}", mavenSpyLogs.getRemote()); - new JenkinsMavenEventSpyLogsReporter().process(context, mavenSpyLogs); + new JenkinsMavenEventSpyLogsPublisher().process(context, mavenSpyLogs); } Element mavenSpyLogsElt = documentBuilder.parse(mavenSpyLogsInputStream).getDocumentElement(); - FilePath skipArchiveArtifactsFile = workspace.child(".skip-archive-generated-artifacts"); - if (skipArchiveArtifactsFile.exists()) { - listener.getLogger().println("[withMaven] Skip archiving of generated artifacts, file '" + skipArchiveArtifactsFile + "' found in workspace"); - } else { - LOGGER.log(Level.FINE, "Look for generated artifacts to archive, file {0} NOT found in workspace", skipArchiveArtifactsFile); - new GeneratedArtifactsReporter().process(context, mavenSpyLogsElt); - } - - FilePath skipJunitFile = workspace.child(".skip-publish-junit-results"); - if (skipJunitFile.exists()) { - listener.getLogger().println("[withMaven] Skip publishing of JUnit results, file '" + skipJunitFile + "' found in workspace"); - } else { - LOGGER.log(Level.FINE, "Look for JUnit results to publish, file {0} NOT found in workspace", skipJunitFile); - new JunitTestsReporter().process(context, mavenSpyLogsElt); + List mavenPublishers = MavenPublisher.buildReportersList(options, listener); + for (MavenPublisher mavenPublisher : mavenPublishers){ + String skipFileName = mavenPublisher.getDescriptor().getSkipFileName(); + if (Boolean.TRUE.equals(mavenPublisher.isDisabled())) { + listener.getLogger().println("[withMaven] Skip '" + mavenPublisher.getDescriptor().getDisplayName() + "' disabled by configuration"); + } else if (StringUtils.isNotEmpty(skipFileName) && workspace.child(skipFileName).exists()) { + listener.getLogger().println("[withMaven] Skip '" + mavenPublisher.getDescriptor().getDisplayName() + "' disabled by marker file '" + skipFileName + "'"); + } else { + if (LOGGER.isLoggable(Level.FINE)) { + listener.getLogger().println("[withMaven] Run '" + mavenPublisher.getDescriptor().getDisplayName() + "'..."); + } + try { + mavenPublisher.process(context, mavenSpyLogsElt); + } catch (IOException|RuntimeException e) { + PrintWriter error = listener.error("[withMaven] WARNING Exception executing Maven reporter '" + mavenPublisher.getDescriptor().getDisplayName() + + "' / " + mavenPublisher.getDescriptor().getId() + "." + + " Please report a bug associated for the component 'pipeline-maven-plugin' at https://issues.jenkins-ci.org "); + e.printStackTrace(error); + + } + } } - FilePath skipFindbugsFile = workspace.child(".skip-publish-findbugs-results"); - if (skipFindbugsFile.exists()) { - listener.getLogger().println("[withMaven] Skip publishing of FindBugs results, file '" + skipFindbugsFile + "' found in workspace"); - } else { - LOGGER.log(Level.FINE, "Look for Findbugs results to publish, file {0} NOT found in workspace", skipFindbugsFile); - new FindbugsAnalysisReporter().process(context, mavenSpyLogsElt); - } - - FilePath skipTasksScannerFile = workspace.child(".skip-task-scanner"); - if (skipTasksScannerFile.exists()) { - listener.getLogger().println("[withMaven] Skip publishing of tasks in source code, file '" + skipTasksScannerFile + "' found in workspace"); - } else { - LOGGER.log(Level.FINE, "Look for tasks in source code to publish, file {0} NOT found in workspace", skipTasksScannerFile); - new TasksScannerReporter().process(context, mavenSpyLogsElt); - } - - } catch (SAXException e) { Run run = context.get(Run.class); if (run.getActions(InterruptedBuildAction.class).isEmpty()) { @@ -145,8 +133,6 @@ public void processMavenSpyLogs(StepContext context, FilePath mavenSpyLogFolder) } } - - public static class MavenArtifact { public String groupId, artifactId, version, type, classifier, extension; public String file; diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/ResultsReporter.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/ResultsReporter.java deleted file mode 100644 index f488e9fc..00000000 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/ResultsReporter.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.jenkinsci.plugins.pipeline.maven; - -import org.jenkinsci.plugins.workflow.steps.StepContext; -import org.w3c.dom.Element; - -import java.io.IOException; - -import javax.annotation.Nonnull; - -/** - * Experimental interface, likely to change in the future. - * - * @author Cyrille Le Clerc - */ -public interface ResultsReporter { - /** - * - * @param context - * @param mavenSpyLogsElt maven spy report. WARNING experimental structure for the moment, subject to change. - * @throws IOException - * @throws InterruptedException - */ - void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsElt) throws IOException, InterruptedException; -} \ No newline at end of file diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStep.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStep.java index 4e3f42c3..3d599e03 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStep.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStep.java @@ -25,37 +25,38 @@ package org.jenkinsci.plugins.pipeline.maven; import com.google.common.collect.ImmutableSet; +import hudson.DescriptorExtensionList; import hudson.EnvVars; -import hudson.model.ItemGroup; -import org.jenkinsci.lib.configprovider.ConfigProvider; -import org.jenkinsci.lib.configprovider.model.Config; -import org.jenkinsci.plugins.configfiles.ConfigFiles; -import org.jenkinsci.plugins.configfiles.maven.GlobalMavenSettingsConfig.GlobalMavenSettingsConfigProvider; -import org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig.MavenSettingsConfigProvider; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; -import org.kohsuke.stapler.AncestorInPath; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.DataBoundSetter; - import hudson.Extension; -import hudson.ExtensionList; import hudson.FilePath; import hudson.Launcher; +import hudson.model.ItemGroup; import hudson.model.JDK; import hudson.model.Run; import hudson.model.TaskListener; import hudson.tasks.Maven; import hudson.tasks.Maven.MavenInstallation; import hudson.util.ListBoxModel; -import java.util.Set; import jenkins.model.Jenkins; import jenkins.mvn.GlobalMavenConfig; import jenkins.mvn.SettingsProvider; +import org.jenkinsci.lib.configprovider.model.Config; +import org.jenkinsci.plugins.configfiles.ConfigFiles; +import org.jenkinsci.plugins.configfiles.maven.GlobalMavenSettingsConfig.GlobalMavenSettingsConfigProvider; +import org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig.MavenSettingsConfigProvider; 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.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; /** * Configures maven environment to use within a pipeline job by calling sh mvn or bat mvn. @@ -73,6 +74,7 @@ public class WithMavenStep extends Step { private String mavenOpts = ""; private String jdk; private String mavenLocalRepo = ""; + private List options = new ArrayList<>(); @DataBoundConstructor public WithMavenStep() { @@ -151,6 +153,27 @@ public void setMavenLocalRepo(String mavenLocalRepo) { this.mavenLocalRepo = mavenLocalRepo; } + public List getOptions() { + return options; + } + + @Override + public WithMavenStep.DescriptorImpl getDescriptor() { + return (WithMavenStep.DescriptorImpl) super.getDescriptor(); + } + + /** + * Return all the registered Maven publishers + */ + public DescriptorExtensionList getOptionsDescriptors() { + return getDescriptor().getOptionsDescriptors(); + } + + @DataBoundSetter + public void setOptions(List options) { + this.options = options; + } + @Override public StepExecution start(StepContext context) throws Exception { return new WithMavenStepExecution(context, this); @@ -185,7 +208,7 @@ public SettingsProvider getDefaultSettingsProvider() { } private Maven.DescriptorImpl getMavenDescriptor() { - return Jenkins.getActiveInstance().getDescriptorByType(Maven.DescriptorImpl.class); + return Jenkins.getInstance().getDescriptorByType(Maven.DescriptorImpl.class); } @Restricted(NoExternalUse.class) // Only for UI calls @@ -199,7 +222,7 @@ public ListBoxModel doFillMavenItems() { } private JDK.DescriptorImpl getJDKDescriptor() { - return Jenkins.getActiveInstance().getDescriptorByType(JDK.DescriptorImpl.class); + return Jenkins.getInstance().getDescriptorByType(JDK.DescriptorImpl.class); } @Restricted(NoExternalUse.class) // Only for UI calls @@ -231,5 +254,13 @@ public ListBoxModel doFillGlobalMavenSettingsConfigItems(@AncestorInPath ItemGro } return r; } + + /** + * Return all the registered Maven publishers + */ + public DescriptorExtensionList getOptionsDescriptors() { + return Jenkins.getInstance().getDescriptorList(MavenPublisher.class); + } + } } diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepExecution.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepExecution.java index b9767af8..614b6faa 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepExecution.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepExecution.java @@ -56,6 +56,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.AbortException; import hudson.EnvVars; +import hudson.ExtensionList; import hudson.FilePath; import hudson.Launcher; import hudson.Launcher.ProcStarter; @@ -156,8 +157,13 @@ public boolean start() throws Exception { LOGGER.log(Level.FINE, "Settings FilePath: {0}", step.getMavenSettingsFilePath()); LOGGER.log(Level.FINE, "Global settings Config: {0}", step.getGlobalMavenSettingsConfig()); LOGGER.log(Level.FINE, "Global settings FilePath: {0}", step.getGlobalMavenSettingsFilePath()); + LOGGER.log(Level.FINE, "Options: {0}", step.getOptions()); } + listener.getLogger().println("[withMaven] Options: " + step.getOptions()); + ExtensionList availableMavenPublishers = Jenkins.getInstance().getExtensionList(MavenPublisher.class); + listener.getLogger().println("[withMaven] Available options: " + Joiner.on(",").join(availableMavenPublishers)); + getComputer(); withContainer = detectWithContainer(); @@ -168,7 +174,7 @@ public boolean start() throws Exception { ConsoleLogFilter consFilter = BodyInvoker.mergeConsoleLogFilters(getContext().get(ConsoleLogFilter.class), new MavenConsoleFilter(getComputer().getDefaultCharset().name())); EnvironmentExpander envEx = EnvironmentExpander.merge(getContext().get(EnvironmentExpander.class), new ExpanderImpl(envOverride)); - body = getContext().newBodyInvoker().withContexts(envEx, consFilter).withCallback(new Callback(tempBinDir)).start(); + body = getContext().newBodyInvoker().withContexts(envEx, consFilter).withCallback(new Callback(tempBinDir, step.getOptions())).start(); return false; } @@ -839,15 +845,18 @@ public void expand(EnvVars env) throws IOException, InterruptedException { private static class Callback extends BodyExecutionCallback.TailCall { private final FilePath tempBinDir; + private final List options; + private final MavenSpyLogProcessor mavenSpyLogProcessor = new MavenSpyLogProcessor(); - public Callback(FilePath tempBinDir) { + public Callback(@Nonnull FilePath tempBinDir, @Nonnull List options) { this.tempBinDir = tempBinDir; + this.options = options; } @Override protected void finished(StepContext context) throws Exception { - mavenSpyLogProcessor.processMavenSpyLogs(context, tempBinDir); + mavenSpyLogProcessor.processMavenSpyLogs(context, tempBinDir, options); try { tempBinDir.deleteRecursive(); diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher.java new file mode 100644 index 00000000..4741b089 --- /dev/null +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher.java @@ -0,0 +1,272 @@ +/* + * The MIT License + * + * Copyright (c) 2016, 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.maven.publishers; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Run; +import hudson.model.StreamBuildListener; +import hudson.model.TaskListener; +import hudson.plugins.findbugs.FindBugsPublisher; +import org.jenkinsci.Symbol; +import org.jenkinsci.plugins.pipeline.maven.MavenPublisher; +import org.jenkinsci.plugins.pipeline.maven.MavenSpyLogProcessor; +import org.jenkinsci.plugins.pipeline.maven.util.XmlUtils; +import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.stapler.DataBoundConstructor; +import org.w3c.dom.Element; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.annotation.Nonnull; + +/** + * @author Cyrille Le Clerc + */ +public class FindbugsAnalysisPublisher extends MavenPublisher { + private static final Logger LOGGER = Logger.getLogger(FindbugsAnalysisPublisher.class.getName()); + + private static final long serialVersionUID = 1L; + + @DataBoundConstructor + public FindbugsAnalysisPublisher() { + + } + + /* + + + + + + ${project.build.outputDirectory} + ${project.compileSourceRoots} + ${findbugs.debug} + ${findbugs.effort} + ${findbugs.excludeBugsFile} + ${findbugs.excludeFilterFile} + ${findbugs.failOnError} + true + ${project.build.directory} + ${findbugs.fork} + ${findbugs.includeFilterFile} + ${findbugs.includeTests} + ${findbugs.jvmArgs} + ${localRepository} + ${findbugs.maxHeap} + ${findbugs.maxRank} + ${findbugs.nested} + ${findbugs.omitVisitors} + ${findbugs.onlyAnalyze} + ${project.reporting.outputDirectory} + ${outputEncoding} + ${plugin.artifacts} + ${findbugs.pluginList} + ${project} + ${findbugs.relaxed} + ${project.remoteArtifactRepositories} + ${project.remoteArtifactRepositories} + ${findbugs.skip} + ${findbugs.skipEmptyReport} + ${encoding} + ${project.build.testOutputDirectory} + ${project.testCompileSourceRoots} + ${findbugs.threshold} + ${findbugs.timeout} + ${findbugs.trace} + ${findbugs.userPrefs} + ${findbugs.visitors} + UTF-8 + ${findbugs.xmlOutput} + ${project.build.directory} + ${project.reporting.outputDirectory}/xref + ${project.reporting.outputDirectory}/xref-test + + + + + + + + ${project.build.outputDirectory} + ${project.compileSourceRoots} + ${findbugs.debug} + ${findbugs.effort} + ${findbugs.excludeBugsFile} + ${findbugs.excludeFilterFile} + ${findbugs.failOnError} + true + ${project.build.directory} + ${findbugs.fork} + ${findbugs.includeFilterFile} + ${findbugs.includeTests} + ${findbugs.jvmArgs} + ${localRepository} + ${findbugs.maxHeap} + ${findbugs.maxRank} + ${findbugs.nested} + ${findbugs.omitVisitors} + ${findbugs.onlyAnalyze} + ${project.reporting.outputDirectory} + ${outputEncoding} + ${plugin.artifacts} + ${findbugs.pluginList} + ${project} + ${findbugs.relaxed} + ${project.remoteArtifactRepositories} + ${project.remoteArtifactRepositories} + ${findbugs.skip} + ${findbugs.skipEmptyReport} + ${encoding} + ${project.build.testOutputDirectory} + ${project.testCompileSourceRoots} + ${findbugs.threshold} + ${findbugs.timeout} + ${findbugs.trace} + ${findbugs.userPrefs} + ${findbugs.visitors} + UTF-8 + ${findbugs.xmlOutput} + ${project.build.directory} + ${project.reporting.outputDirectory}/xref + ${project.reporting.outputDirectory}/xref-test + + + */ + @Override + public void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsElt) throws IOException, InterruptedException { + + TaskListener listener = context.get(TaskListener.class); + if (listener == null) { + LOGGER.warning("TaskListener is NULL, default to stderr"); + listener = new StreamBuildListener((OutputStream) System.err); + } + FilePath workspace = context.get(FilePath.class); + Run run = context.get(Run.class); + Launcher launcher = context.get(Launcher.class); + + + List findbugsEvents = XmlUtils.getExecutionEvents(mavenSpyLogsElt, "org.codehaus.mojo", "findbugs-maven-plugin", "findbugs"); + + if (findbugsEvents.isEmpty()) { + LOGGER.log(Level.FINE, "No org.codehaus.mojo:findbugs-maven-plugin:findbugs execution found"); + return; + } + try { + Class.forName("hudson.plugins.findbugs.FindBugsPublisher"); + } catch (ClassNotFoundException e) { + listener.getLogger().print("[withMaven] Jenkins "); + listener.hyperlink("https://wiki.jenkins-ci.org/display/JENKINS/FindBugs+Plugin", "FindBugs Plugin"); + listener.getLogger().println(" not found, don't display org.codehaus.mojo:findbugs-maven-plugin:findbugs results in pipeline screen."); + return; + } + + + for (Element findBugsTestEvent : findbugsEvents) { + String findBugsEventType = findBugsTestEvent.getAttribute("type"); + if (!findBugsEventType.equals("MojoSucceeded") && !findBugsEventType.equals("MojoFailed")) { + continue; + } + + Element pluginElt = XmlUtils.getUniqueChildElement(findBugsTestEvent, "plugin"); + Element xmlOutputDirectoryElt = XmlUtils.getUniqueChildElementOrNull(pluginElt, "xmlOutputDirectory"); + Element projectElt = XmlUtils.getUniqueChildElement(findBugsTestEvent, "project"); + MavenSpyLogProcessor.MavenArtifact mavenArtifact = XmlUtils.newMavenArtifact(projectElt); + MavenSpyLogProcessor.PluginInvocation pluginInvocation = XmlUtils.newPluginInvocation(pluginElt); + + if (xmlOutputDirectoryElt == null) { + listener.getLogger().println("[withMaven] No element found for in " + XmlUtils.toString(findBugsTestEvent)); + continue; + } + String xmlOutputDirectory = xmlOutputDirectoryElt.getTextContent().trim(); + if (xmlOutputDirectory.contains("${project.build.directory}")) { + String projectBuildDirectory = XmlUtils.getProjectBuildDirectory(projectElt); + if (projectBuildDirectory == null || projectBuildDirectory.isEmpty()) { + listener.getLogger().println("[withMaven] '${project.build.directory}' found for in " + XmlUtils.toString(findBugsTestEvent)); + continue; + } + + xmlOutputDirectory = xmlOutputDirectory.replace("${project.build.directory}", projectBuildDirectory); + + } else if (xmlOutputDirectory.contains("${basedir}")) { + String baseDir = projectElt.getAttribute("baseDir"); + if (baseDir.isEmpty()) { + listener.getLogger().println("[withMaven] '${basedir}' found for in " + XmlUtils.toString(findBugsTestEvent)); + continue; + } + + xmlOutputDirectory = xmlOutputDirectory.replace("${basedir}", baseDir); + } + + xmlOutputDirectory = XmlUtils.getPathInWorkspace(xmlOutputDirectory, workspace); + + String findBugsResultsFile = xmlOutputDirectory + "/findbugsXml.xml"; + listener.getLogger().println("[withMaven] Archive FindBugs analysis results for Maven artifact " + mavenArtifact.toString() + " generated by " + + pluginInvocation + ": " + findBugsResultsFile); + FindBugsPublisher archiver = new FindBugsPublisher(); + + archiver.setPattern(findBugsResultsFile); + + try { + archiver.perform(run, workspace, launcher, listener); + } catch (Exception e) { + listener.error("[withMaven] Silently ignore exception archiving FindBugs results for Maven artifact " + mavenArtifact.toString() + " generated by " + + pluginInvocation + ": " + e); + LOGGER.log(Level.WARNING, "Exception processing " + XmlUtils.toString(findBugsTestEvent), e); + } + + } + + } + + /** + * Don't use symbol "findbugs", it would collide with hudson.plugins.findbugs.FindBugsPublisher + */ + @Symbol("findbugsPublisher") + @Extension + public static class DescriptorImpl extends MavenPublisher.DescriptorImpl { + @Nonnull + @Override + public String getDisplayName() { + return "Findbugs Publisher"; + } + + @Override + public int ordinal() { + return 20; + } + + @Nonnull + @Override + public String getSkipFileName() { + return ".skip-publish-findbugs-results"; + } + } +} diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporter.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher.java similarity index 93% rename from jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporter.java rename to jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher.java index 0057bbdf..d2ccf9a9 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporter.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher.java @@ -1,5 +1,6 @@ -package org.jenkinsci.plugins.pipeline.maven.reporters; +package org.jenkinsci.plugins.pipeline.maven.publishers; +import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.FingerprintMap; @@ -11,10 +12,12 @@ import jenkins.model.Jenkins; import jenkins.util.BuildListenerAdapter; import org.apache.commons.lang.StringUtils; +import org.jenkinsci.Symbol; +import org.jenkinsci.plugins.pipeline.maven.MavenPublisher; import org.jenkinsci.plugins.pipeline.maven.MavenSpyLogProcessor; -import org.jenkinsci.plugins.pipeline.maven.ResultsReporter; import org.jenkinsci.plugins.pipeline.maven.util.XmlUtils; import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.stapler.DataBoundConstructor; import org.w3c.dom.Element; import java.io.IOException; @@ -32,9 +35,17 @@ /** * @author Cyrille Le Clerc */ -public class GeneratedArtifactsReporter implements ResultsReporter{ +public class GeneratedArtifactsPublisher extends MavenPublisher { + private static final Logger LOGGER = Logger.getLogger(MavenSpyLogProcessor.class.getName()); + private static final long serialVersionUID = 1L; + + @DataBoundConstructor + public GeneratedArtifactsPublisher() { + + } + @Override public void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsElt) throws IOException, InterruptedException { @@ -243,4 +254,27 @@ public List listAttachedArtifacts(Element ma } return result; } + + + + @Symbol("artifactsPublisher") + @Extension public static class DescriptorImpl extends MavenPublisher.DescriptorImpl { + @Nonnull + @Override + public String getDisplayName() { + return "Generated Artifacts Publisher"; + } + + @Override + public int ordinal() { + return 1; + } + + + @Nonnull + @Override + public String getSkipFileName() { + return ".skip-archive-generated-artifacts"; + } + } } diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JenkinsMavenEventSpyLogsReporter.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JenkinsMavenEventSpyLogsPublisher.java similarity index 90% rename from jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JenkinsMavenEventSpyLogsReporter.java rename to jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JenkinsMavenEventSpyLogsPublisher.java index 9a5d8329..4ae4398d 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JenkinsMavenEventSpyLogsReporter.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JenkinsMavenEventSpyLogsPublisher.java @@ -1,4 +1,4 @@ -package org.jenkinsci.plugins.pipeline.maven.reporters; +package org.jenkinsci.plugins.pipeline.maven.publishers; import hudson.FilePath; import hudson.Launcher; @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.PrintWriter; +import java.io.Serializable; import java.util.Collections; import java.util.Map; @@ -20,7 +21,10 @@ * * @author Cyrille Le Clerc */ -public class JenkinsMavenEventSpyLogsReporter { +public class JenkinsMavenEventSpyLogsPublisher implements Serializable { + + private static final long serialVersionUID = 1L; + public void process(@Nonnull StepContext context, @Nonnull FilePath mavenSpyLogs) throws IOException, InterruptedException { Run run = context.get(Run.class); diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JunitTestsReporter.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher.java similarity index 92% rename from jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JunitTestsReporter.java rename to jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher.java index d0661ccf..3e537eaf 100644 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/JunitTestsReporter.java +++ b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher.java @@ -22,18 +22,21 @@ * THE SOFTWARE. */ -package org.jenkinsci.plugins.pipeline.maven.reporters; +package org.jenkinsci.plugins.pipeline.maven.publishers; +import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.model.Run; import hudson.model.StreamBuildListener; import hudson.model.TaskListener; import hudson.tasks.junit.JUnitResultArchiver; +import org.jenkinsci.Symbol; import org.jenkinsci.plugins.pipeline.maven.MavenSpyLogProcessor; -import org.jenkinsci.plugins.pipeline.maven.ResultsReporter; +import org.jenkinsci.plugins.pipeline.maven.MavenPublisher; import org.jenkinsci.plugins.pipeline.maven.util.XmlUtils; import org.jenkinsci.plugins.workflow.steps.StepContext; +import org.kohsuke.stapler.DataBoundConstructor; import org.w3c.dom.Element; import java.io.IOException; @@ -47,14 +50,21 @@ /** * @author Cyrille Le Clerc */ -public class JunitTestsReporter implements ResultsReporter { - private static final Logger LOGGER = Logger.getLogger(JunitTestsReporter.class.getName()); +public class JunitTestsPublisher extends MavenPublisher { + private static final Logger LOGGER = Logger.getLogger(JunitTestsPublisher.class.getName()); private static final String GROUP_ID = "org.apache.maven.plugins"; private static final String SUREFIRE_ID = "maven-surefire-plugin"; private static final String FAILSAFE_ID = "maven-failsafe-plugin"; private static final String SUREFIRE_GOAL = "test"; private static final String FAILSAFE_GOAL = "integration-test"; + private static final long serialVersionUID = 1L; + + @DataBoundConstructor + public JunitTestsPublisher() { + + } + /* @@ -231,4 +241,29 @@ private void executeReporter(StepContext context, TaskListener listener, ListCyrille Le Clerc * @see hudson.plugins.tasks.TasksPublisher */ -public class TasksScannerReporter implements ResultsReporter { - private static final Logger LOGGER = Logger.getLogger(FindbugsAnalysisReporter.class.getName()); +public class TasksScannerPublisher extends MavenPublisher { + private static final Logger LOGGER = Logger.getLogger(FindbugsAnalysisPublisher.class.getName()); + + private static final long serialVersionUID = 1L; + + @DataBoundConstructor + public TasksScannerPublisher() { + + } /* @@ -105,4 +114,25 @@ public void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsE LOGGER.log(Level.WARNING, "Exception scanning tasks in " + pattern, e); } } + + @Symbol("openTasksPublisher") + @Extension + public static class DescriptorImpl extends MavenPublisher.DescriptorImpl { + @Nonnull + @Override + public String getDisplayName() { + return "Task Scanner Publisher"; + } + + @Override + public int ordinal() { + return 100; + } + + @Nonnull + @Override + public String getSkipFileName() { + return ".skip-task-scanner"; + } + } } diff --git a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/FindbugsAnalysisReporter.java b/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/FindbugsAnalysisReporter.java deleted file mode 100644 index 117cccfc..00000000 --- a/jenkins-plugin/src/main/java/org/jenkinsci/plugins/pipeline/maven/reporters/FindbugsAnalysisReporter.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2016, 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.maven.reporters; - -import hudson.FilePath; -import hudson.Launcher; -import hudson.model.Run; -import hudson.model.StreamBuildListener; -import hudson.model.TaskListener; -import hudson.plugins.findbugs.FindBugsPublisher; -import hudson.tasks.junit.JUnitResultArchiver; -import org.jenkinsci.plugins.pipeline.maven.MavenSpyLogProcessor; -import org.jenkinsci.plugins.pipeline.maven.ResultsReporter; -import org.jenkinsci.plugins.pipeline.maven.util.XmlUtils; -import org.jenkinsci.plugins.workflow.steps.StepContext; -import org.w3c.dom.Element; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.annotation.Nonnull; - -/** - * @author Cyrille Le Clerc - */ -public class FindbugsAnalysisReporter implements ResultsReporter { - private static final Logger LOGGER = Logger.getLogger(FindbugsAnalysisReporter.class.getName()); - - /* - - - - - - ${project.build.outputDirectory} - ${project.compileSourceRoots} - ${findbugs.debug} - ${findbugs.effort} - ${findbugs.excludeBugsFile} - ${findbugs.excludeFilterFile} - ${findbugs.failOnError} - true - ${project.build.directory} - ${findbugs.fork} - ${findbugs.includeFilterFile} - ${findbugs.includeTests} - ${findbugs.jvmArgs} - ${localRepository} - ${findbugs.maxHeap} - ${findbugs.maxRank} - ${findbugs.nested} - ${findbugs.omitVisitors} - ${findbugs.onlyAnalyze} - ${project.reporting.outputDirectory} - ${outputEncoding} - ${plugin.artifacts} - ${findbugs.pluginList} - ${project} - ${findbugs.relaxed} - ${project.remoteArtifactRepositories} - ${project.remoteArtifactRepositories} - ${findbugs.skip} - ${findbugs.skipEmptyReport} - ${encoding} - ${project.build.testOutputDirectory} - ${project.testCompileSourceRoots} - ${findbugs.threshold} - ${findbugs.timeout} - ${findbugs.trace} - ${findbugs.userPrefs} - ${findbugs.visitors} - UTF-8 - ${findbugs.xmlOutput} - ${project.build.directory} - ${project.reporting.outputDirectory}/xref - ${project.reporting.outputDirectory}/xref-test - - - - - - - - ${project.build.outputDirectory} - ${project.compileSourceRoots} - ${findbugs.debug} - ${findbugs.effort} - ${findbugs.excludeBugsFile} - ${findbugs.excludeFilterFile} - ${findbugs.failOnError} - true - ${project.build.directory} - ${findbugs.fork} - ${findbugs.includeFilterFile} - ${findbugs.includeTests} - ${findbugs.jvmArgs} - ${localRepository} - ${findbugs.maxHeap} - ${findbugs.maxRank} - ${findbugs.nested} - ${findbugs.omitVisitors} - ${findbugs.onlyAnalyze} - ${project.reporting.outputDirectory} - ${outputEncoding} - ${plugin.artifacts} - ${findbugs.pluginList} - ${project} - ${findbugs.relaxed} - ${project.remoteArtifactRepositories} - ${project.remoteArtifactRepositories} - ${findbugs.skip} - ${findbugs.skipEmptyReport} - ${encoding} - ${project.build.testOutputDirectory} - ${project.testCompileSourceRoots} - ${findbugs.threshold} - ${findbugs.timeout} - ${findbugs.trace} - ${findbugs.userPrefs} - ${findbugs.visitors} - UTF-8 - ${findbugs.xmlOutput} - ${project.build.directory} - ${project.reporting.outputDirectory}/xref - ${project.reporting.outputDirectory}/xref-test - - - */ - @Override - public void process(@Nonnull StepContext context, @Nonnull Element mavenSpyLogsElt) throws IOException, InterruptedException { - - TaskListener listener = context.get(TaskListener.class); - if (listener == null) { - LOGGER.warning("TaskListener is NULL, default to stderr"); - listener = new StreamBuildListener((OutputStream) System.err); - } - FilePath workspace = context.get(FilePath.class); - Run run = context.get(Run.class); - Launcher launcher = context.get(Launcher.class); - - - List findbugsEvents = XmlUtils.getExecutionEvents(mavenSpyLogsElt, "org.codehaus.mojo", "findbugs-maven-plugin", "findbugs"); - - if (findbugsEvents.isEmpty()) { - LOGGER.log(Level.FINE, "No org.codehaus.mojo:findbugs-maven-plugin:findbugs execution found"); - return; - } - try { - Class.forName("hudson.plugins.findbugs.FindBugsPublisher"); - } catch (ClassNotFoundException e) { - listener.getLogger().print("[withMaven] Jenkins "); - listener.hyperlink("https://wiki.jenkins-ci.org/display/JENKINS/FindBugs+Plugin", "FindBugs Plugin"); - listener.getLogger().println(" not found, don't display org.codehaus.mojo:findbugs-maven-plugin:findbugs results in pipeline screen."); - return; - } - - - for (Element findBugsTestEvent : findbugsEvents) { - String findBugsEventType = findBugsTestEvent.getAttribute("type"); - if (!findBugsEventType.equals("MojoSucceeded") && !findBugsEventType.equals("MojoFailed")) { - continue; - } - - Element pluginElt = XmlUtils.getUniqueChildElement(findBugsTestEvent, "plugin"); - Element xmlOutputDirectoryElt = XmlUtils.getUniqueChildElementOrNull(pluginElt, "xmlOutputDirectory"); - Element projectElt = XmlUtils.getUniqueChildElement(findBugsTestEvent, "project"); - MavenSpyLogProcessor.MavenArtifact mavenArtifact = XmlUtils.newMavenArtifact(projectElt); - MavenSpyLogProcessor.PluginInvocation pluginInvocation = XmlUtils.newPluginInvocation(pluginElt); - - if (xmlOutputDirectoryElt == null) { - listener.getLogger().println("[withMaven] No element found for in " + XmlUtils.toString(findBugsTestEvent)); - continue; - } - String xmlOutputDirectory = xmlOutputDirectoryElt.getTextContent().trim(); - if (xmlOutputDirectory.contains("${project.build.directory}")) { - String projectBuildDirectory = XmlUtils.getProjectBuildDirectory(projectElt); - if (projectBuildDirectory == null || projectBuildDirectory.isEmpty()) { - listener.getLogger().println("[withMaven] '${project.build.directory}' found for in " + XmlUtils.toString(findBugsTestEvent)); - continue; - } - - xmlOutputDirectory = xmlOutputDirectory.replace("${project.build.directory}", projectBuildDirectory); - - } else if (xmlOutputDirectory.contains("${basedir}")) { - String baseDir = projectElt.getAttribute("baseDir"); - if (baseDir.isEmpty()) { - listener.getLogger().println("[withMaven] '${basedir}' found for in " + XmlUtils.toString(findBugsTestEvent)); - continue; - } - - xmlOutputDirectory = xmlOutputDirectory.replace("${basedir}", baseDir); - } - - xmlOutputDirectory = XmlUtils.getPathInWorkspace(xmlOutputDirectory, workspace); - - String findBugsResultsFile = xmlOutputDirectory + "/findbugsXml.xml"; - listener.getLogger().println("[withMaven] Archive FindBugs analysis results for Maven artifact " + mavenArtifact.toString() + " generated by " + - pluginInvocation + ": " + findBugsResultsFile); - FindBugsPublisher archiver = new FindBugsPublisher(); - - archiver.setPattern(findBugsResultsFile); - - try { - archiver.perform(run, workspace, launcher, listener); - } catch (Exception e) { - listener.error("[withMaven] Silently ignore exception archiving FindBugs results for Maven artifact " + mavenArtifact.toString() + " generated by " + - pluginInvocation + ": " + e); - LOGGER.log(Level.WARNING, "Exception processing " + XmlUtils.toString(findBugsTestEvent), e); - } - - } - - } -} diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/MavenReporter/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/MavenReporter/config.jelly new file mode 100644 index 00000000..9c259cac --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/MavenReporter/config.jelly @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.groovy b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.groovy deleted file mode 100644 index 7bae4dc5..00000000 --- a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package org.jenkinsci.plugins.pipeline.maven.WithMavenStep -def f = namespace(lib.FormTagLib) as lib.FormTagLib - -f.entry(field: 'maven', title: _('Maven')) { - f.select() -} - -f.entry(field: 'jdk', title: _('JDK')) { - f.select() -} - -f.entry(field: 'mavenSettingsConfig', title: _('Maven Settings Config')) { - f.select() -} - -f.entry(field: 'mavenSettingsFilePath', title: _('Maven Settings File Path')) { - f.textbox() -} - -f.entry(field: 'globalMavenSettingsConfig', title: _('Global Maven Settings Config')) { - f.select() -} - -f.entry(field: 'globalMavenSettingsFilePath', title: _('Global Maven Settings File Path')) { - f.textbox() -} - -f.entry(field: 'mavenOpts', title: _('Maven JVM Opts')) { - f.textbox() -} - -f.entry(field: 'mavenLocalRepo', title: _('Maven Local Repository')) { - f.textbox() -} \ No newline at end of file diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.jelly new file mode 100644 index 00000000..1e1a47d1 --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/config.jelly @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-jdk.html b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-jdk.html index 1394e578..6b7a0b4c 100644 --- a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-jdk.html +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-jdk.html @@ -1,5 +1,5 @@
- Select a JDK installation. If auto-install is enabled, the JDK will be downloaded and made available for the pipeline job. + Select a JDK installation. If auto-install is disabled, the JDK will be downloaded and made available for the pipeline job.

Note: This option does not work with docker.image('xxx').inside, the preinstalled JDK on the docker image will be used.

diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-maven.html b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-maven.html index 6bbc861e..a533fa5d 100644 --- a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-maven.html +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/WithMavenStep/help-maven.html @@ -1,5 +1,5 @@
- Select a Maven installation. If auto-install is enabled, maven will be downloaded and made available for the pipeline job. + Select a Maven installation. If auto-install is disabled, maven will be downloaded and made available for the pipeline job.

Note: This option does not work with docker.image('xxx').inside, the preinstalled maven on the docker image will be used.

diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher/config.jelly new file mode 100644 index 00000000..3ca3bd14 --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/FindbugsAnalysisPublisher/config.jelly @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher/config.jelly new file mode 100644 index 00000000..3ca3bd14 --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsPublisher/config.jelly @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher/config.jelly new file mode 100644 index 00000000..3ca3bd14 --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/JunitTestsPublisher/config.jelly @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/TasksScannerPublisher/config.jelly b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/TasksScannerPublisher/config.jelly new file mode 100644 index 00000000..3ca3bd14 --- /dev/null +++ b/jenkins-plugin/src/main/resources/org/jenkinsci/plugins/pipeline/maven/publishers/TasksScannerPublisher/config.jelly @@ -0,0 +1,35 @@ + + + + + + + + + diff --git a/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisherTest.java b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisherTest.java new file mode 100644 index 00000000..fc9e93c3 --- /dev/null +++ b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/MavenPublisherTest.java @@ -0,0 +1,47 @@ +package org.jenkinsci.plugins.pipeline.maven; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +import hudson.util.StreamTaskListener; +import org.hamcrest.CoreMatchers; +import org.jenkinsci.plugins.pipeline.maven.publishers.FindbugsAnalysisPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.GeneratedArtifactsPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.JunitTestsPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.TasksScannerPublisher; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import java.io.ByteArrayOutputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Cyrille Le Clerc + */ +public class MavenPublisherTest { + + @Rule + public JenkinsRule jenkinsRule = new JenkinsRule(); + + @Test + public void listMavenReporters() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + List mavenPublishers = MavenPublisher.buildReportersList(Collections.emptyList(), new StreamTaskListener(baos)); + Assert.assertThat(mavenPublishers.size(), CoreMatchers.is(4)); + + Map reportersByDescriptorId = new HashMap<>(); + for(MavenPublisher mavenPublisher : mavenPublishers) { + reportersByDescriptorId.put(mavenPublisher.getDescriptor().getId(), mavenPublisher); + } + assertThat(reportersByDescriptorId.containsKey(new GeneratedArtifactsPublisher.DescriptorImpl().getId()), is(true)); + assertThat(reportersByDescriptorId.containsKey(new FindbugsAnalysisPublisher.DescriptorImpl().getId()), is(true)); + assertThat(reportersByDescriptorId.containsKey(new JunitTestsPublisher.DescriptorImpl().getId()), is(true)); + assertThat(reportersByDescriptorId.containsKey(new TasksScannerPublisher.DescriptorImpl().getId()), is(true)); + } +} diff --git a/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepOnMasterTest.java b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepOnMasterTest.java index aab21b45..b5d425a1 100644 --- a/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepOnMasterTest.java +++ b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/WithMavenStepOnMasterTest.java @@ -26,12 +26,8 @@ import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; -import com.google.common.base.Function; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; import hudson.model.Fingerprint; import hudson.model.Result; -import hudson.model.Run; import hudson.plugins.tasks.TasksResultAction; import hudson.tasks.Fingerprinter; import hudson.tasks.Maven; @@ -41,20 +37,22 @@ import jenkins.mvn.FilePathGlobalSettingsProvider; import jenkins.mvn.FilePathSettingsProvider; import jenkins.mvn.GlobalMavenConfig; -import jenkins.mvn.GlobalSettingsProvider; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.scm.impl.mock.GitSampleRepoRuleUtils; import org.apache.commons.io.FileUtils; -import org.hamcrest.CoreMatchers; +import org.jenkinsci.Symbol; import org.jenkinsci.plugins.configfiles.GlobalConfigFiles; import org.jenkinsci.plugins.configfiles.maven.GlobalMavenSettingsConfig; import org.jenkinsci.plugins.configfiles.maven.MavenSettingsConfig; import org.jenkinsci.plugins.configfiles.maven.job.MvnGlobalSettingsProvider; import org.jenkinsci.plugins.configfiles.maven.job.MvnSettingsProvider; +import org.jenkinsci.plugins.pipeline.maven.publishers.FindbugsAnalysisPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.GeneratedArtifactsPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.JunitTestsPublisher; +import org.jenkinsci.plugins.pipeline.maven.publishers.TasksScannerPublisher; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; -import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -68,11 +66,8 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; -import java.util.List; import java.util.Map; -import javax.annotation.Nullable; - /** * TODO migrate to {@link WithMavenStepTest} once we have implemented a GitRepoRule that can be used on remote agents */ @@ -128,6 +123,7 @@ public void maven_build_on_master_with_specified_maven_installation_succeeds() t jenkinsRule.assertLogContains("under jenkins/mvn/test/mono-module-maven-app/0.1-SNAPSHOT/mono-module-maven-app-0.1-SNAPSHOT.jar", build); } + @Test public void maven_build_on_master_with_missing_specified_maven_installation_fails() throws Exception { loadMavenJarProjectInGitRepo(this.gitRepoRule); @@ -194,6 +190,76 @@ public void maven_build_jar_project_on_master_succeeds() throws Exception { assertThat(tasksResultAction.getProjectActions().size(), is(1)); } + @Test + public void maven_build_jar_project_on_master_disable_findbugs_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new FindbugsAnalysisPublisher.DescriptorImpl(), "findbugsPublisher", true); + } + + @Test + public void maven_build_jar_project_on_master_disable_tasks_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new TasksScannerPublisher.DescriptorImpl(), "openTasksPublisher", true); + } + + @Test + public void maven_build_jar_project_on_master_disable_junit_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new JunitTestsPublisher.DescriptorImpl(), "junitPublisher", true); + } + + @Test + public void maven_build_jar_project_on_master_disable_generated_artifacts_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new GeneratedArtifactsPublisher.DescriptorImpl(), "artifactsPublisher", true); + } + + @Test + public void maven_build_jar_project_on_master_force_enable_findbugs_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new FindbugsAnalysisPublisher.DescriptorImpl(), "findbugsPublisher", false); + } + + @Test + public void maven_build_jar_project_on_master_force_enable_tasks_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new TasksScannerPublisher.DescriptorImpl(), "openTasksPublisher", false); + } + + @Test + public void maven_build_jar_project_on_master_force_enable_junit_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new JunitTestsPublisher.DescriptorImpl(), "junitPublisher", false); + } + + @Test + public void maven_build_jar_project_on_master_force_enable_generated_artifacts_publisher_succeeds() throws Exception { + maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(new GeneratedArtifactsPublisher.DescriptorImpl(), "artifactsPublisher", false); + } + + + private void maven_build_jar_project_on_master_with_disabled_publisher_param_succeeds(MavenPublisher.DescriptorImpl descriptor, String symbol, boolean disabled) throws Exception { + + String displayName = descriptor.getDisplayName(); + + Symbol symbolAnnotation = descriptor.getClass().getAnnotation(Symbol.class); + String[] symbols = symbolAnnotation.value(); + assertThat(new String[]{symbol}, is(symbols)); + + loadMavenJarProjectInGitRepo(this.gitRepoRule); + + String pipelineScript = "node('master') {\n" + + " git($/" + gitRepoRule.toString() + "/$)\n" + + " withMaven(options:[" + symbol + "(disabled:" + disabled + ")]) {\n" + + " sh 'mvn package verify'\n" + + " }\n" + + "}"; + + WorkflowJob pipeline = jenkinsRule.createProject(WorkflowJob.class, "build-on-master-" + symbol + "-publisher-disabled-" + disabled); + pipeline.setDefinition(new CpsFlowDefinition(pipelineScript, true)); + WorkflowRun build = jenkinsRule.assertBuildStatus(Result.SUCCESS, pipeline.scheduleBuild2(0)); + + String message = "[withMaven] Skip '" + displayName + "' disabled by configuration"; + if (disabled) { + jenkinsRule.assertLogContains(message, build); + } else { + jenkinsRule.assertLogNotContains(message, build); + } + } + @Test public void maven_build_maven_jar_with_flatten_pom_project_on_master_succeeds() throws Exception { loadMavenJarWithFlattenPomProjectInGitRepo(this.gitRepoRule); diff --git a/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporterTest.java b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsReporterTest.java similarity index 97% rename from jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporterTest.java rename to jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsReporterTest.java index 2c1f6f16..ffd5f382 100644 --- a/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/reporters/GeneratedArtifactsReporterTest.java +++ b/jenkins-plugin/src/test/java/org/jenkinsci/plugins/pipeline/maven/publishers/GeneratedArtifactsReporterTest.java @@ -1,4 +1,4 @@ -package org.jenkinsci.plugins.pipeline.maven.reporters; +package org.jenkinsci.plugins.pipeline.maven.publishers; import hudson.FilePath; import org.hamcrest.CoreMatchers; @@ -31,7 +31,7 @@ public class GeneratedArtifactsReporterTest { * generated on Windows */ Document mavenSpyLogsOnWindows; - GeneratedArtifactsReporter generatedArtifactsReporter = new GeneratedArtifactsReporter(); + GeneratedArtifactsPublisher generatedArtifactsReporter = new GeneratedArtifactsPublisher(); @Before public void before() throws Exception {