From 18649eb22ee1ee6bd6b07f7c7bfd861b8a25c0b3 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Mon, 11 Dec 2023 22:23:15 +0100 Subject: [PATCH 01/34] Hide 'process >' and %age complete on narrow terminal window sizes Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/trace/AnsiLogObserver.groovy | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index fddae54250..9d1f52492c 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -378,14 +378,18 @@ class AnsiLogObserver implements TraceObserver { final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) + // Only show 'process >' if we have plenty of width available + final processlabel = cols > 180 ? ' process >' : ''; + if( tot == 0 ) - return "[$hh] process > $label -" + return "[$hh]$processlabel $label -" final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 - final pct = "[${String.valueOf(x).padLeft(3)}%]".toString() + // Only show %age complete if we have a bit of width available + final pct = cols > 120 ? "[${String.valueOf(x).padLeft(3)}%]".toString() : '|'; final numbs = "${(int)com} of ${(int)tot}".toString() - def result = "[${hh}] process > $label $pct $numbs" + def result = "[${hh}]$processlabel $label $pct $numbs" if( stats.cached ) result += ", cached: $stats.cached" if( stats.stored ) From 82ec80b222141b596358e95536338f8ed99f4680 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Mon, 11 Dec 2023 22:30:12 +0100 Subject: [PATCH 02/34] Truncate the start of the process name instead of the end Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 9d1f52492c..fd3572e2c5 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -369,7 +369,7 @@ class AnsiLogObserver implements TraceObserver { protected String fmtChop(String str, int cols) { if( str.size() <= cols ) return str - return cols>3 ? str[0..(cols-3-1)] + '...' : str[0..cols-1] + return str.take(3) + '...' + str.takeRight(cols-3-3) } protected String line(ProgressRecord stats) { From cecb4af9c6c09e7c5c96845dc453d6cfc0700f50 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Mon, 11 Dec 2023 23:30:59 +0100 Subject: [PATCH 03/34] =?UTF-8?q?=20=F0=9F=9A=80=20=20=20N=20E=20X=20T=20F?= =?UTF-8?q?=20L=20O=20W=20=20~=20=20bling=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/cli/CmdRun.groovy | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 8f3edf8b06..633f474ae3 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -45,7 +45,11 @@ import nextflow.secret.SecretsLoader import nextflow.util.CustomPoolFactory import nextflow.util.Duration import nextflow.util.HistoryFile +import org.fusesource.jansi.AnsiConsole import org.yaml.snakeyaml.Yaml +import static org.fusesource.jansi.Ansi.Attribute +import static org.fusesource.jansi.Ansi.Color +import static org.fusesource.jansi.Ansi.ansi /** * CLI sub-command RUN * @@ -310,7 +314,15 @@ class CmdRun extends CmdBase implements HubOptions { checkRunName() - log.info "N E X T F L O W ~ version ${Const.APP_VER}" + log.debug "N E X T F L O W ~ version ${Const.APP_VER}" + def fmt = ansi() + fmt = fmt.a("\n") + fmt = fmt.a(" 🚀 ") + fmt = fmt.bg(Color.GREEN).fg(Color.BLACK).a(" N E X T F L O W ").reset() + fmt = fmt.fg(Color.BLACK).a(" ~ ") + fmt = fmt.fg(Color.GREEN).a("version " + Const.APP_VER).reset() + fmt = fmt.a("\n") + AnsiConsole.out.println(fmt.eraseLine()) Plugins.init() // -- specify the arguments @@ -398,10 +410,19 @@ class CmdRun extends CmdBase implements HubOptions { final ver = NF.dsl2 ? DSL2 : DSL1 final repo = scriptFile.repository ?: scriptFile.source final head = preview ? "* PREVIEW * $scriptFile.repository" : "Launching `$repo`" - if( scriptFile.repository ) - log.info "${head} [$runName] DSL${ver} - revision: ${scriptFile.revisionInfo}" - else - log.info "${head} [$runName] DSL${ver} - revision: ${scriptFile.getScriptId()?.substring(0,10)}" + final revision = scriptFile.repository ? scriptFile.revisionInfo : scriptFile.getScriptId()?.substring(0,10) + + log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" + + def fmt = ansi() + fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ") + fmt = fmt.fg(Color.BLACK).a("[").reset() + fmt = fmt.bold().fg(Color.CYAN).a(runName).reset() + fmt = fmt.fg(Color.BLACK).a("]") + fmt = fmt.a(" DSL${ver} - ") + fmt = fmt.fg(Color.MAGENTA).a("revision: ").bold().a(revision) + fmt = fmt.reset().a("\n") + AnsiConsole.out.println(fmt.eraseLine()) } static String detectDslMode(ConfigMap config, String scriptText, Map sysEnv) { From f42c7ee0acf0fbfef2bdd801eca9e2e4d973e245 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Tue, 12 Dec 2023 00:08:23 +0100 Subject: [PATCH 04/34] Over the top fancy colours for ANSI log Signed-off-by: Phil Ewels --- .../nextflow/trace/AnsiLogObserver.groovy | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index fd3572e2c5..e0cbd93e23 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -264,7 +264,7 @@ class AnsiLogObserver implements TraceObserver { // render line for( ProgressRecord entry : processes ) { - term.a(line(entry)) + term = line(entry, term) term.newline() } rendered = true @@ -372,35 +372,52 @@ class AnsiLogObserver implements TraceObserver { return str.take(3) + '...' + str.takeRight(cols-3-3) } - protected String line(ProgressRecord stats) { + protected Ansi line(ProgressRecord stats, Ansi term) { final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) - // Only show 'process >' if we have plenty of width available - final processlabel = cols > 180 ? ' process >' : ''; + final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 + final pct = "${String.valueOf(x).padLeft(3)}%".toString() + final numbs = " ${(int)com} of ${(int)tot}".toString() - if( tot == 0 ) - return "[$hh]$processlabel $label -" + // Hash: [] + term = term.fg(Color.BLACK).a('[').fg(Color.BLUE).a(hh).fg(Color.BLACK).a('] ') - final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 - // Only show %age complete if we have a bit of width available - final pct = cols > 120 ? "[${String.valueOf(x).padLeft(3)}%]".toString() : '|'; + // Label: process > sayHello + if (cols > 180) + term = term.a('process > ') + term = term.reset().a(label) + + // No tasks + if( tot == 0 ) { + term = term.a(" -") + return term + } + + // Progress: [ 0%] 0 of 10 + if (cols > 120) + term = term.fg(Color.BLACK).a(' [').fg(Color.GREEN).a(pct).fg(Color.BLACK).a(']').reset() + else + term = term.fg(Color.BLACK).a(' |').reset() + term = term.a(numbs) - final numbs = "${(int)com} of ${(int)tot}".toString() - def result = "[${hh}]$processlabel $label $pct $numbs" + // Number of tasks: if( stats.cached ) - result += ", cached: $stats.cached" + term = term.fg(Color.BLACK).a(", cached: $stats.cached").reset() if( stats.stored ) - result += ", stored: $stats.stored" + term = term.a(", stored: $stats.stored") if( stats.failed ) - result += ", failed: $stats.failed" + term = term.a(", failed: $stats.failed") if( stats.retries ) - result += ", retries: $stats.retries" + term = term.a(", retries: $stats.retries") if( stats.terminated && tot ) - result += stats.errored ? ' \u2718' : ' \u2714' - return fmtChop(result, cols) + if( stats.errored ) + term = term.fg(Color.RED).a(' \u2718' ).reset() + else + term = term.fg(Color.GREEN).a(' \u2714 ').reset() + return term } @Override From eb99f99ac99f97602ea786b6043b4eaab1b8c20b Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Tue, 12 Dec 2023 09:35:48 +0100 Subject: [PATCH 05/34] Try not to exceed terminal height Skip processes with zero tasks if we are out of display rows Signed-off-by: Phil Ewels --- .../nextflow/trace/AnsiLogObserver.groovy | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index e0cbd93e23..94e8d72c07 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -85,6 +85,8 @@ class AnsiLogObserver implements TraceObserver { private volatile int cols = 80 + private volatile int rows = 24 + private long startTimestamp private long endTimestamp @@ -249,6 +251,7 @@ class AnsiLogObserver implements TraceObserver { } cols = TerminalFactory.get().getWidth() + rows = TerminalFactory.get().getHeight() // calc max width final now = System.currentTimeMillis() @@ -263,10 +266,19 @@ class AnsiLogObserver implements TraceObserver { lastWidthReset = now // render line + def renderedLines = 0 + def skippedLines = 0 for( ProgressRecord entry : processes ) { - term = line(entry, term) - term.newline() + if( renderedLines <= rows - 5 || entry.getTotalCount() > 0 ) { + term = line(entry, term) + term.newline() + renderedLines += 1 + } else { + skippedLines += 1 + } } + if( skippedLines > 0 ) + term.a("Plus $skippedLines more processes waiting for tasks…").newline() rendered = true } @@ -369,7 +381,7 @@ class AnsiLogObserver implements TraceObserver { protected String fmtChop(String str, int cols) { if( str.size() <= cols ) return str - return str.take(3) + '...' + str.takeRight(cols-3-3) + return str.take(3) + '…' + str.takeRight(cols-1-3) } protected Ansi line(ProgressRecord stats, Ansi term) { From 89af89ff2063bebd72f4c25b01b6bc12379c7652 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Tue, 12 Dec 2023 09:36:42 +0100 Subject: [PATCH 06/34] Yellow process %ages until 100%, then green Signed-off-by: Phil Ewels --- .../groovy/nextflow/trace/AnsiLogObserver.groovy | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 94e8d72c07..f7230dc080 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -409,10 +409,16 @@ class AnsiLogObserver implements TraceObserver { } // Progress: [ 0%] 0 of 10 - if (cols > 120) - term = term.fg(Color.BLACK).a(' [').fg(Color.GREEN).a(pct).fg(Color.BLACK).a(']').reset() - else + if (cols > 120){ + term = term.fg(Color.BLACK).a(' [') + if( pct == "100%" ) + term = term.fg(Color.GREEN).a(pct) + else + term = term.fg(Color.YELLOW).a(pct) + term = term.fg(Color.BLACK).a(']').reset() + } else { term = term.fg(Color.BLACK).a(' |').reset() + } term = term.a(numbs) // Number of tasks: From 89455f2163183006b9c5fe2251531f0dda93e6f6 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Thu, 14 Dec 2023 00:12:47 -0600 Subject: [PATCH 07/34] minor cleanup Signed-off-by: Ben Sherman --- .../main/groovy/nextflow/cli/CmdRun.groovy | 4 +- .../nextflow/trace/AnsiLogObserver.groovy | 38 ++++++++++--------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 633f474ae3..4e6b562363 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -410,7 +410,9 @@ class CmdRun extends CmdBase implements HubOptions { final ver = NF.dsl2 ? DSL2 : DSL1 final repo = scriptFile.repository ?: scriptFile.source final head = preview ? "* PREVIEW * $scriptFile.repository" : "Launching `$repo`" - final revision = scriptFile.repository ? scriptFile.revisionInfo : scriptFile.getScriptId()?.substring(0,10) + final revision = scriptFile.repository + ? scriptFile.revisionInfo + : scriptFile.getScriptId()?.substring(0,10) log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index f7230dc080..8741dd230f 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -273,7 +273,8 @@ class AnsiLogObserver implements TraceObserver { term = line(entry, term) term.newline() renderedLines += 1 - } else { + } + else { skippedLines += 1 } } @@ -398,25 +399,28 @@ class AnsiLogObserver implements TraceObserver { term = term.fg(Color.BLACK).a('[').fg(Color.BLUE).a(hh).fg(Color.BLACK).a('] ') // Label: process > sayHello - if (cols > 180) + if( cols > 180 ) term = term.a('process > ') term = term.reset().a(label) // No tasks - if( tot == 0 ) { - term = term.a(" -") + if( tot == 0 ) { + term = term.a(' -') return term } // Progress: [ 0%] 0 of 10 - if (cols > 120){ - term = term.fg(Color.BLACK).a(' [') - if( pct == "100%" ) - term = term.fg(Color.GREEN).a(pct) - else - term = term.fg(Color.YELLOW).a(pct) - term = term.fg(Color.BLACK).a(']').reset() - } else { + if( cols > 120 ) { + term = term + .fg(Color.BLACK) + .a(' [') + .fg(pct == '100%' ? Color.GREEN : Color.YELLOW) + .a(pct) + .fg(Color.BLACK) + .a(']') + .reset() + } + else { term = term.fg(Color.BLACK).a(' |').reset() } term = term.a(numbs) @@ -430,11 +434,11 @@ class AnsiLogObserver implements TraceObserver { term = term.a(", failed: $stats.failed") if( stats.retries ) term = term.a(", retries: $stats.retries") - if( stats.terminated && tot ) - if( stats.errored ) - term = term.fg(Color.RED).a(' \u2718' ).reset() - else - term = term.fg(Color.GREEN).a(' \u2714 ').reset() + if( stats.terminated && tot ) { + term = stats.errored + ? term.fg(Color.RED).a(' \u2718' ).reset() + : term.fg(Color.GREEN).a(' \u2714 ').reset() + } return term } From 8ccf02d6305cb3edc51acdc76f728cefa26d6ad3 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Thu, 14 Dec 2023 06:22:15 -0600 Subject: [PATCH 08/34] Use Nextflow green in launch header Signed-off-by: Ben Sherman --- build.gradle | 2 +- modules/nextflow/build.gradle | 1 + .../src/main/groovy/nextflow/cli/CmdRun.groovy | 6 +++--- .../groovy/nextflow/trace/AnsiLogObserver.groovy | 12 ++++++------ 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 0c13c17f78..68626336ed 100644 --- a/build.gradle +++ b/build.gradle @@ -108,7 +108,7 @@ allprojects { } // Documentation required libraries - groovyDoc 'org.fusesource.jansi:jansi:1.11' + groovyDoc 'org.fusesource.jansi:jansi:2.4.0' groovyDoc "org.codehaus.groovy:groovy-groovydoc:3.0.19" groovyDoc "org.codehaus.groovy:groovy-ant:3.0.19" } diff --git a/modules/nextflow/build.gradle b/modules/nextflow/build.gradle index c24f235cdc..d846ab1921 100644 --- a/modules/nextflow/build.gradle +++ b/modules/nextflow/build.gradle @@ -23,6 +23,7 @@ dependencies { api "org.codehaus.groovy:groovy-json:3.0.19" api "org.codehaus.groovy:groovy-templates:3.0.19" api "org.codehaus.groovy:groovy-yaml:3.0.19" + api 'org.fusesource.jansi:jansi:2.4.0' api "org.slf4j:jcl-over-slf4j:2.0.7" api "org.slf4j:jul-to-slf4j:2.0.7" api "org.slf4j:log4j-over-slf4j:2.0.7" diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 4e6b562363..853a165a58 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -318,11 +318,11 @@ class CmdRun extends CmdBase implements HubOptions { def fmt = ansi() fmt = fmt.a("\n") fmt = fmt.a(" 🚀 ") - fmt = fmt.bg(Color.GREEN).fg(Color.BLACK).a(" N E X T F L O W ").reset() + fmt = fmt.bgRgb(13, 192, 157).fg(Color.BLACK).a(" N E X T F L O W ").reset() fmt = fmt.fg(Color.BLACK).a(" ~ ") fmt = fmt.fg(Color.GREEN).a("version " + Const.APP_VER).reset() fmt = fmt.a("\n") - AnsiConsole.out.println(fmt.eraseLine()) + AnsiConsole.out().println(fmt.eraseLine()) Plugins.init() // -- specify the arguments @@ -424,7 +424,7 @@ class CmdRun extends CmdBase implements HubOptions { fmt = fmt.a(" DSL${ver} - ") fmt = fmt.fg(Color.MAGENTA).a("revision: ").bold().a(revision) fmt = fmt.reset().a("\n") - AnsiConsole.out.println(fmt.eraseLine()) + AnsiConsole.out().println(fmt.eraseLine()) } static String detectDslMode(ConfigMap config, String scriptText, Map sysEnv) { diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 8741dd230f..b0735b65ce 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -293,7 +293,7 @@ class AnsiLogObserver implements TraceObserver { synchronized protected void renderProgress(WorkflowStats stats) { if( printedLines ) - AnsiConsole.out.println ansi().cursorUp(printedLines+gapLines+1) + AnsiConsole.out().println ansi().cursorUp(printedLines+gapLines+1) // -- print processes final term = ansi() @@ -306,14 +306,14 @@ class AnsiLogObserver implements TraceObserver { final str = term.toString() final count = printAndCountLines(str) - AnsiConsole.out.flush() + AnsiConsole.out().flush() // usually the gap should be negative because `count` should be greater or equal // than the previous `printedLines` value (the output should become longer) // otherwise cleanup the remaining lines gapLines = printedLines > count ? printedLines-count : 0 if( gapLines>0 ) for(int i=0; i Date: Thu, 14 Dec 2023 22:26:28 +0100 Subject: [PATCH 09/34] Highlight the task tag in yellow Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index b0735b65ce..d621dddd7d 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -389,6 +389,9 @@ class AnsiLogObserver implements TraceObserver { final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) + final tagMatch = label =~ /( \(.+\) *)$/ + final labelTag = tagMatch ? tagMatch.group(1) : '' + final labelNoTag = label.replaceFirst(/ \(.+\) *$/, "") final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 @@ -401,7 +404,7 @@ class AnsiLogObserver implements TraceObserver { // Label: process > sayHello if( cols > 180 ) term = term.a('process > ') - term = term.reset().a(label) + term = term.reset().a(labelNoTag).fg(Color.YELLOW).a(labelTag).reset() // No tasks if( tot == 0 ) { From d16b1a6e0b47903b88e2e60707c8124b616cc0cf Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 14 Dec 2023 22:33:11 +0100 Subject: [PATCH 10/34] Minor style tweaks to Nextflow header Signed-off-by: Phil Ewels --- modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 853a165a58..0557039f63 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -318,9 +318,8 @@ class CmdRun extends CmdBase implements HubOptions { def fmt = ansi() fmt = fmt.a("\n") fmt = fmt.a(" 🚀 ") - fmt = fmt.bgRgb(13, 192, 157).fg(Color.BLACK).a(" N E X T F L O W ").reset() - fmt = fmt.fg(Color.BLACK).a(" ~ ") - fmt = fmt.fg(Color.GREEN).a("version " + Const.APP_VER).reset() + fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() + fmt = fmt.fg(Color.BLACK).a(" ~ ").reset().a("version " + Const.APP_VER).reset() fmt = fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) Plugins.init() From d06c3a7f0a25273b8ddc601519709df830d226e7 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Thu, 14 Dec 2023 22:39:40 +0100 Subject: [PATCH 11/34] Ongoing pct in blue, not yellow Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index d621dddd7d..710be9d74a 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -417,7 +417,7 @@ class AnsiLogObserver implements TraceObserver { term = term .fg(Color.BLACK) .a(' [') - .fg(pct == '100%' ? Color.GREEN : Color.YELLOW) + .fg(pct == '100%' ? Color.GREEN : Color.BLUE) .a(pct) .fg(Color.BLACK) .a(']') From 787b48e879129d549db7df766e25de8c9671c46f Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 15 Dec 2023 14:41:43 +0100 Subject: [PATCH 12/34] Use INTENSITY_FAINT instead of black, other colour cleanup Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/cli/CmdRun.groovy | 13 +++---- .../nextflow/trace/AnsiLogObserver.groovy | 35 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 0557039f63..a15143ec1a 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -319,7 +319,7 @@ class CmdRun extends CmdBase implements HubOptions { fmt = fmt.a("\n") fmt = fmt.a(" 🚀 ") fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() - fmt = fmt.fg(Color.BLACK).a(" ~ ").reset().a("version " + Const.APP_VER).reset() + fmt = fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() fmt = fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) Plugins.init() @@ -416,13 +416,14 @@ class CmdRun extends CmdBase implements HubOptions { log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" def fmt = ansi() - fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ") - fmt = fmt.fg(Color.BLACK).a("[").reset() + fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ").reset() + fmt = fmt.a(Attribute.INTENSITY_FAINT).a("[").reset() fmt = fmt.bold().fg(Color.CYAN).a(runName).reset() - fmt = fmt.fg(Color.BLACK).a("]") + fmt = fmt.a(Attribute.INTENSITY_FAINT).a("]") fmt = fmt.a(" DSL${ver} - ") - fmt = fmt.fg(Color.MAGENTA).a("revision: ").bold().a(revision) - fmt = fmt.reset().a("\n") + fmt = fmt.fg(Color.CYAN).a("revision: ").reset() + fmt = fmt.fg(Color.CYAN).a(revision).reset() + fmt = fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) } diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 710be9d74a..f7f5d80ba9 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -237,7 +237,7 @@ class AnsiLogObserver implements TraceObserver { } if( count ) { - term.a("executor > " + line) + term.a(Attribute.INTENSITY_FAINT).a("executor > " + line).reset() term.newline() } } @@ -279,7 +279,8 @@ class AnsiLogObserver implements TraceObserver { } } if( skippedLines > 0 ) - term.a("Plus $skippedLines more processes waiting for tasks…").newline() + term = term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a("Plus ").bold().a(skippedLines).reset() + term = term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a(" more processes waiting for tasks…").reset().newline() rendered = true } @@ -389,8 +390,9 @@ class AnsiLogObserver implements TraceObserver { final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) - final tagMatch = label =~ /( \(.+\) *)$/ + final tagMatch = label =~ / \((.+)\)( *)$/ final labelTag = tagMatch ? tagMatch.group(1) : '' + final labelSpaces = tagMatch ? tagMatch.group(2) : '' final labelNoTag = label.replaceFirst(/ \(.+\) *$/, "") final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) @@ -399,12 +401,19 @@ class AnsiLogObserver implements TraceObserver { final numbs = " ${(int)com} of ${(int)tot}".toString() // Hash: [] - term = term.fg(Color.BLACK).a('[').fg(Color.BLUE).a(hh).fg(Color.BLACK).a('] ') + term = term.a(Attribute.INTENSITY_FAINT).a('[').reset() + term = term.fg(Color.BLUE).a(hh).reset() + term = term.a(Attribute.INTENSITY_FAINT).a('] ').reset() // Label: process > sayHello if( cols > 180 ) - term = term.a('process > ') - term = term.reset().a(labelNoTag).fg(Color.YELLOW).a(labelTag).reset() + term = term.a(Attribute.INTENSITY_FAINT).a('process > ').reset() + term = term.a(labelNoTag) + if( labelTag ){ + term = term.fg(Color.YELLOW).a(Attribute.INTENSITY_FAINT).a(' (').reset() + term = term.fg(Color.YELLOW).a(labelTag) + term = term.a(Attribute.INTENSITY_FAINT).a(')').reset().a(labelSpaces) + } // No tasks if( tot == 0 ) { @@ -415,22 +424,18 @@ class AnsiLogObserver implements TraceObserver { // Progress: [ 0%] 0 of 10 if( cols > 120 ) { term = term - .fg(Color.BLACK) - .a(' [') - .fg(pct == '100%' ? Color.GREEN : Color.BLUE) - .a(pct) - .fg(Color.BLACK) - .a(']') - .reset() + .a(Attribute.INTENSITY_FAINT).a(' [').reset() + .fg(pct == '100%' ? Color.GREEN : Color.BLUE).a(pct).reset() + .a(Attribute.INTENSITY_FAINT).a(']').reset() } else { - term = term.fg(Color.BLACK).a(' |').reset() + term = term.a(Attribute.INTENSITY_FAINT).a(' |').reset() } term = term.a(numbs) // Number of tasks: if( stats.cached ) - term = term.fg(Color.BLACK).a(", cached: $stats.cached").reset() + term = term.a(Attribute.INTENSITY_FAINT).a(", cached: $stats.cached").reset() if( stats.stored ) term = term.a(", stored: $stats.stored") if( stats.failed ) From 2d24b70780bd01d17e4ea36ef2c81739fc3e90d4 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 15 Dec 2023 14:55:35 +0100 Subject: [PATCH 13/34] Respect launcher.options.ansiLog for coloured log header Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/cli/CmdRun.groovy | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index a15143ec1a..818b0dae47 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -314,14 +314,19 @@ class CmdRun extends CmdBase implements HubOptions { checkRunName() - log.debug "N E X T F L O W ~ version ${Const.APP_VER}" - def fmt = ansi() - fmt = fmt.a("\n") - fmt = fmt.a(" 🚀 ") - fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() - fmt = fmt.a("\n") - AnsiConsole.out().println(fmt.eraseLine()) + if( launcher.options.ansiLog ){ + log.debug "N E X T F L O W ~ version ${Const.APP_VER}" + + def fmt = ansi() + fmt = fmt.a("\n") + fmt = fmt.a(" 🚀 ") + fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() + fmt = fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() + fmt = fmt.a("\n") + AnsiConsole.out().println(fmt.eraseLine()) + } else { + log.info "N E X T F L O W ~ version ${Const.APP_VER}" + } Plugins.init() // -- specify the arguments @@ -413,18 +418,22 @@ class CmdRun extends CmdBase implements HubOptions { ? scriptFile.revisionInfo : scriptFile.getScriptId()?.substring(0,10) - log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" - - def fmt = ansi() - fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ").reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a("[").reset() - fmt = fmt.bold().fg(Color.CYAN).a(runName).reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a("]") - fmt = fmt.a(" DSL${ver} - ") - fmt = fmt.fg(Color.CYAN).a("revision: ").reset() - fmt = fmt.fg(Color.CYAN).a(revision).reset() - fmt = fmt.a("\n") - AnsiConsole.out().println(fmt.eraseLine()) + if( launcher.options.ansiLog ){ + log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" + + def fmt = ansi() + fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ").reset() + fmt = fmt.a(Attribute.INTENSITY_FAINT).a("[").reset() + fmt = fmt.bold().fg(Color.CYAN).a(runName).reset() + fmt = fmt.a(Attribute.INTENSITY_FAINT).a("]") + fmt = fmt.a(" DSL${ver} - ") + fmt = fmt.fg(Color.CYAN).a("revision: ").reset() + fmt = fmt.fg(Color.CYAN).a(revision).reset() + fmt = fmt.a("\n") + AnsiConsole.out().println(fmt.eraseLine()) + } else { + log.info "${head} [$runName] DSL${ver} - revision: ${revision}" + } } static String detectDslMode(ConfigMap config, String scriptText, Map sysEnv) { From 0f18d9fcad5fc5a6555c6e66785ab4d2ba3c22d5 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Fri, 15 Dec 2023 22:06:26 +0100 Subject: [PATCH 14/34] =?UTF-8?q?remove=20the=20rocket=20=F0=9F=9A=80?= =?UTF-8?q?=F0=9F=9A=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Phil Ewels --- modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 818b0dae47..adb246e997 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -319,7 +319,6 @@ class CmdRun extends CmdBase implements HubOptions { def fmt = ansi() fmt = fmt.a("\n") - fmt = fmt.a(" 🚀 ") fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() fmt = fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() fmt = fmt.a("\n") From 4ecbce79898229f9092547fe42edaece50d4b30e Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sat, 16 Dec 2023 21:47:51 +0100 Subject: [PATCH 15/34] Fix some of the tests Signed-off-by: Phil Ewels --- .../groovy/nextflow/trace/AnsiLogObserverTest.groovy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy index 907f45f4b6..0f1667b3e4 100644 --- a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy @@ -73,13 +73,15 @@ class AnsiLogObserverTest extends Specification { 'foo' | 3 | 80 | 'foo' 'foo' | 5 | 80 | 'foo ' - 'long_name' | 9 | 5 | 'lo...' + 'long_name' | 9 | 5 | 'long_' + 'long_name' | 9 | 6 | 'lon…me' 'long_name' | 9 | 2 | 'lo' 'long_name' | 9 | 3 | 'lon' 'xx' | 9 | 1 | 'x' 'xx' | 9 | 5 | 'xx ' 'abcd' | 9 | 5 | 'abcd ' - '12345678' | 9 | 5 | '12...' + '12345678' | 9 | 5 | '12345' + '12345678' | 9 | 6 | '123…78' } def 'should chop a string' () { @@ -91,13 +93,14 @@ class AnsiLogObserverTest extends Specification { where: NAME | COLS | EXPECTED - 'long_name' | 5 | 'lo...' + 'long_name' | 6 | 'lon…me' 'long_name' | 2 | 'lo' 'long_name' | 3 | 'lon' 'xx' | 1 | 'x' 'xx' | 5 | 'xx' 'abcd' | 5 | 'abcd' - '12345678' | 5 | '12...' + '12345678' | 5 | '12345' + '12345678' | 6 | '123…78' } From 0b4fec458a7acafd54ba4d11e0ce65da77f0d570 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 17 Dec 2023 00:37:30 +0100 Subject: [PATCH 16/34] Remove unnecessary term assignments Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/cli/CmdRun.groovy | 24 ++++---- .../nextflow/trace/AnsiLogObserver.groovy | 58 +++++++++---------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index adb246e997..453bd85e91 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -318,10 +318,10 @@ class CmdRun extends CmdBase implements HubOptions { log.debug "N E X T F L O W ~ version ${Const.APP_VER}" def fmt = ansi() - fmt = fmt.a("\n") - fmt = fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() - fmt = fmt.a("\n") + fmt.a("\n") + fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() + fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() + fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) } else { log.info "N E X T F L O W ~ version ${Const.APP_VER}" @@ -421,14 +421,14 @@ class CmdRun extends CmdBase implements HubOptions { log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" def fmt = ansi() - fmt = fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ").reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a("[").reset() - fmt = fmt.bold().fg(Color.CYAN).a(runName).reset() - fmt = fmt.a(Attribute.INTENSITY_FAINT).a("]") - fmt = fmt.a(" DSL${ver} - ") - fmt = fmt.fg(Color.CYAN).a("revision: ").reset() - fmt = fmt.fg(Color.CYAN).a(revision).reset() - fmt = fmt.a("\n") + fmt.a(" ┃ Launching").fg(Color.MAGENTA).a(" `$repo` ").reset() + fmt.a(Attribute.INTENSITY_FAINT).a("[").reset() + fmt.bold().fg(Color.CYAN).a(runName).reset() + fmt.a(Attribute.INTENSITY_FAINT).a("]") + fmt.a(" DSL${ver} - ") + fmt.fg(Color.CYAN).a("revision: ").reset() + fmt.fg(Color.CYAN).a(revision).reset() + fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) } else { log.info "${head} [$runName] DSL${ver} - revision: ${revision}" diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index f7f5d80ba9..640123ba54 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -123,7 +123,7 @@ class AnsiLogObserver implements TraceObserver { boolean warn if( isHashLogPrefix(message) && !(warn=message.indexOf('NOTE:')>0) ) return - + if( !started || !statsObserver.hasProgressRecords() ) { println message } @@ -181,7 +181,7 @@ class AnsiLogObserver implements TraceObserver { wait(200) } } - // + // final stats = statsObserver.getStats() renderProgress(stats) renderSummary(stats) @@ -227,7 +227,7 @@ class AnsiLogObserver implements TraceObserver { protected String getExecutorName(String key) { session.getExecutorFactory().getDisplayName(key) } - + protected void renderExecutors(Ansi term) { int count=0 def line = '' @@ -279,8 +279,8 @@ class AnsiLogObserver implements TraceObserver { } } if( skippedLines > 0 ) - term = term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a("Plus ").bold().a(skippedLines).reset() - term = term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a(" more processes waiting for tasks…").reset().newline() + term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a("Plus ").bold().a(skippedLines).reset() + term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a(" more processes waiting for tasks…").reset().newline() rendered = true } @@ -334,7 +334,7 @@ class AnsiLogObserver implements TraceObserver { return if( enableSummary == null && delta <= 60*1_000 ) return - + if( session.isSuccess() && stats.progressLength>0 ) { def report = "" report += "Completed at: ${new Date(endTimestamp).format('dd-MMM-yyyy HH:mm:ss')}\n" @@ -362,13 +362,13 @@ class AnsiLogObserver implements TraceObserver { if( color ) fmt = fmt.fg(Color.DEFAULT) AnsiConsole.out().println(fmt.eraseLine()) } - + protected void printAnsiLines(String lines) { final text = lines .replace('\r','') .replace(NEWLINE, ansi().eraseLine().toString() + NEWLINE) AnsiConsole.out().print(text) - } + } protected String fmtWidth(String name, int width, int cols) { assert name.size() <= width @@ -383,7 +383,7 @@ class AnsiLogObserver implements TraceObserver { protected String fmtChop(String str, int cols) { if( str.size() <= cols ) return str - return str.take(3) + '…' + str.takeRight(cols-1-3) + return cols>5 ? str.take(3) + '…' + str.takeRight(cols-1-3) : str[0..cols-1] } protected Ansi line(ProgressRecord stats, Ansi term) { @@ -401,51 +401,51 @@ class AnsiLogObserver implements TraceObserver { final numbs = " ${(int)com} of ${(int)tot}".toString() // Hash: [] - term = term.a(Attribute.INTENSITY_FAINT).a('[').reset() - term = term.fg(Color.BLUE).a(hh).reset() - term = term.a(Attribute.INTENSITY_FAINT).a('] ').reset() + term.a(Attribute.INTENSITY_FAINT).a('[').reset() + term.fg(Color.BLUE).a(hh).reset() + term.a(Attribute.INTENSITY_FAINT).a('] ').reset() // Label: process > sayHello if( cols > 180 ) - term = term.a(Attribute.INTENSITY_FAINT).a('process > ').reset() - term = term.a(labelNoTag) + term.a(Attribute.INTENSITY_FAINT).a('process > ').reset() + term.a(labelNoTag) if( labelTag ){ - term = term.fg(Color.YELLOW).a(Attribute.INTENSITY_FAINT).a(' (').reset() - term = term.fg(Color.YELLOW).a(labelTag) - term = term.a(Attribute.INTENSITY_FAINT).a(')').reset().a(labelSpaces) + term.fg(Color.YELLOW).a(Attribute.INTENSITY_FAINT).a(' (').reset() + term.fg(Color.YELLOW).a(labelTag) + term.a(Attribute.INTENSITY_FAINT).a(')').reset().a(labelSpaces) } // No tasks if( tot == 0 ) { - term = term.a(' -') + term.a(' -') return term } // Progress: [ 0%] 0 of 10 if( cols > 120 ) { - term = term - .a(Attribute.INTENSITY_FAINT).a(' [').reset() + term.a(Attribute.INTENSITY_FAINT).a(' [').reset() .fg(pct == '100%' ? Color.GREEN : Color.BLUE).a(pct).reset() .a(Attribute.INTENSITY_FAINT).a(']').reset() } else { - term = term.a(Attribute.INTENSITY_FAINT).a(' |').reset() + term.a(Attribute.INTENSITY_FAINT).a(' |').reset() } - term = term.a(numbs) + term.a(numbs) // Number of tasks: if( stats.cached ) - term = term.a(Attribute.INTENSITY_FAINT).a(", cached: $stats.cached").reset() + term.a(Attribute.INTENSITY_FAINT).a(", cached: $stats.cached").reset() if( stats.stored ) - term = term.a(", stored: $stats.stored") + term.a(", stored: $stats.stored") if( stats.failed ) - term = term.a(", failed: $stats.failed") + term.a(", failed: $stats.failed") if( stats.retries ) - term = term.a(", retries: $stats.retries") + term.a(", retries: $stats.retries") if( stats.terminated && tot ) { - term = stats.errored - ? term.fg(Color.RED).a(' \u2718' ).reset() - : term.fg(Color.GREEN).a(' \u2714 ').reset() + if( stats.errored ) + term.fg(Color.RED).a(' \u2718' ).reset() + else + term.fg(Color.GREEN).a(' \u2714 ').reset() } return term } From d79bff7912f7fdd6f389d8c9a9adda5e90f4095b Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 17 Dec 2023 00:58:06 +0100 Subject: [PATCH 17/34] Small refactor to try to get tests to work Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 5 +++-- .../test/groovy/nextflow/trace/AnsiLogObserverTest.groovy | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 640123ba54..0994c6ac8d 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -270,7 +270,7 @@ class AnsiLogObserver implements TraceObserver { def skippedLines = 0 for( ProgressRecord entry : processes ) { if( renderedLines <= rows - 5 || entry.getTotalCount() > 0 ) { - term = line(entry, term) + term.a(line(entry)) term.newline() renderedLines += 1 } @@ -386,7 +386,8 @@ class AnsiLogObserver implements TraceObserver { return cols>5 ? str.take(3) + '…' + str.takeRight(cols-1-3) : str[0..cols-1] } - protected Ansi line(ProgressRecord stats, Ansi term) { + protected Ansi line(ProgressRecord stats) { + final term = ansi() final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) diff --git a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy index 0f1667b3e4..b1aa15fa05 100644 --- a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy @@ -42,7 +42,7 @@ class AnsiLogObserverTest extends Specification { when: observer.@labelWidth = stats.name.size() then: - observer.line(stats) == EXPECTED + observer.line(stats).toString() == EXPECTED where: HASH | SUBMIT | SUCCEEDED | CACHE | STORE | DONE | ERR | EXPECTED From 30d07fe506acb6baf4883418c3b723914a3d36e8 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Tue, 19 Dec 2023 10:14:42 -0600 Subject: [PATCH 18/34] Fix failing tests Signed-off-by: Ben Sherman --- .../nextflow/trace/AnsiLogObserver.groovy | 4 ++-- .../nextflow/trace/AnsiLogObserverTest.groovy | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 0994c6ac8d..d578870a0c 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -444,9 +444,9 @@ class AnsiLogObserver implements TraceObserver { term.a(", retries: $stats.retries") if( stats.terminated && tot ) { if( stats.errored ) - term.fg(Color.RED).a(' \u2718' ).reset() + term.fg(Color.RED).a(' \u2718').reset() else - term.fg(Color.GREEN).a(' \u2714 ').reset() + term.fg(Color.GREEN).a(' \u2714').reset() } return term } diff --git a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy index b1aa15fa05..64e0d1c0fc 100644 --- a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy @@ -42,21 +42,21 @@ class AnsiLogObserverTest extends Specification { when: observer.@labelWidth = stats.name.size() then: - observer.line(stats).toString() == EXPECTED + observer.line(stats).toString().replaceAll('\u001B\\[[\\d;]*[^\\d;]','') == EXPECTED where: HASH | SUBMIT | SUCCEEDED | CACHE | STORE | DONE | ERR | EXPECTED - null | 0 | 0 | 0 | 0 | false | false | '[- ] process > foo -' - '4e/486876' | 1 | 0 | 0 | 0 | false | false | '[4e/486876] process > foo [ 0%] 0 of 1' - '4e/486876' | 0 | 1 | 0 | 0 | false | false | '[4e/486876] process > foo [100%] 1 of 1' - '4e/486876' | 1 | 1 | 0 | 0 | false | false | '[4e/486876] process > foo [ 50%] 1 of 2' - '4e/486876' | 5 | 5 | 0 | 0 | false | false | '[4e/486876] process > foo [ 50%] 5 of 10' - '4e/486876' | 0 | 0 | 5 | 0 | false | false | '[4e/486876] process > foo [100%] 5 of 5, cached: 5' - '4e/486876' | 1 | 1 | 3 | 0 | false | false | '[4e/486876] process > foo [ 80%] 4 of 5, cached: 3' - 'skipped' | 0 | 0 | 0 | 5 | false | false | '[skipped ] process > foo [100%] 5 of 5, stored: 5' - 'skipped' | 1 | 1 | 0 | 3 | false | false | '[skipped ] process > foo [ 80%] 4 of 5, stored: 3' - 'ab/123456' | 0 | 2 | 0 | 0 | true | false | '[ab/123456] process > foo [100%] 2 of 2 ✔' - 'ef/987654' | 0 | 2 | 0 | 0 | true | true | '[ef/987654] process > foo [100%] 2 of 2 ✘' + null | 0 | 0 | 0 | 0 | false | false | '[- ] foo -' + '4e/486876' | 1 | 0 | 0 | 0 | false | false | '[4e/486876] foo | 0 of 1' + '4e/486876' | 0 | 1 | 0 | 0 | false | false | '[4e/486876] foo | 1 of 1' + '4e/486876' | 1 | 1 | 0 | 0 | false | false | '[4e/486876] foo | 1 of 2' + '4e/486876' | 5 | 5 | 0 | 0 | false | false | '[4e/486876] foo | 5 of 10' + '4e/486876' | 0 | 0 | 5 | 0 | false | false | '[4e/486876] foo | 5 of 5, cached: 5' + '4e/486876' | 1 | 1 | 3 | 0 | false | false | '[4e/486876] foo | 4 of 5, cached: 3' + 'skipped' | 0 | 0 | 0 | 5 | false | false | '[skipped ] foo | 5 of 5, stored: 5' + 'skipped' | 1 | 1 | 0 | 3 | false | false | '[skipped ] foo | 4 of 5, stored: 3' + 'ab/123456' | 0 | 2 | 0 | 0 | true | false | '[ab/123456] foo | 2 of 2 ✔' + 'ef/987654' | 0 | 2 | 0 | 0 | true | true | '[ef/987654] foo | 2 of 2 ✘' } From 31f310521727ad5512356d33861f641d0774baa8 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Tue, 19 Dec 2023 17:57:45 +0100 Subject: [PATCH 19/34] Tests: Add tests across multiple terminal widths Signed-off-by: Phil Ewels --- .../nextflow/trace/AnsiLogObserverTest.groovy | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy index 64e0d1c0fc..436a425f8f 100644 --- a/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/trace/AnsiLogObserverTest.groovy @@ -41,22 +41,26 @@ class AnsiLogObserverTest extends Specification { when: observer.@labelWidth = stats.name.size() + observer.@cols = WIDTH + then: observer.line(stats).toString().replaceAll('\u001B\\[[\\d;]*[^\\d;]','') == EXPECTED where: - HASH | SUBMIT | SUCCEEDED | CACHE | STORE | DONE | ERR | EXPECTED - null | 0 | 0 | 0 | 0 | false | false | '[- ] foo -' - '4e/486876' | 1 | 0 | 0 | 0 | false | false | '[4e/486876] foo | 0 of 1' - '4e/486876' | 0 | 1 | 0 | 0 | false | false | '[4e/486876] foo | 1 of 1' - '4e/486876' | 1 | 1 | 0 | 0 | false | false | '[4e/486876] foo | 1 of 2' - '4e/486876' | 5 | 5 | 0 | 0 | false | false | '[4e/486876] foo | 5 of 10' - '4e/486876' | 0 | 0 | 5 | 0 | false | false | '[4e/486876] foo | 5 of 5, cached: 5' - '4e/486876' | 1 | 1 | 3 | 0 | false | false | '[4e/486876] foo | 4 of 5, cached: 3' - 'skipped' | 0 | 0 | 0 | 5 | false | false | '[skipped ] foo | 5 of 5, stored: 5' - 'skipped' | 1 | 1 | 0 | 3 | false | false | '[skipped ] foo | 4 of 5, stored: 3' - 'ab/123456' | 0 | 2 | 0 | 0 | true | false | '[ab/123456] foo | 2 of 2 ✔' - 'ef/987654' | 0 | 2 | 0 | 0 | true | true | '[ef/987654] foo | 2 of 2 ✘' + HASH | SUBMIT | SUCCEEDED | CACHE | STORE | DONE | ERR | WIDTH | EXPECTED + null | 0 | 0 | 0 | 0 | false | false | 190 | '[- ] process > foo -' + '4e/486876' | 1 | 0 | 0 | 0 | false | false | 190 | '[4e/486876] process > foo [ 0%] 0 of 1' + '4e/486876' | 0 | 1 | 0 | 0 | false | false | 190 | '[4e/486876] process > foo [100%] 1 of 1' + '4e/486876' | 1 | 1 | 0 | 0 | false | false | 190 | '[4e/486876] process > foo [ 50%] 1 of 2' + '4e/486876' | 5 | 5 | 0 | 0 | false | false | 190 | '[4e/486876] process > foo [ 50%] 5 of 10' + '4e/486876' | 6 | 6 | 0 | 0 | false | false | 180 | '[4e/486876] foo [ 50%] 6 of 12' + '4e/486876' | 7 | 7 | 0 | 0 | false | false | 70 | '[4e/486876] foo | 7 of 14' + '4e/486876' | 0 | 0 | 5 | 0 | false | false | 190 | '[4e/486876] process > foo [100%] 5 of 5, cached: 5' + '4e/486876' | 1 | 1 | 3 | 0 | false | false | 190 | '[4e/486876] process > foo [ 80%] 4 of 5, cached: 3' + 'skipped' | 0 | 0 | 0 | 5 | false | false | 190 | '[skipped ] process > foo [100%] 5 of 5, stored: 5' + 'skipped' | 1 | 1 | 0 | 3 | false | false | 190 | '[skipped ] process > foo [ 80%] 4 of 5, stored: 3' + 'ab/123456' | 0 | 2 | 0 | 0 | true | false | 190 | '[ab/123456] process > foo [100%] 2 of 2 ✔' + 'ef/987654' | 0 | 2 | 0 | 0 | true | true | 190 | '[ef/987654] process > foo [100%] 2 of 2 ✘' } From df997084ff5cfe99dbce8841266c8b89ecda5c8c Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 21 Jan 2024 19:30:37 +0100 Subject: [PATCH 20/34] Remove all instances of Const.APP_VER Signed-off-by: Phil Ewels --- modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 84be3bd468..9881a7d781 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -320,11 +320,11 @@ class CmdRun extends CmdBase implements HubOptions { def fmt = ansi() fmt.a("\n") fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() - fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + Const.APP_VER).reset() + fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) } else { - log.info "N E X T F L O W ~ version ${Const.APP_VER}" + log.info "N E X T F L O W ~ version ${BuildInfo.version}" } Plugins.init() From 3b1ce5665092b745914ee62c3d996fd3013e5f3f Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sun, 21 Jan 2024 19:37:31 +0100 Subject: [PATCH 21/34] Add squiggly brackets for if statement with > 1 line Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 311ef6914a..2f4ed61a06 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -280,9 +280,10 @@ class AnsiLogObserver implements TraceObserver { skippedLines += 1 } } - if( skippedLines > 0 ) + if( skippedLines > 0 ){ term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a("Plus ").bold().a(skippedLines).reset() term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a(" more processes waiting for tasks…").reset().newline() + } rendered = true } From 1389839ad8ada67241c5c832ef8d502cbe43f2e3 Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Mon, 22 Jan 2024 16:58:57 +0100 Subject: [PATCH 22/34] Make process qualifiers dim Gives additional focus for final process name, which is usually the most interesting bit Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 2f4ed61a06..2dc22ff6c0 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -398,6 +398,8 @@ class AnsiLogObserver implements TraceObserver { final labelTag = tagMatch ? tagMatch.group(1) : '' final labelSpaces = tagMatch ? tagMatch.group(2) : '' final labelNoTag = label.replaceFirst(/ \(.+\) *$/, "") + final labelFinalProcess = labelNoTag.tokenize(':')[-1] + final labelNoFinalProcess = labelFinalProcess.length() > 0 ? labelNoTag - ~/$labelFinalProcess$/ : labelNoTag final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 @@ -412,7 +414,8 @@ class AnsiLogObserver implements TraceObserver { // Label: process > sayHello if( cols > 180 ) term.a(Attribute.INTENSITY_FAINT).a('process > ').reset() - term.a(labelNoTag) + term.a(Attribute.INTENSITY_FAINT).a(labelNoFinalProcess).reset() + term.a(labelFinalProcess) if( labelTag ){ term.fg(Color.YELLOW).a(Attribute.INTENSITY_FAINT).a(' (').reset() term.fg(Color.YELLOW).a(labelTag) From a39fe9b47ad3686afcd16b753d36769296519e25 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 10 Feb 2024 17:28:25 +0100 Subject: [PATCH 23/34] Remove dep with jansi 2.4.0 [ci fast] Signed-off-by: Paolo Di Tommaso --- modules/nextflow/build.gradle | 1 - modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/nextflow/build.gradle b/modules/nextflow/build.gradle index eaf351597c..b5e0bfbaaa 100644 --- a/modules/nextflow/build.gradle +++ b/modules/nextflow/build.gradle @@ -42,7 +42,6 @@ dependencies { api ('org.yaml:snakeyaml:2.0') api ('org.jsoup:jsoup:1.15.4') api 'jline:jline:2.9' - api 'org.fusesource.jansi:jansi:2.4.0' api 'org.pf4j:pf4j:3.10.0' api 'dev.failsafe:failsafe:3.1.0' diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 146ed7c5dd..50f4fbcdf8 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -319,7 +319,7 @@ class CmdRun extends CmdBase implements HubOptions { def fmt = ansi() fmt.a("\n") - fmt.bgRgb(13, 192, 157).fgRgb(0, 0, 0).bold().a(" N E X T F L O W ").reset() + fmt.bg(Color.CYAN).fg(Color.BLACK).bold().a(" N E X T F L O W ").reset() fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) From 7c21657c413bf8f6be119b95f92ebccc89afffa8 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 10 Feb 2024 17:38:36 +0100 Subject: [PATCH 24/34] add printBanner Signed-off-by: Paolo Di Tommaso --- .../main/groovy/nextflow/cli/CmdRun.groovy | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 50f4fbcdf8..9b2602da1e 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -314,18 +314,7 @@ class CmdRun extends CmdBase implements HubOptions { checkRunName() - if( launcher.options.ansiLog ){ - log.debug "N E X T F L O W ~ version ${BuildInfo.version}" - - def fmt = ansi() - fmt.a("\n") - fmt.bg(Color.CYAN).fg(Color.BLACK).bold().a(" N E X T F L O W ").reset() - fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() - fmt.a("\n") - AnsiConsole.out().println(fmt.eraseLine()) - } else { - log.info "N E X T F L O W ~ version ${BuildInfo.version}" - } + printBanner() Plugins.init() // -- specify the arguments @@ -387,6 +376,21 @@ class CmdRun extends CmdBase implements HubOptions { runner.execute(scriptArgs, this.entryName) } + protected void printBanner() { + if( launcher.options.ansiLog ){ + log.debug "N E X T F L O W ~ version ${BuildInfo.version}" + + def fmt = ansi() + fmt.a("\n") + fmt.bg(Color.CYAN).fg(Color.BLACK).bold().a(" N E X T F L O W ").reset() + fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() + fmt.a("\n") + AnsiConsole.out().println(fmt.eraseLine()) + } else { + log.info "N E X T F L O W ~ version ${BuildInfo.version}" + } + + } protected checkConfigEnv(ConfigMap config) { // Warn about setting NXF_ environment variables within env config scope final env = config.env as Map From 9090d1e5c1f604f34300959206caa32920643112 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 10 Feb 2024 17:48:11 +0100 Subject: [PATCH 25/34] Using pre-compiled pattern to improve efficiency [ci fast] Signed-off-by: Paolo Di Tommaso --- .../main/groovy/nextflow/trace/AnsiLogObserver.groovy | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 2dc22ff6c0..da76010e56 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -16,6 +16,8 @@ package nextflow.trace +import java.util.regex.Pattern + import groovy.transform.CompileStatic import jline.TerminalFactory import nextflow.Session @@ -389,15 +391,18 @@ class AnsiLogObserver implements TraceObserver { return cols>5 ? str.take(3) + '…' + str.takeRight(cols-1-3) : str[0..cols-1] } + private final static Pattern TAG_REGEX = ~/ \((.+)\)( *)$/ + private final static Pattern LBL_REPLACE = ~/ \(.+\) *$/ + protected Ansi line(ProgressRecord stats) { final term = ansi() final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) - final tagMatch = label =~ / \((.+)\)( *)$/ + final tagMatch = TAG_REGEX.matcher(label) final labelTag = tagMatch ? tagMatch.group(1) : '' final labelSpaces = tagMatch ? tagMatch.group(2) : '' - final labelNoTag = label.replaceFirst(/ \(.+\) *$/, "") + final labelNoTag = LBL_REPLACE.matcher(label).replaceFirst("") final labelFinalProcess = labelNoTag.tokenize(':')[-1] final labelNoFinalProcess = labelFinalProcess.length() > 0 ? labelNoTag - ~/$labelFinalProcess$/ : labelNoTag final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) From afaf2847696d06321c8ea399daf08a344c4320ab Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sat, 10 Feb 2024 20:14:26 +0100 Subject: [PATCH 26/34] Add a load of comments explaining ANSI log logic Signed-off-by: Phil Ewels --- .../main/groovy/nextflow/cli/CmdRun.groovy | 3 ++ .../nextflow/trace/AnsiLogObserver.groovy | 35 ++++++++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 9b2602da1e..c4281e6291 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -378,8 +378,10 @@ class CmdRun extends CmdBase implements HubOptions { protected void printBanner() { if( launcher.options.ansiLog ){ + // Plain header for verbose log log.debug "N E X T F L O W ~ version ${BuildInfo.version}" + // Fancy coloured header for the ANSI console output def fmt = ansi() fmt.a("\n") fmt.bg(Color.CYAN).fg(Color.BLACK).bold().a(" N E X T F L O W ").reset() @@ -387,6 +389,7 @@ class CmdRun extends CmdBase implements HubOptions { fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) } else { + // Plain header to the console if ANSI is disabled log.info "N E X T F L O W ~ version ${BuildInfo.version}" } diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index da76010e56..6d200c0c82 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -273,15 +273,19 @@ class AnsiLogObserver implements TraceObserver { def renderedLines = 0 def skippedLines = 0 for( ProgressRecord entry : processes ) { + // Only show line if we have space in the visible terminal area + // or if the process has some submitted tasks if( renderedLines <= rows - 5 || entry.getTotalCount() > 0 ) { term.a(line(entry)) term.newline() renderedLines += 1 } + // Process with no active tasks and we're out of screen space, skip else { skippedLines += 1 } } + // Tell the user how many processes without active tasks were hidden if( skippedLines > 0 ){ term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a("Plus ").bold().a(skippedLines).reset() term.a(Attribute.ITALIC).a(Attribute.INTENSITY_FAINT).a(" more processes waiting for tasks…").reset().newline() @@ -386,8 +390,12 @@ class AnsiLogObserver implements TraceObserver { } protected String fmtChop(String str, int cols) { + // Truncate the process name to fit the terminal width if( str.size() <= cols ) return str + // Take the first 3 characters and the final chunk of text + // eg. for: NFCORE_RNASEQ:RNASEQ:FASTQ_SUBSAMPLE_FQ_SALMON:FQ_SUBSAMPLE + // trunacte to: NFC…_SALMON:FQ_SUBSAMPLE return cols>5 ? str.take(3) + '…' + str.takeRight(cols-1-3) : str[0..cols-1] } @@ -398,7 +406,14 @@ class AnsiLogObserver implements TraceObserver { final term = ansi() final float tot = stats.getTotalCount() final float com = stats.getCompletedCount() + // Truncate or pad the label to the correct width final label = fmtWidth(stats.taskName, labelWidth, Math.max(cols-50, 5)) + // Break up the process label into components for styling. eg: + // NFCORE_RNASEQ:RNASEQ:PREPARE_GENOME:GUNZIP_GTF (genes.gtf.gz) + // labelTag = genes.gtf.gz + // labelSpaces = whitespace padding after process name + // labelFinalProcess = GUNZIP_GTF + // labelNoFinalProcess = NFCORE_RNASEQ:RNASEQ:PREPARE_GENOME: final tagMatch = TAG_REGEX.matcher(label) final labelTag = tagMatch ? tagMatch.group(1) : '' final labelSpaces = tagMatch ? tagMatch.group(2) : '' @@ -408,20 +423,26 @@ class AnsiLogObserver implements TraceObserver { final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 + // eg. 100% (whitespace padded for alignment) final pct = "${String.valueOf(x).padLeft(3)}%".toString() + // eg. 1 of 1 final numbs = " ${(int)com} of ${(int)tot}".toString() - // Hash: [] + // Task hash, eg: [fa/71091a] term.a(Attribute.INTENSITY_FAINT).a('[').reset() term.fg(Color.BLUE).a(hh).reset() term.a(Attribute.INTENSITY_FAINT).a('] ').reset() - // Label: process > sayHello + // Only show 'process > ' if the terminal has lots of width if( cols > 180 ) term.a(Attribute.INTENSITY_FAINT).a('process > ').reset() + // Stem of process name, dim text term.a(Attribute.INTENSITY_FAINT).a(labelNoFinalProcess).reset() + // Final process name, regular text term.a(labelFinalProcess) + // Active process with a tag, eg: (genes.gtf.gz) if( labelTag ){ + // Tag in yellow, () dim but tag text regular term.fg(Color.YELLOW).a(Attribute.INTENSITY_FAINT).a(' (').reset() term.fg(Color.YELLOW).a(labelTag) term.a(Attribute.INTENSITY_FAINT).a(')').reset().a(labelSpaces) @@ -433,18 +454,23 @@ class AnsiLogObserver implements TraceObserver { return term } - // Progress: [ 0%] 0 of 10 + // Progress percentage, eg: [ 80%] if( cols > 120 ) { + // Only show the percentage if we have lots of width + // Percentage text in green if 100%, otherwise blue term.a(Attribute.INTENSITY_FAINT).a(' [').reset() .fg(pct == '100%' ? Color.GREEN : Color.BLUE).a(pct).reset() .a(Attribute.INTENSITY_FAINT).a(']').reset() } else { + // If narrow terminal, show single pipe char instead of percentage to save space term.a(Attribute.INTENSITY_FAINT).a(' |').reset() } + // Progress active task count, eg: 8 of 10 term.a(numbs) - // Number of tasks: + // Completed task counts and status + // Dim text for cached, otherwise regular if( stats.cached ) term.a(Attribute.INTENSITY_FAINT).a(", cached: $stats.cached").reset() if( stats.stored ) @@ -453,6 +479,7 @@ class AnsiLogObserver implements TraceObserver { term.a(", failed: $stats.failed") if( stats.retries ) term.a(", retries: $stats.retries") + // Show red cross ('✘') or green tick ('✔') according to status if( stats.terminated && tot ) { if( stats.errored ) term.fg(Color.RED).a(' \u2718').reset() From 32e3a166885285d77dcb7cd3a71058cc345c201b Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sat, 10 Feb 2024 20:36:37 +0100 Subject: [PATCH 27/34] Put back Nextflow green RGB header Use raw ANSI codes to get precise RGB codes. Signed-off-by: Phil Ewels --- modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index c4281e6291..2971877e03 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -384,7 +384,11 @@ class CmdRun extends CmdBase implements HubOptions { // Fancy coloured header for the ANSI console output def fmt = ansi() fmt.a("\n") - fmt.bg(Color.CYAN).fg(Color.BLACK).bold().a(" N E X T F L O W ").reset() + // Use exact Nextflow green RGB (13, 192, 157) and exact black text (0,0,0) + // Should render the same on every terminal, irrespective of colour scheme + // Jansi library can't do RGBs, so just do the ANSI codes manually + fmt.a("\033[1;38;2;0;0;0;48;2;13;192;157m N E X T F L O W ").reset() + // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) From 36823ac8ab6bc13a21ac39b88cfe8033754abb3e Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Sat, 10 Feb 2024 20:43:58 +0100 Subject: [PATCH 28/34] Fix spelling error spotted by Codespell Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 6d200c0c82..05f4c763ce 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -395,7 +395,7 @@ class AnsiLogObserver implements TraceObserver { return str // Take the first 3 characters and the final chunk of text // eg. for: NFCORE_RNASEQ:RNASEQ:FASTQ_SUBSAMPLE_FQ_SALMON:FQ_SUBSAMPLE - // trunacte to: NFC…_SALMON:FQ_SUBSAMPLE + // truncate to: NFC…_SALMON:FQ_SUBSAMPLE return cols>5 ? str.take(3) + '…' + str.takeRight(cols-1-3) : str[0..cols-1] } From ee6ee7caf6affba57918c45c9a1edf7935a99e0c Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 10 Feb 2024 20:55:52 +0100 Subject: [PATCH 29/34] Minor change Signed-off-by: Paolo Di Tommaso --- .../src/main/groovy/nextflow/cli/CmdRun.groovy | 16 +++++++++------- .../groovy/nextflow/trace/AnsiLogObserver.groovy | 12 ++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index c4982fb043..f2693ffb86 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -381,23 +381,25 @@ class CmdRun extends CmdBase implements HubOptions { // Plain header for verbose log log.debug "N E X T F L O W ~ version ${BuildInfo.version}" - // Fancy coloured header for the ANSI console output - def fmt = ansi() - fmt.a("\n") // Use exact Nextflow green RGB (13, 192, 157) and exact black text (0,0,0) // Should render the same on every terminal, irrespective of colour scheme // Jansi library can't do RGBs, so just do the ANSI codes manually - fmt.a("\033[1;38;2;0;0;0;48;2;13;192;157m N E X T F L O W ").reset() + final GREEN = "\033[1;38;2;0;0;0;48;2;13;192;157m" + // Fancy coloured header for the ANSI console output + final fmt = ansi() + fmt.a("\n") + fmt.a("$GREEN N E X T F L O W ").reset() // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") - AnsiConsole.out().println(fmt.eraseLine()) - } else { + AnsiConsole.out.println(fmt.eraseLine()) + } + else { // Plain header to the console if ANSI is disabled log.info "N E X T F L O W ~ version ${BuildInfo.version}" } - } + protected checkConfigEnv(ConfigMap config) { // Warn about setting NXF_ environment variables within env config scope final env = config.env as Map diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 47c2f841ed..52bee73aa5 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -303,7 +303,7 @@ class AnsiLogObserver implements TraceObserver { synchronized protected void renderProgress(WorkflowStats stats) { if( printedLines ) - AnsiConsole.out().println ansi().cursorUp(printedLines+gapLines+1) + AnsiConsole.out.println ansi().cursorUp(printedLines+gapLines+1) // -- print processes final term = ansi() @@ -316,14 +316,14 @@ class AnsiLogObserver implements TraceObserver { final str = term.toString() final count = printAndCountLines(str) - AnsiConsole.out().flush() + AnsiConsole.out.flush() // usually the gap should be negative because `count` should be greater or equal // than the previous `printedLines` value (the output should become longer) // otherwise cleanup the remaining lines gapLines = printedLines > count ? printedLines-count : 0 if( gapLines>0 ) for(int i=0; i Date: Sat, 10 Feb 2024 21:01:05 +0100 Subject: [PATCH 30/34] Simplify string trimming [ci fast] Signed-off-by: Paolo Di Tommaso --- .../src/main/groovy/nextflow/trace/AnsiLogObserver.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy index 52bee73aa5..b60ff3dc81 100644 --- a/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/trace/AnsiLogObserver.groovy @@ -419,7 +419,7 @@ class AnsiLogObserver implements TraceObserver { final labelSpaces = tagMatch ? tagMatch.group(2) : '' final labelNoTag = LBL_REPLACE.matcher(label).replaceFirst("") final labelFinalProcess = labelNoTag.tokenize(':')[-1] - final labelNoFinalProcess = labelFinalProcess.length() > 0 ? labelNoTag - ~/$labelFinalProcess$/ : labelNoTag + final labelNoFinalProcess = labelFinalProcess.length() > 0 ? labelNoTag - labelFinalProcess : labelNoTag final hh = (stats.hash && tot>0 ? stats.hash : '-').padRight(9) final x = tot ? Math.floor(com / tot * 100f).toInteger() : 0 From 40abece8448f954e954527b0ef2551a3069ea5f2 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 10 Feb 2024 21:16:42 +0100 Subject: [PATCH 31/34] Minor changes Signed-off-by: Paolo Di Tommaso --- .../src/main/groovy/nextflow/cli/CmdRun.groovy | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index f2693ffb86..ee8510dbde 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -64,8 +64,8 @@ class CmdRun extends CmdBase implements HubOptions { static final public List VALID_PARAMS_FILE = ['json', 'yml', 'yaml'] - static final public DSL2 = '2' - static final public DSL1 = '1' + static final public String DSL2 = '2' + static final public String DSL1 = '1' static { // install the custom pool factory for GPars threads @@ -388,7 +388,7 @@ class CmdRun extends CmdBase implements HubOptions { // Fancy coloured header for the ANSI console output final fmt = ansi() fmt.a("\n") - fmt.a("$GREEN N E X T F L O W ").reset() + fmt.a("${GREEN} N E X T F L O W ").reset() // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") @@ -424,12 +424,15 @@ class CmdRun extends CmdBase implements HubOptions { NextflowMeta.instance.enableDsl(dsl) // -- show launch info final ver = NF.dsl2 ? DSL2 : DSL1 - final repo = scriptFile.repository ?: scriptFile.source + final repo = scriptFile.repository ?: scriptFile.source.toString() final head = preview ? "* PREVIEW * $scriptFile.repository" : "Launching `$repo`" final revision = scriptFile.repository - ? scriptFile.revisionInfo + ? scriptFile.revisionInfo.toString() : scriptFile.getScriptId()?.substring(0,10) + printLaunchInfo(ver, repo, head, revision) + } + protected void printLaunchInfo(String ver, String repo, String head, String revision) { if( launcher.options.ansiLog ){ log.debug "${head} [$runName] DSL${ver} - revision: ${revision}" @@ -443,7 +446,8 @@ class CmdRun extends CmdBase implements HubOptions { fmt.fg(Color.CYAN).a(revision).reset() fmt.a("\n") AnsiConsole.out().println(fmt.eraseLine()) - } else { + } + else { log.info "${head} [$runName] DSL${ver} - revision: ${revision}" } } From 00c22b1eab280cf1b6dd9618dafbc8618f87b0ee Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sun, 11 Feb 2024 11:10:00 +0100 Subject: [PATCH 32/34] Restore Phil code [ci fast] Signed-off-by: Paolo Di Tommaso --- .../src/main/groovy/nextflow/cli/CmdRun.groovy | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index ee8510dbde..20f41fb5ab 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -16,6 +16,9 @@ package nextflow.cli + +import static org.fusesource.jansi.Ansi.* + import java.nio.file.NoSuchFileException import java.nio.file.Path import java.util.regex.Pattern @@ -47,9 +50,6 @@ import nextflow.util.Duration import nextflow.util.HistoryFile import org.fusesource.jansi.AnsiConsole import org.yaml.snakeyaml.Yaml -import static org.fusesource.jansi.Ansi.Attribute -import static org.fusesource.jansi.Ansi.Color -import static org.fusesource.jansi.Ansi.ansi /** * CLI sub-command RUN * @@ -381,14 +381,13 @@ class CmdRun extends CmdBase implements HubOptions { // Plain header for verbose log log.debug "N E X T F L O W ~ version ${BuildInfo.version}" + // Fancy coloured header for the ANSI console output + def fmt = ansi() + fmt.a("\n") // Use exact Nextflow green RGB (13, 192, 157) and exact black text (0,0,0) // Should render the same on every terminal, irrespective of colour scheme // Jansi library can't do RGBs, so just do the ANSI codes manually - final GREEN = "\033[1;38;2;0;0;0;48;2;13;192;157m" - // Fancy coloured header for the ANSI console output - final fmt = ansi() - fmt.a("\n") - fmt.a("${GREEN} N E X T F L O W ").reset() + fmt.a("\033[1;38;2;0;0;0;48;2;13;192;157m N E X T F L O W ").reset() // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") From 13e631ce8304c60972a607a66dcb949306e53abe Mon Sep 17 00:00:00 2001 From: Phil Ewels Date: Mon, 12 Feb 2024 17:56:22 +0100 Subject: [PATCH 33/34] Find ANSI colour codes that work across most (all?) terminals Signed-off-by: Phil Ewels --- .../src/main/groovy/nextflow/cli/CmdRun.groovy | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 20f41fb5ab..6371941d62 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -384,10 +384,17 @@ class CmdRun extends CmdBase implements HubOptions { // Fancy coloured header for the ANSI console output def fmt = ansi() fmt.a("\n") - // Use exact Nextflow green RGB (13, 192, 157) and exact black text (0,0,0) - // Should render the same on every terminal, irrespective of colour scheme - // Jansi library can't do RGBs, so just do the ANSI codes manually - fmt.a("\033[1;38;2;0;0;0;48;2;13;192;157m N E X T F L O W ").reset() + // Use exact colour codes so that they render the same on every terminal, + // irrespective of terminal colour scheme. + // Nextflow green RGB (13, 192, 157) and exact black text (0,0,0), + // Apple Terminal only supports 256 colours, so use the closest match: + // light sea green | #20B2AA | 38;5;0 + // Don't use black for text as terminals mess with this in their colour schemes. + // Use very dark grey, which is more reliable. + // Jansi library bundled in Jline can't do exact RGBs, + // so just do the ANSI codes manually + fmt.a("\033[1m\033[38;5;232m\033[48;5;43m N E X T F L O W ").reset() + // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset() fmt.a("\n") From 10049b2feb407982bcd353298382025622a84e9b Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Mon, 12 Feb 2024 18:42:00 +0100 Subject: [PATCH 34/34] Minor change [ci skip] Signed-off-by: Paolo Di Tommaso --- modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy index 6371941d62..700bcad165 100644 --- a/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/cli/CmdRun.groovy @@ -393,7 +393,8 @@ class CmdRun extends CmdBase implements HubOptions { // Use very dark grey, which is more reliable. // Jansi library bundled in Jline can't do exact RGBs, // so just do the ANSI codes manually - fmt.a("\033[1m\033[38;5;232m\033[48;5;43m N E X T F L O W ").reset() + final BACKGROUND = "\033[1m\033[38;5;232m\033[48;5;43m" + fmt.a("$BACKGROUND N E X T F L O W ").reset() // Show Nextflow version fmt.a(Attribute.INTENSITY_FAINT).a(" ~ ").reset().a("version " + BuildInfo.version).reset()