Skip to content

Fix command failure output formatting#321

Open
kindermax wants to merge 1 commit intomasterfrom
codex/update-failure-log-format
Open

Fix command failure output formatting#321
kindermax wants to merge 1 commit intomasterfrom
codex/update-failure-log-format

Conversation

@kindermax
Copy link
Collaborator

@kindermax kindermax commented Mar 26, 2026

Summary

  • prefix dependency failure trees with lets: on the first line
  • print the final command failure as a separate status line like lets: exit status 1
  • unwrap execute errors so exit status messages stay concise while path and checksum errors keep their useful context
  • update changelog and CLI/Bats/unit coverage for the new output format

Testing

  • go test ./...
  • lets test-bats dependency_failure_tree.bats

Summary by Sourcery

Adjust command failure reporting to present a clearer, lets-prefixed dependency tree and a concise final status line.

Bug Fixes:

  • Ensure execute errors surface the underlying exit status while preserving useful path and checksum context in failure messages.

Enhancements:

  • Prefix dependency failure trees with a consistent lets: label and adjust indentation for better readability.
  • Log the final command failure as a separate lets:-prefixed status line in the CLI error output.

Documentation:

  • Document the new command failure output format in the changelog.

Tests:

  • Extend unit and Bats tests to cover the new dependency tree formatting and failure message behavior.

@sourcery-ai
Copy link

sourcery-ai bot commented Mar 26, 2026

Reviewer's Guide

Adjusts how command and dependency failure output is formatted by prefixing dependency trees with lets:, emitting a separate final status line, and unwrapping execute errors to produce concise but context-preserving failure messages, with tests and docs updated accordingly.

Sequence diagram for CLI dependency failure handling and output formatting

sequenceDiagram
    actor User
    participant CLI
    participant Executor
    participant DependencyError
    participant ExecuteError
    participant Logger
    participant Stderr

    User->>CLI: invoke lets command
    CLI->>Executor: run command with dependencies
    Executor-->>CLI: error (wrapped in DependencyError)

    CLI->>CLI: errors.As(err, &depErr)
    alt dependency_error
        CLI->>DependencyError: PrintDependencyTree(depErr, Stderr)
        DependencyError->>Stderr: write lets:-prefixed tree

        CLI->>DependencyError: FailureMessage()
        DependencyError->>ExecuteError: unwrap to ExecuteError
        ExecuteError->>ExecuteError: Cause()
        ExecuteError-->>DependencyError: underlying error
        DependencyError-->>CLI: concise failure message

        CLI->>Logger: Errorf("%s", failureMessage)
        CLI->>CLI: getExitCode(err, 1)
        CLI-->>User: process exit with code
    else other_error
        CLI->>Logger: Errorf("%s", err.Error())
        CLI->>CLI: getExitCode(err, 1)
        CLI-->>User: process exit with code
    end
Loading

Class diagram for updated error types DependencyError and ExecuteError

classDiagram
    class DependencyError {
        +[]string Chain
        +error Err
        +Error() string
        +Unwrap() error
        +ExitCode() int
        +FailureMessage() string
    }

    class ExecuteError {
        +Error() string
        +Unwrap() error
        +Cause() error
        +ExitCode() int
    }

    DependencyError --> ExecuteError : may_wrap
    ExecuteError --> error : wraps
    DependencyError --> error : wraps

    class CLI {
        +Main(version string, buildDate string) int
    }

    CLI --> DependencyError : handles
    CLI --> ExecuteError : receives_via_error_chain
Loading

File-Level Changes

Change Details Files
Add FailureMessage behavior to DependencyError and root-cause extraction to ExecuteError for clearer failure messages.
  • Introduce DependencyError.FailureMessage to return the unwrapped ExecuteError cause when applicable, or the original error otherwise.
  • Add ExecuteError.Unwrap and ExecuteError.Cause helpers to expose the underlying error and its root cause.
  • Add unit tests validating FailureMessage behavior for plain execute errors, non-execute errors, and path-aware execute errors.
internal/executor/dependency_error.go
internal/executor/dependency_error_test.go
internal/executor/executor.go
Change dependency tree output to be prefixed with lets: and adjust indentation to support the new format.
  • Introduce a treePrefix constant and derive indentation for tree levels based on its length.
  • Ensure the first dependency line is prefixed with lets: while subsequent lines are indented to align visually under the prefix.
  • Update dependency tree tests to assert the new prefixes and indentation layout.
