Harden lint cancellation handling
This commit is contained in:
parent
47c0f56863
commit
49bf3c36f4
3 changed files with 147 additions and 1 deletions
|
|
@ -206,6 +206,15 @@ func (adapter CommandAdapter) Run(ctx context.Context, input RunInput, files []s
|
|||
}
|
||||
}
|
||||
|
||||
if err := runContext.Err(); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
result.Tool.Status = "timeout"
|
||||
} else {
|
||||
result.Tool.Status = "canceled"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
if adapter.parseOutput != nil && output != "" {
|
||||
result.Findings = adapter.parseOutput(adapter.name, adapter.category, output)
|
||||
}
|
||||
|
|
@ -300,7 +309,7 @@ func (CatalogAdapter) Category() string { return "correctness" }
|
|||
|
||||
func (CatalogAdapter) Fast() bool { return true }
|
||||
|
||||
func (CatalogAdapter) Run(_ context.Context, input RunInput, files []string) AdapterResult {
|
||||
func (CatalogAdapter) Run(ctx context.Context, input RunInput, files []string) AdapterResult {
|
||||
startedAt := time.Now()
|
||||
result := AdapterResult{
|
||||
Tool: ToolRun{
|
||||
|
|
@ -346,6 +355,9 @@ func (CatalogAdapter) Run(_ context.Context, input RunInput, files []string) Ada
|
|||
var findings []Finding
|
||||
if len(files) > 0 {
|
||||
for _, file := range files {
|
||||
if err := ctx.Err(); err != nil {
|
||||
break
|
||||
}
|
||||
scanPath := file
|
||||
if !filepath.IsAbs(scanPath) {
|
||||
scanPath = filepath.Join(input.Path, file)
|
||||
|
|
@ -357,9 +369,22 @@ func (CatalogAdapter) Run(_ context.Context, input RunInput, files []string) Ada
|
|||
findings = append(findings, fileFindings...)
|
||||
}
|
||||
} else {
|
||||
if ctx.Err() != nil {
|
||||
result.Tool.Status = "canceled"
|
||||
result.Tool.Duration = time.Since(startedAt).Round(time.Millisecond).String()
|
||||
return result
|
||||
}
|
||||
findings, _ = scanner.ScanDir(input.Path)
|
||||
}
|
||||
|
||||
if err := ctx.Err(); err != nil {
|
||||
result.Tool.Status = "canceled"
|
||||
result.Tool.Duration = time.Since(startedAt).Round(time.Millisecond).String()
|
||||
result.Tool.Findings = len(findings)
|
||||
result.Findings = findings
|
||||
return result
|
||||
}
|
||||
|
||||
for index := range findings {
|
||||
rule := catalog.ByID(findings[index].RuleID)
|
||||
findings[index].Tool = "catalog"
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ func (service *Service) Run(ctx context.Context, input RunInput) (Report, error)
|
|||
var toolRuns []ToolRun
|
||||
|
||||
for _, adapter := range selectedAdapters {
|
||||
if err := ctx.Err(); err != nil {
|
||||
break
|
||||
}
|
||||
if input.Hook && !adapter.Fast() {
|
||||
toolRuns = append(toolRuns, ToolRun{
|
||||
Name: adapter.Name(),
|
||||
|
|
|
|||
|
|
@ -566,6 +566,38 @@ func TestServiceTools_EmptyInventoryReturnsEmptySlice(t *testing.T) {
|
|||
assert.Empty(t, tools)
|
||||
}
|
||||
|
||||
func TestServiceRun_Good_StopsDispatchingAfterContextCancel(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "composer.json"), []byte("{\n \"name\": \"example/test\"\n}\n"), 0o644))
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(dir, ".core"), 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".core", "lint.yaml"), []byte(`lint:
|
||||
php:
|
||||
- first
|
||||
- second
|
||||
`), 0o644))
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
var secondRan bool
|
||||
svc := &Service{adapters: []Adapter{
|
||||
cancellingAdapter{name: "first", cancel: cancel},
|
||||
recordingAdapter{name: "second", ran: &secondRan},
|
||||
}}
|
||||
|
||||
report, err := svc.Run(ctx, RunInput{
|
||||
Path: dir,
|
||||
Lang: "php",
|
||||
FailOn: "warning",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, report.Tools, 1)
|
||||
assert.Equal(t, "first", report.Tools[0].Name)
|
||||
assert.False(t, secondRan)
|
||||
assert.Empty(t, report.Findings)
|
||||
}
|
||||
|
||||
type shortcutAdapter struct {
|
||||
name string
|
||||
category string
|
||||
|
|
@ -599,6 +631,92 @@ func (adapter shortcutAdapter) Run(_ context.Context, _ RunInput, _ []string) Ad
|
|||
}
|
||||
}
|
||||
|
||||
type recordingAdapter struct {
|
||||
name string
|
||||
ran *bool
|
||||
}
|
||||
|
||||
func (adapter recordingAdapter) Name() string { return adapter.name }
|
||||
|
||||
func (adapter recordingAdapter) Available() bool { return true }
|
||||
|
||||
func (adapter recordingAdapter) Languages() []string { return []string{"php"} }
|
||||
|
||||
func (adapter recordingAdapter) Command() string { return adapter.name }
|
||||
|
||||
func (adapter recordingAdapter) Entitlement() string { return "" }
|
||||
|
||||
func (adapter recordingAdapter) RequiresEntitlement() bool { return false }
|
||||
|
||||
func (adapter recordingAdapter) MatchesLanguage(languages []string) bool {
|
||||
for _, language := range languages {
|
||||
if language == "php" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (adapter recordingAdapter) Category() string { return "correctness" }
|
||||
|
||||
func (adapter recordingAdapter) Fast() bool { return true }
|
||||
|
||||
func (adapter recordingAdapter) Run(_ context.Context, _ RunInput, _ []string) AdapterResult {
|
||||
if adapter.ran != nil {
|
||||
*adapter.ran = true
|
||||
}
|
||||
return AdapterResult{
|
||||
Tool: ToolRun{
|
||||
Name: adapter.name,
|
||||
Status: "passed",
|
||||
Duration: "0s",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type cancellingAdapter struct {
|
||||
name string
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
func (adapter cancellingAdapter) Name() string { return adapter.name }
|
||||
|
||||
func (adapter cancellingAdapter) Available() bool { return true }
|
||||
|
||||
func (adapter cancellingAdapter) Languages() []string { return []string{"php"} }
|
||||
|
||||
func (adapter cancellingAdapter) Command() string { return adapter.name }
|
||||
|
||||
func (adapter cancellingAdapter) Entitlement() string { return "" }
|
||||
|
||||
func (adapter cancellingAdapter) RequiresEntitlement() bool { return false }
|
||||
|
||||
func (adapter cancellingAdapter) MatchesLanguage(languages []string) bool {
|
||||
for _, language := range languages {
|
||||
if language == "php" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (adapter cancellingAdapter) Category() string { return "correctness" }
|
||||
|
||||
func (adapter cancellingAdapter) Fast() bool { return true }
|
||||
|
||||
func (adapter cancellingAdapter) Run(_ context.Context, _ RunInput, _ []string) AdapterResult {
|
||||
if adapter.cancel != nil {
|
||||
adapter.cancel()
|
||||
}
|
||||
return AdapterResult{
|
||||
Tool: ToolRun{
|
||||
Name: adapter.name,
|
||||
Status: "passed",
|
||||
Duration: "0s",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type duplicateAdapter struct {
|
||||
name string
|
||||
finding Finding
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue