Skip to content

Commit

Permalink
[Windows-Compatibilty-feature] - Initial changes to add Powershell sc…
Browse files Browse the repository at this point in the history
…ript as an alternative to grep
  • Loading branch information
kthatipally committed Aug 9, 2024
1 parent 974a5d2 commit e471059
Showing 1 changed file with 67 additions and 13 deletions.
80 changes: 67 additions & 13 deletions provider/internal/builtin/service_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -83,14 +84,12 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
if c.Pattern == "" {
return response, fmt.Errorf("could not parse provided regex pattern as string: %v", conditionInfo)
}

var outputBytes []byte
grep := exec.Command("grep", "-o", "-n", "-R", "-P", c.Pattern, p.config.Location)
outputBytes, err := grep.Output()
//Runs on Windows using PowerShell.exe and Unix based systems using grep
outputBytes, err := runOSSpecificGrepCommand(c.Pattern, p.config.Location)
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
return response, nil
}
return response, fmt.Errorf("could not run grep with provided pattern %+v", err)
return response, err
}

matches := []string{}
Expand All @@ -100,13 +99,10 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
}

for _, match := range matches {
//TODO(fabianvf): This will not work if there is a `:` in the filename, do we care?
pieces := strings.SplitN(match, ":", 3)
if len(pieces) != 3 {
//TODO(fabianvf): Just log or return?
//(shawn-hurley): I think the return is good personally
return response, fmt.Errorf(
"malformed response from grep, cannot parse grep output '%s' with pattern {filepath}:{lineNumber}:{matchingText}", match)
var pieces []string
pieces, err := parseGrepOutputForFileContent(match)
if err != nil {
return response, fmt.Errorf("could not parse grep output '%s' for the Pattern '%v': %v ", match, c.Pattern, err)
}

containsFile, err := provider.FilterFilePattern(c.FilePattern, pieces[0])
Expand All @@ -130,6 +126,7 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
if err != nil {
return response, fmt.Errorf("cannot convert line number string to integer")
}

response.Incidents = append(response.Incidents, provider.IncidentContext{
FileURI: uri.File(absPath),
LineNumber: &lineNumber,
Expand Down Expand Up @@ -497,3 +494,60 @@ func (b *builtinServiceClient) isFileIncluded(absolutePath string) bool {
b.log.V(7).Info("excluding file from search", "file", absolutePath)
return false
}

func parseGrepOutputForFileContent(match string) ([]string, error) {
// This will parse the output of the PowerShell/grep in the form
// "Filepath:Linenumber:Matchingtext" to return string array of path, line number and matching text
// works with handling both windows and unix based file paths eg: "C:\path\to\file" and "/path/to/file"
re, err := regexp.Compile(`^(.*?):(\d+):(.*)$`)
if err != nil {
return nil, fmt.Errorf("failed to compile regular expression: %v", err)
}
submatches := re.FindStringSubmatch(match)
if len(submatches) != 4 {
return nil, fmt.Errorf(
"malformed response from file search, cannot parse result '%s' with pattern %#q", match, re)
}
return submatches[1:], nil
}

func runOSSpecificGrepCommand(pattern string, location string) ([]byte, error) {
var outputBytes []byte
var err error
var utilName string

if runtime.GOOS == "windows" {
utilName = "powershell.exe"
// Windows does not have grep, so we use PowerShell.exe's Select-String instead
// This is a workaround until we can find a better solution
psScript := `
$pattern = $env:PATTERN
$location = $env:FILEPATH
Get-ChildItem -Path $location -Recurse -File | ForEach-Object {
$file = $_
# Search for the pattern in the file
Select-String -Path $file.FullName -Pattern $pattern -AllMatches | ForEach-Object {
foreach ($match in $_.Matches) {
"{0}:{1}:{2}" -f $file.FullName, $_.LineNumber, $match.Value
}
}
}`
findstr := exec.Command(utilName, "-Command", psScript)
findstr.Env = append(os.Environ(), "PATTERN="+pattern, "FILEPATH="+location)
outputBytes, err = findstr.Output()

} else {
utilName = "grep"
// Linux and MacOS use grep
grep := exec.Command(utilName, "-o", "-n", "-R", "-P", pattern, location)
outputBytes, err = grep.Output()
}
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
return nil, nil
}
return nil, fmt.Errorf("could not run '%s' with provided pattern %+v", utilName, err)
}

return outputBytes, nil
}

0 comments on commit e471059

Please sign in to comment.