chore: improved log message for failing hooks#5603
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRefactors GCP credential handling, changes impersonation option composition, introduces formatted hook execution error messages with unit tests, wraps tflint execution errors to preserve underlying errors, and adds a hook-exit-code fixture plus an integration test asserting non-zero hook reporting. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
50ff568 to
92cf5d1
Compare
internal/tflint/tflint.go
Outdated
| func (err ErrorRunningTflint) Error() string { | ||
| return fmt.Sprintf("Error while running tflint with args: %v", err.args) | ||
| if err.Err != nil { | ||
| return fmt.Sprintf("Error while running tflint with args: %v: %s", err.Args, err.Err) |
There was a problem hiding this comment.
| return fmt.Sprintf("Error while running tflint with args: %v: %s", err.Args, err.Err) | |
| return fmt.Sprintf("Error encountered while running tflint with args: '%v': %s", err.Args, err.Err) |
internal/tflint/tflint.go
Outdated
| return fmt.Sprintf("Error while running tflint with args: %v: %s", err.Args, err.Err) | ||
| } | ||
|
|
||
| return fmt.Sprintf("Error while running tflint with args: %v", err.Args) |
There was a problem hiding this comment.
| return fmt.Sprintf("Error while running tflint with args: %v", err.Args) | |
| return fmt.Sprintf("Error encountered while running tflint with args: '%v'", err.Args) |
internal/runner/run/hook.go
Outdated
| } | ||
|
|
||
| if exitCodeErr == nil && output != "" { | ||
| return fmt.Sprintf("Hook %q (command: %s) failed with exit code %d:\n%s", hookName, cmd, exitCode, output) |
There was a problem hiding this comment.
| return fmt.Sprintf("Hook %q (command: %s) failed with exit code %d:\n%s", hookName, cmd, exitCode, output) | |
| return fmt.Sprintf("Hook %q (command: %s) exited with non-zero exit code %d:\n%s", hookName, cmd, exitCode, output) |
internal/runner/run/hook.go
Outdated
| return fmt.Sprintf("Hook %q (command: %s) failed with exit code %d:\n%s", hookName, cmd, exitCode, output) | ||
| } | ||
|
|
||
| if exitCodeErr == nil { |
internal/runner/run/hook.go
Outdated
| } | ||
|
|
||
| if exitCodeErr == nil { | ||
| return fmt.Sprintf("Hook %q (command: %s) failed with exit code %d", hookName, cmd, exitCode) |
There was a problem hiding this comment.
| return fmt.Sprintf("Hook %q (command: %s) failed with exit code %d", hookName, cmd, exitCode) | |
| return fmt.Sprintf("Hook %q (command: %s) exited with non-zero exit code %d", hookName, cmd, exitCode) |
internal/runner/run/hook.go
Outdated
| } | ||
| } | ||
|
|
||
| return fmt.Sprintf("Hook %q failed: %s", hookName, err.Error()) |
There was a problem hiding this comment.
| return fmt.Sprintf("Hook %q failed: %s", hookName, err.Error()) | |
| return fmt.Sprintf("Hook %q failed to execute: %s", hookName, err.Error()) |
Assuming this is correct. I'm not sure about that.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
test/integration_hooks_test.go (1)
480-483: Strengthen this test to assert hook name and command context too.The new assertions cover exit code + output, but the PR goal also includes clearer hook identity/command context. Add assertions for those substrings so regressions in message context are caught.
Suggested assertion extension
output := stderr.String() // Error message should show exit code and the actual hook output + assert.Contains(t, output, `Hook "hook_exit_nonzero"`) + assert.Contains(t, output, "command:") assert.Contains(t, output, `exited with non-zero exit code 2`) assert.Contains(t, output, "lint warning: something is wrong")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/integration_hooks_test.go` around lines 480 - 483, The test currently asserts the exit code and hook output but misses checking hook identity and command context; update the assertions around the local variable output (in test/integration_hooks_test.go) to also assert that output contains the hook name (e.g., the hook identifier string used in the test) and the executed command context (e.g., the command invocation or arguments shown in logs), by adding two assert.Contains calls referencing the same output variable so the failure message includes both the hook name and the command string.internal/runner/run/hook.go (1)
41-41: Render command arguments with quoting to avoid ambiguous diagnostics.Joining raw args with spaces can misrepresent commands when args contain spaces/quotes. Quoting each token makes the logged command deterministic and copy/paste-safe.
Suggested update
import ( "context" "fmt" + "strconv" "slices" "strings" "sync" @@ - cmd := strings.Join(append([]string{processErr.Command}, processErr.Args...), " ") + parts := append([]string{processErr.Command}, processErr.Args...) + for i := range parts { + parts[i] = strconv.Quote(parts[i]) + } + cmd := strings.Join(parts, " ")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/runner/run/hook.go` at line 41, The logged command is built by joining processErr.Command and processErr.Args raw which misrepresents tokens containing spaces or quotes; update the construction of cmd (where processErr.Command and processErr.Args are used) to quote each token deterministically (e.g., map strconv.Quote over the command and each arg or otherwise shell-escape each token) and then join the quoted tokens with spaces so the diagnostic is copy/paste-safe and unambiguous.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@internal/runner/run/hook.go`:
- Line 41: The logged command is built by joining processErr.Command and
processErr.Args raw which misrepresents tokens containing spaces or quotes;
update the construction of cmd (where processErr.Command and processErr.Args are
used) to quote each token deterministically (e.g., map strconv.Quote over the
command and each arg or otherwise shell-escape each token) and then join the
quoted tokens with spaces so the diagnostic is copy/paste-safe and unambiguous.
In `@test/integration_hooks_test.go`:
- Around line 480-483: The test currently asserts the exit code and hook output
but misses checking hook identity and command context; update the assertions
around the local variable output (in test/integration_hooks_test.go) to also
assert that output contains the hook name (e.g., the hook identifier string used
in the test) and the executed command context (e.g., the command invocation or
arguments shown in logs), by adding two assert.Contains calls referencing the
same output variable so the failure message includes both the hook name and the
command string.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
internal/gcphelper/config.gointernal/runner/run/hook.gointernal/runner/run/hook_internal_test.gointernal/tflint/tflint.gotest/integration_hooks_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
internal/gcphelper/config.go (1)
104-118: Good fix for impersonation, but the comment is now stale.Passing
clientOpts...toCredentialsTokenSourcecorrectly allows impersonation to use explicitly provided credentials as the source identity, rather than always relying on ADC. This is the right behavior.However, the comment on lines 105-107 now contradicts the implementation — it states the impersonate library "uses Application Default Credentials internally as the source identity," but with this change, it uses the gathered
clientOptsfirst (falling back to ADC only if none were provided).📝 Suggested comment update
// Handle service account impersonation. - // When impersonation is configured, the impersonation token source replaces - // any base credentials. The impersonate library uses Application Default - // Credentials internally as the source identity. + // When impersonation is configured, the impersonation token source replaces + // any base credentials. The impersonate library uses the provided credentials + // (or Application Default Credentials if none were provided) as the source identity. if gcpCfg != nil && gcpCfg.ImpersonateServiceAccount != "" {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/gcphelper/config.go` around lines 104 - 118, Update the stale comment above the impersonation block to reflect the actual behavior: note that when gcpCfg.ImpersonateServiceAccount is set we call impersonate.CredentialsTokenSource with the current clientOpts so the impersonation token source will use the explicitly provided credentials as the source identity (falling back to ADC only if no clientOpts are supplied), instead of claiming the impersonate library always uses Application Default Credentials.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@internal/gcphelper/config.go`:
- Around line 104-118: Update the stale comment above the impersonation block to
reflect the actual behavior: note that when gcpCfg.ImpersonateServiceAccount is
set we call impersonate.CredentialsTokenSource with the current clientOpts so
the impersonation token source will use the explicitly provided credentials as
the source identity (falling back to ADC only if no clientOpts are supplied),
instead of claiming the impersonate library always uses Application Default
Credentials.
Resolve conflict in internal/gcphelper/config.go: use `env` variable with two-line pattern to keep envCreds in outer scope. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Description
When a hook (like tflint or a custom linter) exits with a non-zero code, the error message now shows the command, exit code, and actual output instead of the generic "Failed to execute ...
exit status 2".
Fixes #5596.
TODOs
Read the Gruntwork contribution guidelines.
Release Notes (draft)
Added / Removed / Updated [X].
Migration Guide
Summary by CodeRabbit
New Features
Bug Fixes
Tests