Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add errors-only output mode #2588

Closed
wants to merge 13 commits into from
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "my-app",
"scripts": {
"okay": "echo 'working'",
"error": "echo 'intentionally failing' && exit 2"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"pipeline": {
"error": {
"outputs": ["foo"]
},
"okay": {
"outputs": []
}
}
}
28 changes: 26 additions & 2 deletions cli/integration_tests/monorepo_one_script_error/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -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 f66954acab3dda71
my-app:error: cache miss, executing 15d3d7967bc433e3
my-app:error:
my-app:error: > error
my-app:error: > echo 'intentionally failing' && exit 2
Expand All @@ -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 f66954acab3dda71
my-app:error: cache miss, executing 15d3d7967bc433e3
my-app:error:
my-app:error: > error
my-app:error: > echo 'intentionally failing' && exit 2
Expand All @@ -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 only
$ ${TURBO} --output-logs=errors-only error okay
dobesv marked this conversation as resolved.
Show resolved Hide resolved
\xe2\x80\xa2 Packages in scope: my-app (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:
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: 1 successful, 2 total
Cached: 0 cached, 2 total
Time:\s*[\.0-9]+m?s (re)

ERROR run failed: command exited (1)
[1]
dobesv marked this conversation as resolved.
Show resolved Hide resolved
31 changes: 30 additions & 1 deletion cli/integration_tests/single_package_deps/run.t
Original file line number Diff line number Diff line change
Expand Up @@ -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)


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 e04d8bf0e58a455b
test: cache hit, suppressing output 804a9b713dc63f25

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)

dobesv marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions cli/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,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
}

Expand Down
29 changes: 22 additions & 7 deletions cli/internal/runcache/runcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -198,17 +198,32 @@ 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
}

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 "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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is annoying, but lint requires the code comment to be a whole line starting with OnError. You can still break it up into multiple lines, but I guess specifically what you have here is not acceptable 🤦🏾 ...

if tc.taskOutputMode == util.ErrorTaskOutput {
tc.ReplayLogFile(terminal, logger)
}
}

// nopWriteCloser is modeled after io.NopCloser, which is for Readers
type nopWriteCloser struct {
io.Writer
Expand Down Expand Up @@ -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 {
Expand Down
16 changes: 12 additions & 4 deletions cli/internal/util/task_output_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,6 +35,7 @@ var TaskOutputModeStrings = []string{
noTaskOutputString,
hashTaskOutputString,
newTaskOutputString,
errorTaskOutputString,
}

// FromTaskOutputModeString converts a task output mode's string representation into the enum value
Expand All @@ -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)
Expand All @@ -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)
Expand Down