diff --git a/src/main/java/hudson/plugins/textfinder/TextFinderModel.java b/src/main/java/hudson/plugins/textfinder/TextFinderModel.java new file mode 100644 index 0000000..d23e98f --- /dev/null +++ b/src/main/java/hudson/plugins/textfinder/TextFinderModel.java @@ -0,0 +1,78 @@ +package hudson.plugins.textfinder; + +import hudson.Extension; +import hudson.Util; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Descriptor; +import java.io.Serializable; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import org.kohsuke.stapler.DataBoundConstructor; + +public final class TextFinderModel extends AbstractDescribableImpl + implements Serializable { + private static final long serialVersionUID = 1L; + + private final String fileSet; + private final String regexp; + private final boolean succeedIfFound; + private final boolean unstableIfFound; + private final boolean notBuiltIfFound; + /** True to also scan the whole console output */ + private final boolean alsoCheckConsoleOutput; + + @DataBoundConstructor + public TextFinderModel( + String fileSet, + String regexp, + boolean succeedIfFound, + boolean unstableIfFound, + boolean alsoCheckConsoleOutput, + boolean notBuiltIfFound) { + this.fileSet = fileSet != null ? Util.fixEmpty(fileSet.trim()) : null; + this.regexp = regexp; + this.succeedIfFound = succeedIfFound; + this.unstableIfFound = unstableIfFound; + this.alsoCheckConsoleOutput = alsoCheckConsoleOutput; + this.notBuiltIfFound = notBuiltIfFound; + + // Attempt to compile regular expression + try { + Pattern.compile(regexp); + } catch (PatternSyntaxException e) { + // falls through + } + } + + public String getFileSet() { + return fileSet; + } + + public String getRegexp() { + return regexp; + } + + public boolean isSucceedIfFound() { + return succeedIfFound; + } + + public boolean isUnstableIfFound() { + return unstableIfFound; + } + + public boolean isNotBuiltIfFound() { + return notBuiltIfFound; + } + + public boolean isAlsoCheckConsoleOutput() { + return alsoCheckConsoleOutput; + } + + @Extension + public static class DescriptorImpl extends Descriptor { + @Override + public String getDisplayName() { + return "Text Finder"; + } + } +} diff --git a/src/main/java/hudson/plugins/textfinder/TextFinderPublisher.java b/src/main/java/hudson/plugins/textfinder/TextFinderPublisher.java index 3e72e88..76b86a7 100644 --- a/src/main/java/hudson/plugins/textfinder/TextFinderPublisher.java +++ b/src/main/java/hudson/plugins/textfinder/TextFinderPublisher.java @@ -25,11 +25,14 @@ import java.io.PrintStream; import java.io.Serializable; import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.servlet.ServletException; import jenkins.MasterToSlaveFileCallable; +import jenkins.model.Jenkins; import jenkins.tasks.SimpleBuildStep; import org.apache.commons.io.IOUtils; import org.apache.tools.ant.DirectoryScanner; @@ -37,7 +40,6 @@ import org.apache.tools.ant.types.FileSet; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; /** @@ -48,63 +50,53 @@ */ public class TextFinderPublisher extends Recorder implements Serializable, SimpleBuildStep { - public String fileSet; - public final String regexp; - public boolean succeedIfFound; - public boolean unstableIfFound; - public boolean notBuiltIfFound; - /** True to also scan the whole console output */ - public boolean alsoCheckConsoleOutput; + /** This is the primary text finder in the configuration. */ + private final TextFinderModel primaryTextFinder; - @DataBoundConstructor - public TextFinderPublisher(String regexp) { - this.regexp = regexp; + /** Additional text finder configurations are stored here. */ + private final List additionalTextFinders; - // Attempt to compile regular expression - try { - Pattern.compile(regexp); - } catch (PatternSyntaxException e) { - // falls through - } - } - - @Deprecated + /** + * @param fileSet Kept for backward compatibility with old configuration. + * @param regexp Kept for backward compatibility with old configuration. + * @param succeedIfFound Kept for backward compatibility with old configuration. + * @param unstableIfFound Kept for backward compatibility with old configuration. + * @param notBuiltIfFound Kept for backward compatibility with old configuration. + * @param alsoCheckConsoleOutput Kept for backward compatibility with old configuration. + * @param additionalTextFinders configuration for additional textFinders + */ + @DataBoundConstructor public TextFinderPublisher( String fileSet, String regexp, boolean succeedIfFound, boolean unstableIfFound, - boolean alsoCheckConsoleOutput) { - this(regexp); - this.fileSet = Util.fixEmpty(fileSet.trim()); - this.succeedIfFound = succeedIfFound; - this.unstableIfFound = unstableIfFound; - this.alsoCheckConsoleOutput = alsoCheckConsoleOutput; - } - - @DataBoundSetter - public void setFileSet(String fileSet) { - this.fileSet = Util.fixEmpty(fileSet.trim()); - } - - @DataBoundSetter - public void setSucceedIfFound(boolean succeedIfFound) { - this.succeedIfFound = succeedIfFound; - } - - @DataBoundSetter - public void setUnstableIfFound(boolean unstableIfFound) { - this.unstableIfFound = unstableIfFound; - } - - @DataBoundSetter - public void setNotBuiltIfFound(boolean notBuiltIfFound) { - this.notBuiltIfFound = notBuiltIfFound; - } + boolean notBuiltIfFound, + boolean alsoCheckConsoleOutput, + List additionalTextFinders) { + this.primaryTextFinder = + new TextFinderModel( + Util.fixEmpty(fileSet != null ? fileSet.trim() : ""), + regexp, + succeedIfFound, + unstableIfFound, + alsoCheckConsoleOutput, + notBuiltIfFound); + + this.additionalTextFinders = new ArrayList<>(); + if (additionalTextFinders != null && !additionalTextFinders.isEmpty()) { + this.additionalTextFinders.addAll(additionalTextFinders); + } - @DataBoundSetter - public void setAlsoCheckConsoleOutput(boolean alsoCheckConsoleOutput) { - this.alsoCheckConsoleOutput = alsoCheckConsoleOutput; + // Attempt to compile regular expressions + try { + Pattern.compile(primaryTextFinder.getRegexp()); + for (TextFinderModel textFinder : this.additionalTextFinders) { + Pattern.compile(textFinder.getRegexp()); + } + } catch (PatternSyntaxException e) { + // falls through + } } @Override @@ -115,57 +107,64 @@ public BuildStepMonitor getRequiredMonitorService() { @Override public void perform(Run run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException { - findText(run, workspace, listener); + findText(primaryTextFinder, run, workspace, listener); + for (TextFinderModel additionalTextFinder : additionalTextFinders) { + findText(additionalTextFinder, run, workspace, listener); + } } /** Indicates an orderly abortion of the processing. */ private static final class AbortException extends RuntimeException {} - private void findText(Run run, FilePath workspace, TaskListener listener) + private void findText( + TextFinderModel textFinder, Run run, FilePath workspace, TaskListener listener) throws IOException, InterruptedException { try { PrintStream logger = listener.getLogger(); boolean foundText = false; - if (alsoCheckConsoleOutput) { + if (textFinder.isAlsoCheckConsoleOutput()) { // Do not mention the pattern we are looking for to avoid false positives logger.println("[Text Finder] Scanning console output..."); foundText |= checkFile( run.getLogFile(), - compilePattern(logger, regexp), + compilePattern(logger, textFinder.getRegexp()), logger, run.getCharset(), true); logger.println( "[Text Finder] Finished looking for pattern " + "'" - + regexp + + textFinder.getRegexp() + "'" + " in the console output"); } final RemoteOutputStream ros = new RemoteOutputStream(logger); - if (fileSet != null) { + if (textFinder.getFileSet() != null) { logger.println( "[Text Finder] Looking for pattern " + "'" - + regexp + + textFinder.getRegexp() + "'" + " in the files at " + "'" - + fileSet + + textFinder.getFileSet() + "'"); - foundText |= workspace.act(new FileChecker(ros, fileSet, regexp)); + foundText |= + workspace.act( + new FileChecker( + ros, textFinder.getFileSet(), textFinder.getRegexp())); } - if (foundText != succeedIfFound) { + if (foundText != textFinder.isSucceedIfFound()) { final Result finalResult; - if (notBuiltIfFound) { + if (textFinder.isNotBuiltIfFound()) { finalResult = Result.NOT_BUILT; } else { - finalResult = unstableIfFound ? Result.UNSTABLE : Result.FAILURE; + finalResult = textFinder.isUnstableIfFound() ? Result.UNSTABLE : Result.FAILURE; } run.setResult(finalResult); } @@ -228,6 +227,46 @@ private static Pattern compilePattern(PrintStream logger, String regexp) { return pattern; } + /** cut off first item as it is provided with getters */ + @SuppressWarnings("unused") + public List getAdditionalTextFinders() { + return additionalTextFinders; + } + + // backward compatibility getters below + // all these getters are there for backward compatibility + // get first field from list which is used to store original config values + @SuppressWarnings("unused") + public String getFileSet() { + return this.primaryTextFinder.getFileSet(); + } + + @SuppressWarnings("unused") + public String getRegexp() { + return this.primaryTextFinder.getRegexp(); + } + + @SuppressWarnings("unused") + public boolean isSucceedIfFound() { + return this.primaryTextFinder.isSucceedIfFound(); + } + + @SuppressWarnings("unused") + public boolean isUnstableIfFound() { + return this.primaryTextFinder.isUnstableIfFound(); + } + + @SuppressWarnings("unused") + public boolean isNotBuiltIfFound() { + return this.primaryTextFinder.isNotBuiltIfFound(); + } + + @SuppressWarnings("unused") + public boolean isAlsoCheckConsoleOutput() { + return this.primaryTextFinder.isAlsoCheckConsoleOutput(); + } + // backward compatibility getters above + @Symbol("findText") @Extension public static final class DescriptorImpl extends BuildStepDescriptor { @@ -246,6 +285,15 @@ public boolean isApplicable(Class jobType) { return true; } + public List getItemDescriptors() { + Jenkins jenkins = Jenkins.getInstance(); + if (jenkins != null) { + return jenkins.getDescriptorList(TextFinderModel.class); + } else { + throw new NullPointerException("not able to get Jenkins instance"); + } + } + /** * Checks the regular expression validity. * diff --git a/src/main/resources/hudson/plugins/textfinder/TextFinderModel/config.jelly b/src/main/resources/hudson/plugins/textfinder/TextFinderModel/config.jelly new file mode 100644 index 0000000..a130914 --- /dev/null +++ b/src/main/resources/hudson/plugins/textfinder/TextFinderModel/config.jelly @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/textfinder/TextFinderPublisher/config.jelly b/src/main/resources/hudson/plugins/textfinder/TextFinderPublisher/config.jelly index 41debca..59c555c 100644 --- a/src/main/resources/hudson/plugins/textfinder/TextFinderPublisher/config.jelly +++ b/src/main/resources/hudson/plugins/textfinder/TextFinderPublisher/config.jelly @@ -19,4 +19,12 @@ + + + diff --git a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherAgentTest.java b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherAgentTest.java index 4dcdb48..efc7108 100644 --- a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherAgentTest.java +++ b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherAgentTest.java @@ -90,4 +90,30 @@ public void failureIfFoundInConsoleOnAgent() throws Exception { assertLogContainsMatch(build.getLogFile(), ECHO_UNIQUE_TEXT, build, true); rule.assertBuildStatus(Result.FAILURE, build); } + + @Test + public void lastFinderWins() throws Exception { + DumbSlave agent = rule.createOnlineSlave(); + WorkflowJob project = rule.createProject(WorkflowJob.class, "pipeline"); + project.setDefinition( + new CpsFlowDefinition( + String.format( + "node('%s') {isUnix() ? sh('echo foobar') : bat(\"prompt \\$G\\r\\necho foobar\")}\n" + + "node('%s') {" + + "findText regexp: 'foobar', alsoCheckConsoleOutput: true\n" + + "findText regexp: 'foobar', unstableIfFound: true, alsoCheckConsoleOutput: true\n" + + "findText regexp: 'foobar', notBuiltIfFound: true, alsoCheckConsoleOutput: true\n" + + "}", + agent.getNodeName(), agent.getNodeName()))); + WorkflowRun build = project.scheduleBuild2(0).get(); + rule.waitForCompletion(build); + rule.assertLogContains("[Text Finder] Scanning console output...", build); + rule.assertLogContains( + "[Text Finder] Finished looking for pattern '" + + UNIQUE_TEXT + + "' in the console output", + build); + assertLogContainsMatch(build.getLogFile(), ECHO_UNIQUE_TEXT, build, true); + rule.assertBuildStatus(Result.NOT_BUILT, build); + } } diff --git a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherFreestyleTest.java b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherFreestyleTest.java index 503cc8d..37946f9 100644 --- a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherFreestyleTest.java +++ b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherFreestyleTest.java @@ -14,7 +14,10 @@ import hudson.tasks.Shell; import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.junit.Rule; import org.junit.Test; @@ -49,9 +52,15 @@ public void successIfFoundInConsole() throws Exception { ? new BatchFile("prompt $G\n" + ECHO_UNIQUE_TEXT) : new Shell(ECHO_UNIQUE_TEXT); project.getBuildersList().add(command); - TextFinderPublisher textFinder = new TextFinderPublisher(UNIQUE_TEXT); - textFinder.setSucceedIfFound(true); - textFinder.setAlsoCheckConsoleOutput(true); + TextFinderPublisher textFinder = + new TextFinderPublisher( + "", + UNIQUE_TEXT, + true, + false, + false, + true, + Collections.emptyList()); project.getPublishersList().add(textFinder); FreeStyleBuild build = project.scheduleBuild2(0).get(); rule.waitForCompletion(build); @@ -73,8 +82,15 @@ public void failureIfFoundInConsole() throws Exception { ? new BatchFile("prompt $G\n" + ECHO_UNIQUE_TEXT) : new Shell(ECHO_UNIQUE_TEXT); project.getBuildersList().add(command); - TextFinderPublisher textFinder = new TextFinderPublisher(UNIQUE_TEXT); - textFinder.setAlsoCheckConsoleOutput(true); + TextFinderPublisher textFinder = + new TextFinderPublisher( + "", + UNIQUE_TEXT, + false, + false, + false, + true, + Collections.emptyList()); project.getPublishersList().add(textFinder); FreeStyleBuild build = project.scheduleBuild2(0).get(); rule.waitForCompletion(build); @@ -96,9 +112,15 @@ public void unstableIfFoundInConsole() throws Exception { ? new BatchFile("prompt $G\n" + ECHO_UNIQUE_TEXT) : new Shell(ECHO_UNIQUE_TEXT); project.getBuildersList().add(command); - TextFinderPublisher textFinder = new TextFinderPublisher(UNIQUE_TEXT); - textFinder.setUnstableIfFound(true); - textFinder.setAlsoCheckConsoleOutput(true); + TextFinderPublisher textFinder = + new TextFinderPublisher( + "", + UNIQUE_TEXT, + false, + true, + false, + true, + Collections.emptyList()); project.getPublishersList().add(textFinder); FreeStyleBuild build = project.scheduleBuild2(0).get(); rule.waitForCompletion(build); @@ -115,8 +137,15 @@ public void unstableIfFoundInConsole() throws Exception { @Test public void notFoundInConsole() throws Exception { FreeStyleProject project = rule.createFreeStyleProject(); - TextFinderPublisher textFinder = new TextFinderPublisher(UNIQUE_TEXT); - textFinder.setAlsoCheckConsoleOutput(true); + TextFinderPublisher textFinder = + new TextFinderPublisher( + "", + UNIQUE_TEXT, + false, + false, + false, + true, + Collections.emptyList()); project.getPublishersList().add(textFinder); FreeStyleBuild build = project.scheduleBuild2(0).get(); rule.waitForCompletion(build); @@ -129,6 +158,43 @@ public void notFoundInConsole() throws Exception { rule.assertBuildStatus(Result.SUCCESS, build); } + @Test + public void lastFinderWins() throws Exception { + FreeStyleProject project = rule.createFreeStyleProject("freestyle"); + CommandInterpreter command = + Functions.isWindows() + ? new BatchFile("prompt $G\n" + ECHO_UNIQUE_TEXT) + : new Shell(ECHO_UNIQUE_TEXT); + project.getBuildersList().add(command); + + List finders = new ArrayList<>(); + finders.add(new TextFinderModel("", UNIQUE_TEXT, false, true, false, true)); // 2nd + finders.add( + new TextFinderModel( + "", UNIQUE_TEXT, false, false, false, true)); // 3rd, this one must win + TextFinderPublisher textFinder = + new TextFinderPublisher( + "", + UNIQUE_TEXT, + false, + true, + false, + true, + finders); // 1st will be finder with args from constructor + project.getPublishersList().add(textFinder); + + FreeStyleBuild build = project.scheduleBuild2(0).get(); + rule.waitForCompletion(build); + rule.assertLogContains("[Text Finder] Scanning console output...", build); + rule.assertLogContains( + "[Text Finder] Finished looking for pattern '" + + UNIQUE_TEXT + + "' in the console output", + build); + assertLogContainsMatch(build.getLogFile(), ECHO_UNIQUE_TEXT, build, true); + rule.assertBuildStatus(Result.UNSTABLE, build); + } + @Test public void createTextFinderViaWebClient() throws Exception { FreeStyleProject project = rule.createFreeStyleProject(); @@ -169,9 +235,9 @@ public void createTextFinderViaWebClient() throws Exception { // Ensure that the Text Finder was configured correctly. assertEquals(1, project.getPublishersList().size()); TextFinderPublisher textFinder = (TextFinderPublisher) project.getPublishersList().get(0); - assertEquals("file1", textFinder.fileSet); - assertEquals(UNIQUE_TEXT, textFinder.regexp); - assertTrue(textFinder.unstableIfFound); - assertTrue(textFinder.alsoCheckConsoleOutput); + assertEquals("file1", textFinder.getFileSet()); + assertEquals(UNIQUE_TEXT, textFinder.getRegexp()); + assertTrue(textFinder.isUnstableIfFound()); + assertTrue(textFinder.isAlsoCheckConsoleOutput()); } } diff --git a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherPipelineTest.java b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherPipelineTest.java index fd43e53..f359c95 100644 --- a/src/test/java/hudson/plugins/textfinder/TextFinderPublisherPipelineTest.java +++ b/src/test/java/hudson/plugins/textfinder/TextFinderPublisherPipelineTest.java @@ -310,4 +310,27 @@ public void notFoundInConsole() throws Exception { build); rule.assertBuildStatus(Result.SUCCESS, build); } + + @Test + public void lastFinderWins() throws Exception { + WorkflowJob project = rule.createProject(WorkflowJob.class, "pipeline"); + project.setDefinition( + new CpsFlowDefinition( + "node {isUnix() ? sh('echo foobar') : bat(\"prompt \\$G\\r\\necho foobar\")}\n" + + "node {" + + "findText regexp: 'foobar', alsoCheckConsoleOutput: true\n" + + "findText regexp: 'foobar', unstableIfFound: true, alsoCheckConsoleOutput: true\n" + + "findText regexp: 'foobar', notBuiltIfFound: true, alsoCheckConsoleOutput: true\n" + + "}")); + WorkflowRun build = project.scheduleBuild2(0).get(); + rule.waitForCompletion(build); + rule.assertLogContains("[Text Finder] Scanning console output...", build); + rule.assertLogContains( + "[Text Finder] Finished looking for pattern '" + + UNIQUE_TEXT + + "' in the console output", + build); + assertLogContainsMatch(build.getLogFile(), ECHO_UNIQUE_TEXT, build, true); + rule.assertBuildStatus(Result.NOT_BUILT, build); + } }