Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.5.1, Faster & more stable #374

Merged
merged 10 commits into from
Nov 26, 2023
2 changes: 2 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ jobs:
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Test
run: go test ./...
- name: Build
run: make build
- name: Clean modcache
Expand Down
2 changes: 0 additions & 2 deletions benchmarks/html_report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package benchmarks

import (
"crypto/sha256"
"fmt"
html_report "github.com/daveshanley/vacuum/html-report"
"github.com/daveshanley/vacuum/model"
"github.com/daveshanley/vacuum/motor"
Expand Down Expand Up @@ -121,5 +120,4 @@ func TestHtmlReport_GenerateReportIdenticalRun200(t *testing.T) {
}

}
fmt.Print("done")
}
59 changes: 46 additions & 13 deletions cmd/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ func GetLintCommand() *cobra.Command {
noStyleFlag, _ := cmd.Flags().GetBool("no-style")
baseFlag, _ := cmd.Flags().GetString("base")
skipCheckFlag, _ := cmd.Flags().GetBool("skip-check")
remoteFlag, _ := cmd.Flags().GetBool("remote")

// disable color and styling, for CI/CD use.
// https://github.com/daveshanley/vacuum/issues/234
Expand Down Expand Up @@ -75,8 +76,18 @@ func GetLintCommand() *cobra.Command {
}

// setup logging
handler := pterm.NewSlogHandler(&pterm.DefaultLogger)
pterm.DefaultLogger.Level = pterm.LogLevelError
handler := pterm.NewSlogHandler(&pterm.Logger{
Formatter: pterm.LogFormatterColorful,
Writer: os.Stdout,
Level: pterm.LogLevelError,
ShowTime: false,
MaxWidth: 280,
KeyStyles: map[string]pterm.Style{
"error": *pterm.NewStyle(pterm.FgRed, pterm.Bold),
"err": *pterm.NewStyle(pterm.FgRed, pterm.Bold),
"caller": *pterm.NewStyle(pterm.FgGray, pterm.Bold),
},
})
logger := slog.New(handler)

defaultRuleSets := rulesets.BuildDefaultRuleSetsWithLogger(logger)
Expand Down Expand Up @@ -121,6 +132,9 @@ func GetLintCommand() *cobra.Command {
}

start := time.Now()

var filesProcessedSize int64
var filesProcessed int
var size int64
for i, arg := range args {

Expand All @@ -135,6 +149,7 @@ func GetLintCommand() *cobra.Command {
lfr := lintFileRequest{
fileName: arg,
baseFlag: baseFlag,
remote: remoteFlag,
multiFile: mf,
skipCheckFlag: skipCheckFlag,
silent: silent,
Expand All @@ -152,7 +167,12 @@ func GetLintCommand() *cobra.Command {
lock: &printLock,
logger: logger,
}
errs = append(errs, lintFile(lfr))
fs, fp, err := lintFile(lfr)

filesProcessedSize = filesProcessedSize + fs + size
filesProcessed = filesProcessed + fp + 1

errs = append(errs, err)
doneChan <- true
}(doneChan, i, arg)
}
Expand All @@ -171,7 +191,7 @@ func GetLintCommand() *cobra.Command {

duration := time.Since(start)

RenderTime(timeFlag, duration, size)
RenderTimeAndFiles(timeFlag, duration, filesProcessedSize, filesProcessed)

if len(errs) > 0 {
return errors.Join(errs...)
Expand Down Expand Up @@ -219,6 +239,7 @@ type lintFileRequest struct {
fileName string
baseFlag string
multiFile bool
remote bool
skipCheckFlag bool
silent bool
detailsFlag bool
Expand All @@ -236,7 +257,7 @@ type lintFileRequest struct {
logger *slog.Logger
}

func lintFile(req lintFileRequest) error {
func lintFile(req lintFileRequest) (int64, int, error) {
// read file.
specBytes, ferr := os.ReadFile(req.fileName)

Expand All @@ -247,16 +268,17 @@ func lintFile(req lintFileRequest) error {

pterm.Error.Printf("Unable to read file '%s': %s\n", req.fileName, ferr.Error())
pterm.Println()
return ferr
return 0, 0, ferr

}

result := motor.ApplyRulesToRuleSet(&motor.RuleSetExecution{
RuleSet: req.selectedRS,
Spec: specBytes,
SpecFileName: req.fileName,
CustomFunctions: req.functions,
Base: req.baseFlag,
AllowLookup: true,
AllowLookup: req.remote,
SkipDocumentCheck: req.skipCheckFlag,
Logger: req.logger,
})
Expand All @@ -268,7 +290,7 @@ func lintFile(req lintFileRequest) error {
pterm.Error.Printf("unable to process spec '%s', error: %s", req.fileName, err.Error())
pterm.Println()
}
return fmt.Errorf("linting failed due to %d issues", len(result.Errors))
return result.FileSize, result.FilesProcessed, fmt.Errorf("linting failed due to %d issues", len(result.Errors))
}

resultSet := model.NewRuleResultSet(results)
Expand All @@ -280,7 +302,7 @@ func lintFile(req lintFileRequest) error {
defer req.lock.Unlock()
if !req.detailsFlag {
RenderSummary(resultSet, req.silent, req.totalFiles, req.fileIndex, req.fileName, req.failSeverityFlag)
return CheckFailureSeverity(req.failSeverityFlag, errs, warnings, informs)
return result.FileSize, result.FilesProcessed, CheckFailureSeverity(req.failSeverityFlag, errs, warnings, informs)
}

abs, _ := filepath.Abs(req.fileName)
Expand All @@ -291,7 +313,7 @@ func lintFile(req lintFileRequest) error {

RenderSummary(resultSet, req.silent, req.totalFiles, req.fileIndex, req.fileName, req.failSeverityFlag)

return CheckFailureSeverity(req.failSeverityFlag, errs, warnings, informs)
return result.FileSize, result.FilesProcessed, CheckFailureSeverity(req.failSeverityFlag, errs, warnings, informs)
}

func processResults(results []*model.RuleFunctionResult, specData []string, snippets, errors bool, silent bool, abs, filename string) {
Expand All @@ -311,11 +333,15 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
if !snippets {
tableData = [][]string{{"Location", "Severity", "Message", "Rule", "Category", "Path"}}
}

// width, height, err := terminal.GetSize(0)
// TODO: determine the terminal size and render the linting results in a table that fits the screen.

for i, r := range results {

if i > 200 {
if i > 1000 {
tableData = append(tableData, []string{"", "", pterm.LightRed(fmt.Sprintf("...%d "+
"more violations not rendered.", len(results)-200)), ""})
"more violations not rendered.", len(results)-1000)), ""})
break
}
if snippets {
Expand All @@ -330,9 +356,16 @@ func processResults(results []*model.RuleFunctionResult, specData []string, snip
startCol = r.StartNode.Column
}

start := fmt.Sprintf("%s:%v:%v", filename, startLine, startCol)
f := filename
if r.Origin != nil {
f = r.Origin.AbsoluteLocation
startLine = r.Origin.Line
startCol = r.Origin.Column
}
start := fmt.Sprintf("%s:%v:%v", f, startLine, startCol)
m := r.Message
p := r.Path

if len(r.Path) > 60 {
p = fmt.Sprintf("%s...", r.Path[:60])
}
Expand Down
19 changes: 19 additions & 0 deletions cmd/shared_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/daveshanley/vacuum/model"
"github.com/daveshanley/vacuum/plugin"
"github.com/daveshanley/vacuum/rulesets"
"github.com/dustin/go-humanize"
"github.com/pb33f/libopenapi/index"
"github.com/pterm/pterm"
"time"
)
Expand All @@ -27,6 +29,23 @@ func BuildRuleSetFromUserSuppliedSet(rsBytes []byte, rs rulesets.RuleSets) (*rul
return rs.GenerateRuleSetFromSuppliedRuleSet(userRS), nil
}

// RenderTimeAndFiles will render out the time taken to process a specification, and the size of the file in kb.
// it will also render out how many files were processed.
func RenderTimeAndFiles(timeFlag bool, duration time.Duration, fileSize int64, totalFiles int) {
if timeFlag {
pterm.Println()
l := "milliseconds"
d := fmt.Sprintf("%d", duration.Milliseconds())
if duration.Milliseconds() > 1000 {
l = "seconds"
d = humanize.FormatFloat("##.##", duration.Seconds())
}
pterm.Info.Println(fmt.Sprintf("vacuum took %s %s to lint %s across %d files", d, l,
index.HumanFileSize(float64(fileSize)), totalFiles))
pterm.Println()
}
}

// RenderTime will render out the time taken to process a specification, and the size of the file in kb.
func RenderTime(timeFlag bool, duration time.Duration, fi int64) {
if timeFlag {
Expand Down
38 changes: 29 additions & 9 deletions functions/openapi/examples.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,25 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext
//check components.
objNode := context.Index.GetSchemasNode()

if context.SpecInfo.SpecFormat == model.OAS3 {
results = checkAllDefinitionsForExamples([]*yaml.Node{objNode}, results, "$.components.schemas", context)
}
if context.SpecInfo.SpecFormat == model.OAS2 {
results = checkAllDefinitionsForExamples([]*yaml.Node{objNode}, results, "$.definitions", context)
ctxComponentsTimeout, cancelComponents := ctx.WithTimeout(ctx.Background(), time.Millisecond*500)
defer cancelComponents()
cc := make(chan bool)
go func(cc chan bool) {
if context.SpecInfo.SpecFormat == model.OAS3 {
results = checkAllDefinitionsForExamples([]*yaml.Node{objNode}, results, "$.components.schemas", context)
}
if context.SpecInfo.SpecFormat == model.OAS2 {
results = checkAllDefinitionsForExamples([]*yaml.Node{objNode}, results, "$.definitions", context)
}
cc <- true
}(cc)

select {
case <-ctxComponentsTimeout.Done():
pterm.Warning.Println("bug: examples function timed-out after 500ms, trying to scan examples for " +
"components. Disable rules that use the example function checking for this spec. Please report this!")
case <-cc:
break
}

// check parameters
Expand All @@ -169,7 +183,7 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext
}
}

ctxTimeout, cancel := ctx.WithTimeout(ctx.Background(), time.Second*1)
ctxTimeout, cancel := ctx.WithTimeout(ctx.Background(), time.Millisecond*500)
defer cancel()
f := make(chan bool)
go func() {
Expand All @@ -192,11 +206,11 @@ func (ex Examples) RunRule(nodes []*yaml.Node, context model.RuleFunctionContext

select {
case <-ctxTimeout.Done():
pterm.Warning.Println("bug: examples function timed-out after a second, trying to scan examples for " +
pterm.Warning.Println("bug: examples function timed-out after 500ms, trying to scan examples for " +
"response bodies, request bodies, and parameters. Disable rules that use the example function checking for this spec. Please report this!")
return *results
case <-f:
// ok
break
}

return *results
Expand Down Expand Up @@ -466,7 +480,7 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
res := model.BuildFunctionResultString(fmt.Sprintf("Schema for `%s` does not "+
"contain any examples or example data", nameNodeValue))

res.StartNode = sLabel
res.StartNode = sValue
res.EndNode = sValue
res.Path = basePath
res.Rule = context.Rule
Expand Down Expand Up @@ -499,6 +513,12 @@ func analyzeExample(nameNodeValue string, mediaTypeNode *yaml.Node, basePath str
// check if the example validates against the convertedSchema
// extract the convertedSchema
if sValue != nil {

//origin := context.Index.GetRolodex().FindNodeOrigin(sValue)
//if origin != nil {
// fmt.Sprintf("no idea.")
//}

convertedSchema, err := parser.ConvertNodeIntoJSONSchema(sValue, context.Index)

if err != nil {
Expand Down
16 changes: 12 additions & 4 deletions functions/openapi/unused_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,15 @@ func (uc UnusedComponent) RunRule(nodes []*yaml.Node, context model.RuleFunction
for key, ref := range resultMap {

// check everything!
if allRefs[key] == nil {
u := strings.Split(key, "#/")
var keyAlt = key
if len(u) == 2 {
if u[0] == "" {
keyAlt = fmt.Sprintf("%s#/%s", context.Index.GetSpecAbsolutePath(), u[1])
}
}

if allRefs[key] == nil && allRefs[keyAlt] == nil {
found := false
// check poly refs if the reference can't be found
if oneOfRefs[key] != nil || allOfRefs[key] != nil || anyOfRefs[key] != nil {
Expand All @@ -128,11 +136,11 @@ func (uc UnusedComponent) RunRule(nodes []*yaml.Node, context model.RuleFunction
_, path := utils.ConvertComponentIdIntoPath(ref.Definition)

// roll back node by one, so we have the actual start.
rolledBack := *ref.Node
rolledBack.Line = ref.Node.Line - 1
//rolledBack := *ref.Node
//rolledBack.Line = ref.Node.Line - 1
results = append(results, model.RuleFunctionResult{
Message: fmt.Sprintf("`%s` is potentially unused or has been orphaned", key),
StartNode: &rolledBack,
StartNode: ref.Node,
EndNode: utils.FindLastChildNodeWithLevel(ref.Node, 0),
Path: path,
Rule: context.Rule,
Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ go 1.21
require (
github.com/alecthomas/chroma v0.10.0
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d
github.com/dop251/goja_nodejs v0.0.0-20231022114343-5c1f9037c9ab
github.com/dop251/goja_nodejs v0.0.0-20231122114759-e84d9a924c5c
github.com/dustin/go-humanize v1.0.1
github.com/ghodss/yaml v1.0.0
github.com/gizak/termui/v3 v3.1.0
github.com/json-iterator/go v1.1.12
github.com/mitchellh/mapstructure v1.5.0
github.com/pb33f/libopenapi v0.13.11
github.com/pb33f/libopenapi-validator v0.0.28
github.com/pterm/pterm v0.12.70
github.com/pb33f/libopenapi v0.13.14
github.com/pb33f/libopenapi-validator v0.0.30
github.com/pterm/pterm v0.12.71
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
Expand Down Expand Up @@ -58,8 +58,8 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/term v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Loading
Loading