From 9d5c446b89276faac0f7d44cc237e481036e79ea Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Thu, 3 Nov 2022 16:45:39 -0700 Subject: [PATCH 1/8] Add errors-only output mode --- cli/internal/run/run.go | 3 ++ cli/internal/runcache/runcache.go | 29 ++++++++++++++----- cli/internal/util/task_output_mode.go | 16 +++++++--- docs/components/output-mode-table.mdx | 13 +++++---- .../docs/reference/command-line-reference.mdx | 2 ++ 5 files changed, 46 insertions(+), 17 deletions(-) diff --git a/cli/internal/run/run.go b/cli/internal/run/run.go index eb33cead0d09e..e13d4a6ebc48f 100644 --- a/cli/internal/run/run.go +++ b/cli/internal/run/run.go @@ -1113,6 +1113,9 @@ func (ec *execContext) exec(ctx gocontext.Context, packageTask *nodes.PackageTas } else { prefixedUI.Warn("command finished with error, but continuing...") } + + taskCache.OnError(prefixedUI, progressLogger) + return err } diff --git a/cli/internal/runcache/runcache.go b/cli/internal/runcache/runcache.go index 7250892f9a6ca..b03e19489194d 100644 --- a/cli/internal/runcache/runcache.go +++ b/cli/internal/runcache/runcache.go @@ -154,7 +154,7 @@ type TaskCache struct { // Returns true if successful. func (tc TaskCache) RestoreOutputs(ctx context.Context, prefixedUI *cli.PrefixedUi, progressLogger hclog.Logger) (bool, error) { if tc.cachingDisabled || tc.rc.readsDisabled { - if tc.taskOutputMode != util.NoTaskOutput { + if tc.taskOutputMode != util.NoTaskOutput && tc.taskOutputMode != util.ErrorTaskOutput { prefixedUI.Output(fmt.Sprintf("cache bypass, force executing %s", ui.Dim(tc.hash))) } return false, nil @@ -175,7 +175,7 @@ func (tc TaskCache) RestoreOutputs(ctx context.Context, prefixedUI *cli.Prefixed if err != nil { return false, err } else if !hit { - if tc.taskOutputMode != util.NoTaskOutput { + if tc.taskOutputMode != util.NoTaskOutput && tc.taskOutputMode != util.ErrorTaskOutput { prefixedUI.Output(fmt.Sprintf("cache miss, executing %s", ui.Dim(tc.hash))) } return false, nil @@ -198,10 +198,9 @@ func (tc TaskCache) RestoreOutputs(ctx context.Context, prefixedUI *cli.Prefixed case util.FullTaskOutput: progressLogger.Debug("log file", "path", tc.LogFileName) prefixedUI.Info(fmt.Sprintf("cache hit, replaying output %s", ui.Dim(tc.hash))) - if tc.LogFileName.FileExists() { - - tc.rc.logReplayer(progressLogger, prefixedUI, tc.LogFileName) - } + tc.ReplayLogFile(prefixedUI, progressLogger) + case util.ErrorTaskOutput: + // The task succeeded, so we don't output anything in this case default: // NoLogs, do not output anything } @@ -209,6 +208,22 @@ func (tc TaskCache) RestoreOutputs(ctx context.Context, prefixedUI *cli.Prefixed return true, nil } +// ReplayLogFile writes out the stored logfile to the terminal +func (tc TaskCache) ReplayLogFile(prefixedUI *cli.PrefixedUi, progressLogger hclog.Logger) { + if tc.LogFileName.FileExists() { + tc.rc.logReplayer(progressLogger, prefixedUI, tc.LogFileName) + } +} + +// OnError +// If output mode is set to log output only after error, we'll have buffered output +// to disk without logging it. Now we can log that here. +func (tc TaskCache) OnError(terminal *cli.PrefixedUi, logger hclog.Logger) { + if tc.taskOutputMode == util.ErrorTaskOutput { + tc.ReplayLogFile(terminal, logger) + } +} + // nopWriteCloser is modeled after io.NopCloser, which is for Readers type nopWriteCloser struct { io.Writer @@ -253,7 +268,7 @@ func (tc TaskCache) OutputWriter(prefix string) (io.WriteCloser, error) { file: output, bufio: bufWriter, } - if tc.taskOutputMode == util.NoTaskOutput || tc.taskOutputMode == util.HashTaskOutput { + if tc.taskOutputMode == util.NoTaskOutput || tc.taskOutputMode == util.HashTaskOutput || tc.taskOutputMode == util.ErrorTaskOutput { // only write to log file, not to stdout fwc.Writer = bufWriter } else { diff --git a/cli/internal/util/task_output_mode.go b/cli/internal/util/task_output_mode.go index ec6d7ee5178f4..bcc2abf6abc16 100644 --- a/cli/internal/util/task_output_mode.go +++ b/cli/internal/util/task_output_mode.go @@ -17,13 +17,16 @@ const ( HashTaskOutput // NewTaskOutput will show all new task output and turbo-computed task hashes for cached output NewTaskOutput + // ErrorTaskOutput will show task output for failures only; no cache miss/hit messages are emitted + ErrorTaskOutput ) const ( - fullTaskOutputString = "full" - noTaskOutputString = "none" - hashTaskOutputString = "hash-only" - newTaskOutputString = "new-only" + fullTaskOutputString = "full" + noTaskOutputString = "none" + hashTaskOutputString = "hash-only" + newTaskOutputString = "new-only" + errorTaskOutputString = "errors-only" ) // TaskOutputModeStrings is an array containing the string representations for task output modes @@ -32,6 +35,7 @@ var TaskOutputModeStrings = []string{ noTaskOutputString, hashTaskOutputString, newTaskOutputString, + errorTaskOutputString, } // FromTaskOutputModeString converts a task output mode's string representation into the enum value @@ -45,6 +49,8 @@ func FromTaskOutputModeString(value string) (TaskOutputMode, error) { return HashTaskOutput, nil case newTaskOutputString: return NewTaskOutput, nil + case errorTaskOutputString: + return ErrorTaskOutput, nil } return FullTaskOutput, fmt.Errorf("invalid task output mode: %v", value) @@ -61,6 +67,8 @@ func ToTaskOutputModeString(value TaskOutputMode) (string, error) { return hashTaskOutputString, nil case NewTaskOutput: return newTaskOutputString, nil + case ErrorTaskOutput: + return errorTaskOutputString, nil } return "", fmt.Errorf("invalid task output mode: %v", value) diff --git a/docs/components/output-mode-table.mdx b/docs/components/output-mode-table.mdx index 1ca6f36172a68..56fd80a2190d7 100644 --- a/docs/components/output-mode-table.mdx +++ b/docs/components/output-mode-table.mdx @@ -1,6 +1,7 @@ -| option | description | -| --------- | ---------------------------------------- | -| full | This is the default. Displays all output | -| hash-only | Show only the hashes of the tasks | -| new-only | Only show output from cache misses | -| none | Hides all task output | +| option | description | +| ----------- | ---------------------------------------- | +| full | This is the default. Displays all output | +| hash-only | Show only the hashes of the tasks | +| new-only | Only show output from cache misses | +| errors-only | Only show output from task failures | +| none | Hides all task output | diff --git a/docs/pages/repo/docs/reference/command-line-reference.mdx b/docs/pages/repo/docs/reference/command-line-reference.mdx index 198db7ca21a2c..c8bfbf3c4dfb2 100644 --- a/docs/pages/repo/docs/reference/command-line-reference.mdx +++ b/docs/pages/repo/docs/reference/command-line-reference.mdx @@ -296,6 +296,8 @@ Set type of output logging. Defaults to "outputMode" for the task in `turbo.json ```shell turbo run build --output-logs=full turbo run build --output-logs=new-only +turbo run build --output-logs=errors-only +turbo run build --output-logs=none ``` #### `--only` From 5a3278afd7047616a0a4a188d40e874d86907106 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 8 Nov 2022 13:24:47 -0800 Subject: [PATCH 2/8] Tweak comment Co-authored-by: Mehul Kar --- cli/internal/runcache/runcache.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/internal/runcache/runcache.go b/cli/internal/runcache/runcache.go index b03e19489194d..41e54bf329a38 100644 --- a/cli/internal/runcache/runcache.go +++ b/cli/internal/runcache/runcache.go @@ -216,7 +216,7 @@ func (tc TaskCache) ReplayLogFile(prefixedUI *cli.PrefixedUi, progressLogger hcl } // OnError -// If output mode is set to log output only after error, we'll have buffered output +// If output-mode is set to "errors-only", we'll have buffered output // to disk without logging it. Now we can log that here. func (tc TaskCache) OnError(terminal *cli.PrefixedUi, logger hclog.Logger) { if tc.taskOutputMode == util.ErrorTaskOutput { From 4a3cb9cbe3fb22328b7265efcaae7444214e5b9e Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 8 Nov 2022 13:26:25 -0800 Subject: [PATCH 3/8] Revert docs change --- docs/components/output-mode-table.mdx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/components/output-mode-table.mdx b/docs/components/output-mode-table.mdx index 56fd80a2190d7..1ca6f36172a68 100644 --- a/docs/components/output-mode-table.mdx +++ b/docs/components/output-mode-table.mdx @@ -1,7 +1,6 @@ -| option | description | -| ----------- | ---------------------------------------- | -| full | This is the default. Displays all output | -| hash-only | Show only the hashes of the tasks | -| new-only | Only show output from cache misses | -| errors-only | Only show output from task failures | -| none | Hides all task output | +| option | description | +| --------- | ---------------------------------------- | +| full | This is the default. Displays all output | +| hash-only | Show only the hashes of the tasks | +| new-only | Only show output from cache misses | +| none | Hides all task output | From 2c08f94295db0fa335142a6ef9dd6cee047492fb Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 8 Nov 2022 13:27:06 -0800 Subject: [PATCH 4/8] Revert docs change --- docs/pages/repo/docs/reference/command-line-reference.mdx | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/pages/repo/docs/reference/command-line-reference.mdx b/docs/pages/repo/docs/reference/command-line-reference.mdx index c8bfbf3c4dfb2..198db7ca21a2c 100644 --- a/docs/pages/repo/docs/reference/command-line-reference.mdx +++ b/docs/pages/repo/docs/reference/command-line-reference.mdx @@ -296,8 +296,6 @@ Set type of output logging. Defaults to "outputMode" for the task in `turbo.json ```shell turbo run build --output-logs=full turbo run build --output-logs=new-only -turbo run build --output-logs=errors-only -turbo run build --output-logs=none ``` #### `--only` From 7001c35c04cca5353eadbc6d8a7768f2107a8fa7 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Tue, 8 Nov 2022 14:01:07 -0800 Subject: [PATCH 5/8] Add output-mode tests --- .../monorepo_one_script_error/run.t | 24 ++++++++++++++ .../single_package_deps/run.t | 31 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/cli/integration_tests/monorepo_one_script_error/run.t b/cli/integration_tests/monorepo_one_script_error/run.t index be4d0fc17d04f..2aad264340f8e 100644 --- a/cli/integration_tests/monorepo_one_script_error/run.t +++ b/cli/integration_tests/monorepo_one_script_error/run.t @@ -52,3 +52,27 @@ Make sure it isn't cached ERROR run failed: command exited (1) [1] + +Running with --output-mode=errors-only gives error output but no cache hit/miss message + $ ${TURBO} --output-logs=errors-only error + \xe2\x80\xa2 Packages in scope: my-app (esc) + \xe2\x80\xa2 Running error in 1 packages (esc) + \xe2\x80\xa2 Remote caching disabled (esc) + my-app:error: ERROR: command finished with error: command \(.*/run\.t/apps/my-app\) npm run error exited \(1\) (re) + my-app:error: + my-app:error: > error + my-app:error: > echo 'intentionally failing' && exit 2 + my-app:error: + my-app:error: intentionally failing + my-app:error: npm ERR! Lifecycle script `error` failed with error: + my-app:error: npm ERR! Error: command failed + my-app:error: npm ERR! in workspace: my-app + my-app:error: npm ERR! at location: .*/run.t/apps/my-app (re) + command \(.*/run.t/apps/my-app\) npm run error exited \(1\) (re) + + Tasks: 0 successful, 1 total + Cached: 0 cached, 1 total + Time:\s*[\.0-9]+m?s (re) + + ERROR run failed: command exited (1) + [1] diff --git a/cli/integration_tests/single_package_deps/run.t b/cli/integration_tests/single_package_deps/run.t index 9f16d7a71bff0..a0ece7041053c 100644 --- a/cli/integration_tests/single_package_deps/run.t +++ b/cli/integration_tests/single_package_deps/run.t @@ -39,4 +39,33 @@ Run a second time, verify caching works because there is a config Tasks: 2 successful, 2 total Cached: 2 cached, 2 total Time:\s*[\.0-9]+m?s >>> FULL TURBO (re) - \ No newline at end of file + +Run with --output-logs=hash-only + $ ${TURBO} run test --single-package --output-logs=hash-only + \xe2\x80\xa2 Running test (esc) + \xe2\x80\xa2 Remote caching disabled (esc) + build: cache hit, suppressing output 08d0a37ad9eee1e5 + test: cache hit, suppressing output cb5a64e4f86a6543 + + Tasks: 2 successful, 2 total + Cached: 2 cached, 2 total + Time:\s*[\.0-9]+m?s >>> FULL TURBO (re) + +Run with --output-logs=errors-only + $ ${TURBO} run test --single-package --output-logs=errors-only + \xe2\x80\xa2 Running test (esc) + \xe2\x80\xa2 Remote caching disabled (esc) + + Tasks: 2 successful, 2 total + Cached: 2 cached, 2 total + Time:\s*[\.0-9]+m?s >>> FULL TURBO (re) + +Run with --output-logs=none + $ ${TURBO} run test --single-package --output-logs=none + \xe2\x80\xa2 Running test (esc) + \xe2\x80\xa2 Remote caching disabled (esc) + + Tasks: 2 successful, 2 total + Cached: 2 cached, 2 total + Time:\s*[\.0-9]+m?s >>> FULL TURBO (re) + From c7a67ace063f2963148fbfc5492ecc616db8be9d Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Wed, 9 Nov 2022 12:00:17 -0800 Subject: [PATCH 6/8] Expand test to validate success output is suppressed while error output is displayed --- .../monorepo/apps/my-app/package.json | 1 + .../monorepo_one_script_error/monorepo/turbo.json | 3 +++ .../monorepo_one_script_error/run.t | 14 +++++++------- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/cli/integration_tests/monorepo_one_script_error/monorepo/apps/my-app/package.json b/cli/integration_tests/monorepo_one_script_error/monorepo/apps/my-app/package.json index e9a8240dfe9fb..1e0506a390ec7 100644 --- a/cli/integration_tests/monorepo_one_script_error/monorepo/apps/my-app/package.json +++ b/cli/integration_tests/monorepo_one_script_error/monorepo/apps/my-app/package.json @@ -1,6 +1,7 @@ { "name": "my-app", "scripts": { + "okay": "echo 'working'", "error": "echo 'intentionally failing' && exit 2" } } diff --git a/cli/integration_tests/monorepo_one_script_error/monorepo/turbo.json b/cli/integration_tests/monorepo_one_script_error/monorepo/turbo.json index 97c84fc604045..261c09d8d4b81 100644 --- a/cli/integration_tests/monorepo_one_script_error/monorepo/turbo.json +++ b/cli/integration_tests/monorepo_one_script_error/monorepo/turbo.json @@ -3,6 +3,9 @@ "pipeline": { "error": { "outputs": ["foo"] + }, + "okay": { + "outputs": [] } } } diff --git a/cli/integration_tests/monorepo_one_script_error/run.t b/cli/integration_tests/monorepo_one_script_error/run.t index 2aad264340f8e..162533965d380 100644 --- a/cli/integration_tests/monorepo_one_script_error/run.t +++ b/cli/integration_tests/monorepo_one_script_error/run.t @@ -8,7 +8,7 @@ Note that npm reports any failed script as exit code 1, even though we "exit 2" \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing aed5e5ad070dbe8e + my-app:error: cache miss, executing [0-9a-f]+ (re) my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 @@ -33,7 +33,7 @@ Make sure it isn't cached \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing aed5e5ad070dbe8e + my-app:error: cache miss, executing [0-9a-f]+ (re) my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 @@ -53,10 +53,10 @@ Make sure it isn't cached ERROR run failed: command exited (1) [1] -Running with --output-mode=errors-only gives error output but no cache hit/miss message - $ ${TURBO} --output-logs=errors-only error +Running with --output-mode=errors-only gives error output only + $ ${TURBO} --output-logs=errors-only error okay \xe2\x80\xa2 Packages in scope: my-app (esc) - \xe2\x80\xa2 Running error in 1 packages (esc) + \xe2\x80\xa2 Running error, okay in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) my-app:error: ERROR: command finished with error: command \(.*/run\.t/apps/my-app\) npm run error exited \(1\) (re) my-app:error: @@ -70,8 +70,8 @@ Running with --output-mode=errors-only gives error output but no cache hit/miss my-app:error: npm ERR! at location: .*/run.t/apps/my-app (re) command \(.*/run.t/apps/my-app\) npm run error exited \(1\) (re) - Tasks: 0 successful, 1 total - Cached: 0 cached, 1 total + Tasks: 1 successful, 2 total + Cached: 0 cached, 2 total Time:\s*[\.0-9]+m?s (re) ERROR run failed: command exited (1) From 4aca58d38a53683747694149939c61c399864973 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Thu, 10 Nov 2022 13:39:57 -0800 Subject: [PATCH 7/8] check task hash explicitly in test --- cli/integration_tests/monorepo_one_script_error/run.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/integration_tests/monorepo_one_script_error/run.t b/cli/integration_tests/monorepo_one_script_error/run.t index 162533965d380..0c67dd8196ccb 100644 --- a/cli/integration_tests/monorepo_one_script_error/run.t +++ b/cli/integration_tests/monorepo_one_script_error/run.t @@ -8,7 +8,7 @@ Note that npm reports any failed script as exit code 1, even though we "exit 2" \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing [0-9a-f]+ (re) + my-app:error: cache miss, executing 8209cabe6c5afb03 my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 @@ -33,7 +33,7 @@ Make sure it isn't cached \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing [0-9a-f]+ (re) + my-app:error: cache miss, executing 8209cabe6c5afb03 my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 From 5999ab1c6a214dd9b0703e5b5f02cdc10b769af9 Mon Sep 17 00:00:00 2001 From: Dobes Vandermeer Date: Thu, 10 Nov 2022 13:45:48 -0800 Subject: [PATCH 8/8] Update hashes in tests --- cli/integration_tests/monorepo_one_script_error/run.t | 4 ++-- cli/integration_tests/single_package_deps/run.t | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cli/integration_tests/monorepo_one_script_error/run.t b/cli/integration_tests/monorepo_one_script_error/run.t index 0c67dd8196ccb..2c7a9704b25b3 100644 --- a/cli/integration_tests/monorepo_one_script_error/run.t +++ b/cli/integration_tests/monorepo_one_script_error/run.t @@ -8,7 +8,7 @@ Note that npm reports any failed script as exit code 1, even though we "exit 2" \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing 8209cabe6c5afb03 + my-app:error: cache miss, executing 15d3d7967bc433e3 my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 @@ -33,7 +33,7 @@ Make sure it isn't cached \xe2\x80\xa2 Packages in scope: my-app (esc) \xe2\x80\xa2 Running error in 1 packages (esc) \xe2\x80\xa2 Remote caching disabled (esc) - my-app:error: cache miss, executing 8209cabe6c5afb03 + my-app:error: cache miss, executing 15d3d7967bc433e3 my-app:error: my-app:error: > error my-app:error: > echo 'intentionally failing' && exit 2 diff --git a/cli/integration_tests/single_package_deps/run.t b/cli/integration_tests/single_package_deps/run.t index 039b6f6034c31..3475b6712f77d 100644 --- a/cli/integration_tests/single_package_deps/run.t +++ b/cli/integration_tests/single_package_deps/run.t @@ -44,8 +44,8 @@ Run with --output-logs=hash-only $ ${TURBO} run test --single-package --output-logs=hash-only \xe2\x80\xa2 Running test (esc) \xe2\x80\xa2 Remote caching disabled (esc) - build: cache hit, suppressing output 08d0a37ad9eee1e5 - test: cache hit, suppressing output cb5a64e4f86a6543 + build: cache hit, suppressing output e04d8bf0e58a455b + test: cache hit, suppressing output 804a9b713dc63f25 Tasks: 2 successful, 2 total Cached: 2 cached, 2 total