internal/executor/dependency_error.go
internal/executor/dependency_error_test.go
tests/dependency_failure_tree.bats
Emit a separate final status line for failed commands and adjust CLI behavior to use the new failure messaging.
  • After printing a DependencyError tree, log a separate final failure line using DependencyError.FailureMessage via the CLI.
  • Return the appropriate exit code when handling DependencyError failures through getExitCode.
  • Update bats tests to assert presence of lets:-prefixed command lines and final lets: exit status N lines.
internal/cli/cli.go
tests/dependency_failure_tree.bats
tests/command_cmd.bats
tests/default_env.bats
Document the new failure output format in the changelog.
  • Add a changelog entry describing the new lets:-prefixed tree and separate status line format for command failures.
docs/docs/changelog.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="tests/command_cmd.bats" line_range="43-48" />
<code_context>
     # we can not guarantee that all commands will run and complete.
     # But error message must be in the output.
-    assert_output --partial "failed to run command 'cmd-as-map-error': exit status 2"
+    assert_output --partial "lets: cmd-as-map-error"
+    assert_output --partial "lets: exit status 2"
 }

</code_context>
<issue_to_address>
**suggestion (testing):** Assert ordering and avoid accidentally allowing the old verbose error format

The new expectations only verify that the `lets:` lines appear somewhere in the output. To better capture the intended behavior and prevent regressions, please also (1) assert that the command line appears before the `exit status` line, and (2) assert that the old verbose message (`"failed to run command 'cmd-as-map-error': exit status 2"`) does not appear.

```suggestion
    # as there is no guarantee in which order cmds runs
    # we can not guarantee that all commands will run and complete.
    # But error message must be in the output.
    assert_output --partial "lets: cmd-as-map-error"
    assert_output --partial "lets: exit status 2"

    # Ensure the command line appears before the exit status line.
    local cmd_prefix exit_prefix
    cmd_prefix=${output%%lets: cmd-as-map-error*}
    exit_prefix=${output%%lets: exit status 2*}

    # Sanity check: both fragments must be present
    [ "$cmd_prefix" != "$output" ]
    [ "$exit_prefix" != "$output" ]

    # cmd-as-map-error must appear before the exit status
    (( ${#cmd_prefix} < ${#exit_prefix} ))

    # Ensure the old verbose error format is not present.
    refute_output --partial "failed to run command 'cmd-as-map-error': exit status 2"
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 43 to 48
# as there is no guarantee in which order cmds runs
# we can not guarantee that all commands will run and complete.
# But error message must be in the output.
assert_output --partial "failed to run command 'cmd-as-map-error': exit status 2"
assert_output --partial "lets: cmd-as-map-error"
assert_output --partial "lets: exit status 2"
}
Copy link

Choose a reason for hiding this comment

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

suggestion (testing): Assert ordering and avoid accidentally allowing the old verbose error format

The new expectations only verify that the lets: lines appear somewhere in the output. To better capture the intended behavior and prevent regressions, please also (1) assert that the command line appears before the exit status line, and (2) assert that the old verbose message ("failed to run command 'cmd-as-map-error': exit status 2") does not appear.

Suggested change
# as there is no guarantee in which order cmds runs
# we can not guarantee that all commands will run and complete.
# But error message must be in the output.
assert_output --partial "failed to run command 'cmd-as-map-error': exit status 2"
assert_output --partial "lets: cmd-as-map-error"
assert_output --partial "lets: exit status 2"
}
# as there is no guarantee in which order cmds runs
# we can not guarantee that all commands will run and complete.
# But error message must be in the output.
assert_output --partial "lets: cmd-as-map-error"
assert_output --partial "lets: exit status 2"
# Ensure the command line appears before the exit status line.
local cmd_prefix exit_prefix
cmd_prefix=${output%%lets: cmd-as-map-error*}
exit_prefix=${output%%lets: exit status 2*}
# Sanity check: both fragments must be present
[ "$cmd_prefix" != "$output" ]
[ "$exit_prefix" != "$output" ]
# cmd-as-map-error must appear before the exit status
(( ${#cmd_prefix} < ${#exit_prefix} ))
# Ensure the old verbose error format is not present.
refute_output --partial "failed to run command 'cmd-as-map-error': exit status 2"
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant