Skip to content

Commit fc40136

Browse files
kthatipallyeemcmullan
authored andcommitted
✨ Windows Compatibility-Add PowerShell Script as an Alternative to Grep on Linux (konveyor#690)
PR Summary This pull request introduces a PowerShell-based alternative to the grep command, ensuring that the built-in File Searching feature is also compatible with Windows systems. service_client.go: Added runOSSpecificGrepCommand to handle grep functionality for both Windows (using PowerShell) and Unix-based systems. This function checks for the OS platform and invokes powershell(windows) and grep(linux). Replaced direct grep command execution with runOSSpecificGrepCommand to support cross-platform compatibility. service_client.go: Implemented parseGrepOutputForFileContent to parse the output from both PowerShell and grep. This enhancement not only refines the existing parsing mechanism for Linux but also accommodates Windows file path patterns and addresses issues with the split function when multiple ':' appear in the input. WIP: Testing --------- Signed-off-by: kthatipally <[email protected]> Signed-off-by: Emily McMullan <[email protected]>
1 parent 84c6b79 commit fc40136

File tree

1 file changed

+72
-24
lines changed

1 file changed

+72
-24
lines changed

provider/internal/builtin/service_client.go

Lines changed: 72 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -84,25 +84,12 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
8484
if c.Pattern == "" {
8585
return response, fmt.Errorf("could not parse provided regex pattern as string: %v", conditionInfo)
8686
}
87+
8788
var outputBytes []byte
88-
var err error
89-
if runtime.GOOS == "darwin" {
90-
cmd := fmt.Sprintf(
91-
`find %v -type f | \
92-
while read file; do perl -ne '/(%v)/ && print "$ARGV:$.:$1\n";' "$file"; done`,
93-
p.config.Location, c.Pattern,
94-
)
95-
findstr := exec.Command("/bin/sh", "-c", cmd)
96-
outputBytes, err = findstr.Output()
97-
} else {
98-
grep := exec.Command("grep", "-o", "-n", "-R", "-P", c.Pattern, p.config.Location)
99-
outputBytes, err = grep.Output()
100-
}
89+
//Runs on Windows using PowerShell.exe and Unix based systems using grep
90+
outputBytes, err := runOSSpecificGrepCommand(c.Pattern, p.config.Location, cond.ProviderContext)
10191
if err != nil {
102-
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
103-
return response, nil
104-
}
105-
return response, fmt.Errorf("could not run grep with provided pattern %+v", err)
92+
return response, err
10693
}
10794
matches := []string{}
10895
outputString := strings.TrimSpace(string(outputBytes))
@@ -111,13 +98,10 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
11198
}
11299

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

123107
containsFile, err := provider.FilterFilePattern(c.FilePattern, pieces[0])
@@ -141,6 +125,7 @@ func (p *builtinServiceClient) Evaluate(ctx context.Context, cap string, conditi
141125
if err != nil {
142126
return response, fmt.Errorf("cannot convert line number string to integer")
143127
}
128+
144129
response.Incidents = append(response.Incidents, provider.IncidentContext{
145130
FileURI: uri.File(absPath),
146131
LineNumber: &lineNumber,
@@ -508,3 +493,66 @@ func (b *builtinServiceClient) isFileIncluded(absolutePath string) bool {
508493
b.log.V(7).Info("excluding file from search", "file", absolutePath)
509494
return false
510495
}
496+
497+
func parseGrepOutputForFileContent(match string) ([]string, error) {
498+
// This will parse the output of the PowerShell/grep in the form
499+
// "Filepath:Linenumber:Matchingtext" to return string array of path, line number and matching text
500+
// works with handling both windows and unix based file paths eg: "C:\path\to\file" and "/path/to/file"
501+
re, err := regexp.Compile(`^(.*?):(\d+):(.*)$`)
502+
if err != nil {
503+
return nil, fmt.Errorf("failed to compile regular expression: %v", err)
504+
}
505+
submatches := re.FindStringSubmatch(match)
506+
if len(submatches) != 4 {
507+
return nil, fmt.Errorf(
508+
"malformed response from file search, cannot parse result '%s' with pattern %#q", match, re)
509+
}
510+
return submatches[1:], nil
511+
}
512+
513+
func runOSSpecificGrepCommand(pattern string, location string, providerContext provider.ProviderContext) ([]byte, error) {
514+
var outputBytes []byte
515+
var err error
516+
var utilName string
517+
518+
if runtime.GOOS == "windows" {
519+
utilName = "powershell.exe"
520+
// Windows does not have grep, so we use PowerShell.exe's Select-String instead
521+
// This is a workaround until we can find a better solution
522+
psScript := `
523+
$pattern = $env:PATTERN
524+
$location = $env:FILEPATH
525+
Get-ChildItem -Path $location -Recurse -File | ForEach-Object {
526+
$file = $_
527+
# Search for the pattern in the file
528+
Select-String -Path $file.FullName -Pattern $pattern -AllMatches | ForEach-Object {
529+
foreach ($match in $_.Matches) {
530+
"{0}:{1}:{2}" -f $file.FullName, $_.LineNumber, $match.Value
531+
}
532+
}
533+
}`
534+
findstr := exec.Command(utilName, "-Command", psScript)
535+
findstr.Env = append(os.Environ(), "PATTERN="+pattern, "FILEPATH="+location)
536+
outputBytes, err = findstr.Output()
537+
538+
} else if runtime.GOOS == "darwin" {
539+
cmd := fmt.Sprintf(
540+
`find %v -type f | \
541+
while read file; do perl -ne '/(%v)/ && print "$ARGV:$.:$1\n";' "$file"; done`,
542+
location, pattern,
543+
)
544+
findstr := exec.Command("/bin/sh", "-c", cmd)
545+
outputBytes, err = findstr.Output()
546+
} else {
547+
grep := exec.Command("grep", "-o", "-n", "-R", "-P", pattern, location)
548+
outputBytes, err = grep.Output()
549+
}
550+
if err != nil {
551+
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
552+
return nil, nil
553+
}
554+
return nil, fmt.Errorf("could not run '%s' with provided pattern %+v", utilName, err)
555+
}
556+
557+
return outputBytes, nil
558+
}

0 commit comments

Comments
 (0)