Skip to content

Commit

Permalink
handle *grep errors
Browse files Browse the repository at this point in the history
Try parsing the stdout of *grep even in case of an error. This allows to
return output when hitting, for instance, eperms on certain files.

The stderr of *grep is now logged directly while the exit code is being
recorded and used by vgrep on exit.

Fixes: #216
Signed-off-by: Valentin Rothberg <[email protected]>
  • Loading branch information
vrothberg committed May 20, 2024
1 parent 97a6e61 commit 14fe855
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 12 deletions.
39 changes: 39 additions & 0 deletions test/errors.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bats -t

load helpers

# Create a write-only file which causes the *grep implementations to fail. In
# that case, vgrep should still parse the stdout of *grep and return the error
# reported on stdout along with the exit code.

WONLY_FILE=test/search_files/wonly.txt

function setup() {
touch $WONLY_FILE
chmod 200 $WONLY_FILE
}

function teardown() {
rm $WONLY_FILE
}

@test "Search with permission error" {
run_vgrep --no-header foo test/search_files
[ "$status" -eq 2 ]
[[ ${lines[0]} =~ "test/search_files/wonly.txt: Permission denied (os error 13)" ]]
[[ ${lines[1]} =~ "bar baz" ]]
}

@test "Search with permission error (--no-ripgrep)" {
# Since the file isn't under version control, git will not try to read it
run_vgrep --no-header --no-ripgrep foo test/search_files
[ "$status" -eq 0 ]
[[ ${lines[0]} =~ "bar baz" ]]
}

@test "Search with permission error (--no-git --no-ripgrep)" {
run_vgrep --no-header --no-git --no-ripgrep foo test/search_files
[ "$status" -eq 2 ]
[[ ${lines[0]} =~ "test/search_files/wonly.txt: Permission denied" ]]
[[ ${lines[1]} =~ "bar baz" ]]
}
2 changes: 2 additions & 0 deletions test/helpers.bash
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ function run_vgrep() {
fi
run $VGREP $args "$@"
if [ "$status" -ne 0 ]; then
echo "-------------"
echo "CLI: $VGREP $args $*"
echo "OUT: $output"
echo "-------------"
fi
}
35 changes: 23 additions & 12 deletions vgrep.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,11 @@ type cliArgs struct {
// vgrep stores state and the user-specified command-line arguments.
type vgrep struct {
cliArgs
matches [][]string
workDir string
lock lockfile.Lockfile
waiter sync.WaitGroup
exitCode int
matches [][]string
workDir string
lock lockfile.Lockfile
waiter sync.WaitGroup
}

// the type of underlying grep program
Expand Down Expand Up @@ -169,6 +170,9 @@ func main() {
}

if len(v.matches) == 0 {
if v.exitCode != 0 {
os.Exit(v.exitCode)
}
os.Exit(1)
}

Expand All @@ -178,7 +182,7 @@ func main() {
v.commandPrintMatches([]int{})
}
v.waiter.Wait()
os.Exit(0)
os.Exit(v.exitCode)
}

v.waiter.Add(1)
Expand All @@ -187,13 +191,16 @@ func main() {

if len(v.matches) == 0 {
v.waiter.Wait()
if v.exitCode != 0 {
os.Exit(v.exitCode)
}
os.Exit(1)
}

// Last resort, print all matches.
v.commandPrintMatches([]int{})
v.waiter.Wait()
os.Exit(0)
os.Exit(v.exitCode)
}

// runCommand executes the program specified in args and returns the stdout as
Expand All @@ -212,13 +219,17 @@ func (v *vgrep) runCommand(args []string, env string) ([]string, error) {
err := cmd.Run()
if err != nil {
logrus.Debugf("error running command: %v", err)
errStr := err.Error()
if errStr == "exit status 1" {
logrus.Debug("ignoring error (no matches found)")

if exitError, ok := err.(*exec.ExitError); ok {
exitCode := exitError.ExitCode()
switch exitCode {
case 1:
logrus.Debug("ignoring error (no matches found)")
default:
logrus.Errorf("%s", strings.TrimSuffix(serr.String(), "\n"))
v.exitCode = exitCode
}
err = nil
} else {
spl := strings.Split(serr.String(), "\n")
err = fmt.Errorf("%s [%s]", spl[0], args[0])
}
}

Expand Down

0 comments on commit 14fe855

Please sign in to comment.