From e603edc33e7ca71ae5f2959e7a26d53d47103def Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Wed, 26 Aug 2020 21:19:22 +0200 Subject: [PATCH 1/4] #201 Expand detecting if console note needs a LF. Do it also for kubernetes plugin. Test integration with timestamper --- pom.xml | 6 +++ .../plugins/ansicolor/AnsiColorStep.java | 10 +++- .../plugins/ansicolor/JenkinsTestSupport.java | 2 +- .../ansicolor/TimestamperIntegrationTest.java | 46 +++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/test/java/hudson/plugins/ansicolor/TimestamperIntegrationTest.java diff --git a/pom.xml b/pom.xml index f8848ee..edb8bc5 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,12 @@ 3.16.1 test + + org.jenkins-ci.plugins + timestamper + 1.11.3 + test + diff --git a/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java b/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java index fb53bd5..dafce92 100644 --- a/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java +++ b/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java @@ -15,6 +15,7 @@ import javax.annotation.Nonnull; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.Set; import java.util.logging.Level; @@ -123,6 +124,11 @@ public Set> getRequiredContext() { private static class AnsiColorExecution extends BodyExecutionCallback { private static final Logger LOGGER = Logger.getLogger(AnsiColorExecution.class.getName()); + private static final String[] DISTURBING_DECORATORS = new String[]{ + "timestamper.pipeline.GlobalDecorator", + "kubernetes.pipeline.SecretsMasker" + }; + private final String colorMapName; private Boolean needsPrintln; @@ -165,8 +171,8 @@ private void issueAction(StepContext context, ColorizedAction action) { private boolean needsPrintln() { if (needsPrintln == null) { - needsPrintln = ExtensionList.lookup(TaskListenerDecorator.Factory.class).stream() - .anyMatch(f -> f.getClass().getName().contains("hudson.plugins.timestamper.pipeline.GlobalDecorator")); + needsPrintln = ExtensionList.lookup(TaskListenerDecorator.Factory.class).stream().map(f -> f.getClass().getName()) + .anyMatch(n -> Arrays.stream(DISTURBING_DECORATORS).anyMatch(n::contains)); } return needsPrintln; } diff --git a/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java index a67b683..0534fca 100644 --- a/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java +++ b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java @@ -31,7 +31,7 @@ protected void assertOutputOnRunningPipeline(Collection expectedOutput, @Override public void evaluate() throws Throwable { - final WorkflowJob project = jenkinsRule.j.jenkins.createProject(WorkflowJob.class, "p"); + final WorkflowJob project = jenkinsRule.j.jenkins.createProject(WorkflowJob.class, "test-project-" + JenkinsTestSupport.this.getClass().getSimpleName()); project.setDefinition(new CpsFlowDefinition(pipelineScript, true)); jenkinsRule.j.assertBuildStatusSuccess(project.scheduleBuild2(0)); StringWriter writer = new StringWriter(); diff --git a/src/test/java/hudson/plugins/ansicolor/TimestamperIntegrationTest.java b/src/test/java/hudson/plugins/ansicolor/TimestamperIntegrationTest.java new file mode 100644 index 0000000..4620683 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/TimestamperIntegrationTest.java @@ -0,0 +1,46 @@ +package hudson.plugins.ansicolor; + +import org.junit.Test; + +import java.util.Arrays; + +public class TimestamperIntegrationTest extends JenkinsTestSupport { + + @Test + public void canTriggerFunctionalityTimestamperFirst() { + final String script = "timestamps {" + + "ansiColor('xterm') {" + + "echo '\033[34mHello\033[0m \033[33mcolorful\033[0m \033[35mworld!\033[0m'" + + "}" + + "}"; + canTriggerFunctionality(script); + } + + + @Test + public void canTriggerFunctionalityTimestamperLast() { + final String script = "ansiColor('xterm') {" + + "timestamps {" + + "echo '\033[34mHello\033[0m \033[33mcolorful\033[0m \033[35mworld!\033[0m'" + + "}" + + "}"; + canTriggerFunctionality(script); + } + + private void canTriggerFunctionality(String script) { + assertOutputOnRunningPipeline( + Arrays.asList( + "Hello", + "colorful", + "world!" + ), + Arrays.asList( + "\033[34mHello", + "\033[33mcolorful", + "\033[35mworld!" + ), + script, + false + ); + } +} From 9444fabb3ac10c2d2e577cf6f645a0be69b723a9 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Thu, 10 Sep 2020 22:41:36 +0200 Subject: [PATCH 2/4] #201 Check for plugins swallowing up ConsoleNotes. Render artificial NL to allow ansicolor to work despite. Add tests --- pom.xml | 6 +++ .../plugins/ansicolor/AnsiColorStep.java | 19 ++++---- .../plugins/ansicolor/AnsiColorStepTest.java | 44 +++++++++++++++++++ .../kubernetes/pipeline/SecretsMasker.java | 21 +++++++++ .../timestamper/pipeline/GlobalDecorator.java | 15 +++++++ 5 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 src/test/java/hudson/plugins/ansicolor/mock/kubernetes/pipeline/SecretsMasker.java create mode 100644 src/test/java/hudson/plugins/ansicolor/mock/timestamper/pipeline/GlobalDecorator.java diff --git a/pom.xml b/pom.xml index c28ffbb..e8833a7 100644 --- a/pom.xml +++ b/pom.xml @@ -98,6 +98,12 @@ timestamper 1.11.3 test + + + org.jenkins-ci.plugins.workflow + workflow-api + + org.awaitility diff --git a/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java b/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java index 4e22ee2..dac7416 100644 --- a/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java +++ b/src/main/java/hudson/plugins/ansicolor/AnsiColorStep.java @@ -15,9 +15,7 @@ import javax.annotation.Nonnull; import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.Set; +import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -125,15 +123,17 @@ public Set> getRequiredContext() { private static class AnsiColorExecution extends BodyExecutionCallback { private static final Logger LOGGER = Logger.getLogger(AnsiColorExecution.class.getName()); - private static final String[] DISTURBING_DECORATORS = new String[]{ - "timestamper.pipeline.GlobalDecorator", - "kubernetes.pipeline.SecretsMasker" - }; + private static final Map, String[]> EXTENSIONS_NL = new HashMap<>(); private final String colorMapName; private Boolean needsPrintln; + static { + EXTENSIONS_NL.put(DynamicContext.Typed.class, new String[]{"kubernetes.pipeline.SecretsMasker"}); + EXTENSIONS_NL.put(TaskListenerDecorator.Factory.class, new String[]{"timestamper.pipeline.GlobalDecorator"}); + } + public AnsiColorExecution(String colorMapName) { this.colorMapName = colorMapName; } @@ -178,8 +178,9 @@ private void issueAction(StepContext context, ColorizedAction action) { private boolean needsPrintln() { if (needsPrintln == null) { - needsPrintln = ExtensionList.lookup(TaskListenerDecorator.Factory.class).stream().map(f -> f.getClass().getName()) - .anyMatch(n -> Arrays.stream(DISTURBING_DECORATORS).anyMatch(n::contains)); + needsPrintln = EXTENSIONS_NL.entrySet().stream().anyMatch(e -> + ExtensionList.lookup(e.getKey()).stream().map(ext -> ext.getClass().getName()).anyMatch(n -> Arrays.stream(e.getValue()).anyMatch(n::contains)) + ); } return needsPrintln; } diff --git a/src/test/java/hudson/plugins/ansicolor/AnsiColorStepTest.java b/src/test/java/hudson/plugins/ansicolor/AnsiColorStepTest.java index 69b0b55..7828824 100644 --- a/src/test/java/hudson/plugins/ansicolor/AnsiColorStepTest.java +++ b/src/test/java/hudson/plugins/ansicolor/AnsiColorStepTest.java @@ -1,9 +1,14 @@ package hudson.plugins.ansicolor; +import hudson.ExtensionList; import hudson.model.queue.QueueTaskFuture; +import hudson.plugins.ansicolor.mock.kubernetes.pipeline.SecretsMasker; +import hudson.plugins.ansicolor.mock.timestamper.pipeline.GlobalDecorator; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator; +import org.jenkinsci.plugins.workflow.steps.DynamicContext; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -12,7 +17,11 @@ import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.LoggerRule; import org.jvnet.hudson.test.RestartableJenkinsRule; +import org.mockito.Mockito; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.IOException; import java.io.StringWriter; import java.time.Duration; import java.util.Arrays; @@ -167,6 +176,18 @@ public void evaluate() throws Throwable { }); } + @Test + public void willPrintAdditionalNlOnKubernetesPlugin() { + ExtensionList.lookup(DynamicContext.Typed.class).add(0, new SecretsMasker()); + assertNlsOnRunningPipeline(); + } + + @Test + public void willPrintAdditionalNlOnTimestamperPlugin() { + ExtensionList.lookup(TaskListenerDecorator.Factory.class).add(0, new GlobalDecorator()); + assertNlsOnRunningPipeline(); + } + private void assertOutputOnRunningPipeline(Collection expectedOutput, Collection notExpectedOutput, String pipelineScript) { story.addStep(new Statement() { @@ -183,4 +204,27 @@ public void evaluate() throws Throwable { } }); } + + private void assertNlsOnRunningPipeline() { + story.addStep(new Statement() { + + @Override + public void evaluate() throws Throwable { + final String script = "ansiColor('xterm') {\n" + + "echo '\033[34mHello\033[0m \033[33mcolorful\033[0m \033[35mworld!\033[0m'" + + "}"; + final WorkflowJob project = story.j.jenkins.createProject(WorkflowJob.class, "willPrintAdditionalNlOnKubernetesPlugin"); + project.setDefinition(new CpsFlowDefinition(script, true)); + story.j.assertBuildStatusSuccess(project.scheduleBuild2(0)); + StringWriter writer = new StringWriter(); + assertTrue(project.getLastBuild().getLogText().writeHtmlTo(0, writer) > 0); + final String html = writer.toString().replaceAll("", "") + .replaceAll("", "") + .replaceAll("", "") + .replaceAll("", ""); + final String nl = System.lineSeparator(); + assertThat(html).contains("ansiColor" + nl + "[Pipeline] {" + nl + nl).contains("[Pipeline] }" + nl + nl + "[Pipeline] // ansiColor"); + } + }); + } } diff --git a/src/test/java/hudson/plugins/ansicolor/mock/kubernetes/pipeline/SecretsMasker.java b/src/test/java/hudson/plugins/ansicolor/mock/kubernetes/pipeline/SecretsMasker.java new file mode 100644 index 0000000..3a87263 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/mock/kubernetes/pipeline/SecretsMasker.java @@ -0,0 +1,21 @@ +package hudson.plugins.ansicolor.mock.kubernetes.pipeline; + +import org.jenkinsci.plugins.workflow.steps.DynamicContext; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.IOException; + +public class SecretsMasker extends DynamicContext.Typed { + @Nonnull + @Override + protected Class type() { + return String.class; + } + + @CheckForNull + @Override + protected String get(DelegatedContext context) throws IOException, InterruptedException { + return null; + } +} diff --git a/src/test/java/hudson/plugins/ansicolor/mock/timestamper/pipeline/GlobalDecorator.java b/src/test/java/hudson/plugins/ansicolor/mock/timestamper/pipeline/GlobalDecorator.java new file mode 100644 index 0000000..a9325a5 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/mock/timestamper/pipeline/GlobalDecorator.java @@ -0,0 +1,15 @@ +package hudson.plugins.ansicolor.mock.timestamper.pipeline; + +import org.jenkinsci.plugins.workflow.flow.FlowExecutionOwner; +import org.jenkinsci.plugins.workflow.log.TaskListenerDecorator; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public class GlobalDecorator implements TaskListenerDecorator.Factory { + @CheckForNull + @Override + public TaskListenerDecorator of(@Nonnull FlowExecutionOwner owner) { + return null; + } +} From 9b9c66f6c0bf58aa0c13e12f4333055715ac5565 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Fri, 11 Sep 2020 21:59:57 +0200 Subject: [PATCH 3/4] Dont specify timestamper version to avoid library conflicts --- pom.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pom.xml b/pom.xml index e8833a7..8918ebf 100644 --- a/pom.xml +++ b/pom.xml @@ -96,14 +96,7 @@ org.jenkins-ci.plugins timestamper - 1.11.3 test - - - org.jenkins-ci.plugins.workflow - workflow-api - - org.awaitility From d55e3b7ee2e8903c704b62605b3d5c713da25fc1 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Tue, 15 Sep 2020 21:03:49 +0200 Subject: [PATCH 4/4] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbc1d4..84170e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ============ * [#202](https://github.com/jenkinsci/ansicolor-plugin/pull/202): Render logs correctly for long running jobs which generate lots of output - [@tszmytka](https://github.com/tszmytka). +* [#203](https://github.com/jenkinsci/ansicolor-plugin/pull/203): Interaction with kubernetes plugin - [@tszmytka](https://github.com/tszmytka). * Your contribution here.