mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 17:37:46 +08:00
153 lines
3.3 KiB
Go
153 lines
3.3 KiB
Go
package linter
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type Config struct {
|
|
Warn LintWarnFunc
|
|
SkipRules []string
|
|
SkipAll bool
|
|
ReturnAsError bool
|
|
}
|
|
|
|
type Linter struct {
|
|
SkippedRules map[string]struct{}
|
|
CalledRules []string
|
|
SkipAll bool
|
|
ReturnAsError bool
|
|
Warn LintWarnFunc
|
|
}
|
|
|
|
func New(config *Config) *Linter {
|
|
toret := &Linter{
|
|
SkippedRules: map[string]struct{}{},
|
|
CalledRules: []string{},
|
|
Warn: config.Warn,
|
|
}
|
|
toret.SkipAll = config.SkipAll
|
|
toret.ReturnAsError = config.ReturnAsError
|
|
for _, rule := range config.SkipRules {
|
|
toret.SkippedRules[rule] = struct{}{}
|
|
}
|
|
return toret
|
|
}
|
|
|
|
func (lc *Linter) Run(rule LinterRuleI, location []parser.Range, txt ...string) {
|
|
if lc == nil || lc.Warn == nil || lc.SkipAll || rule.IsDeprecated() {
|
|
return
|
|
}
|
|
rulename := rule.RuleName()
|
|
if _, ok := lc.SkippedRules[rulename]; ok {
|
|
return
|
|
}
|
|
lc.CalledRules = append(lc.CalledRules, rulename)
|
|
rule.Run(lc.Warn, location, txt...)
|
|
}
|
|
|
|
func (lc *Linter) Error() error {
|
|
if lc == nil || !lc.ReturnAsError {
|
|
return nil
|
|
}
|
|
if len(lc.CalledRules) == 0 {
|
|
return nil
|
|
}
|
|
var rules []string
|
|
uniqueRules := map[string]struct{}{}
|
|
for _, r := range lc.CalledRules {
|
|
uniqueRules[r] = struct{}{}
|
|
}
|
|
for r := range uniqueRules {
|
|
rules = append(rules, r)
|
|
}
|
|
return errors.Errorf("lint violation found for rules: %s", strings.Join(rules, ", "))
|
|
}
|
|
|
|
type LinterRuleI interface {
|
|
RuleName() string
|
|
Run(warn LintWarnFunc, location []parser.Range, txt ...string)
|
|
IsDeprecated() bool
|
|
}
|
|
|
|
type LinterRule[F any] struct {
|
|
Name string
|
|
Description string
|
|
Deprecated bool
|
|
URL string
|
|
Format F
|
|
}
|
|
|
|
func (rule *LinterRule[F]) RuleName() string {
|
|
return rule.Name
|
|
}
|
|
|
|
func (rule *LinterRule[F]) Run(warn LintWarnFunc, location []parser.Range, txt ...string) {
|
|
if len(txt) == 0 {
|
|
txt = []string{rule.Description}
|
|
}
|
|
short := strings.Join(txt, " ")
|
|
warn(rule.Name, rule.Description, rule.URL, short, location)
|
|
}
|
|
|
|
func (rule *LinterRule[F]) IsDeprecated() bool {
|
|
return rule.Deprecated
|
|
}
|
|
|
|
func LintFormatShort(rulename, msg string, line int) string {
|
|
msg = fmt.Sprintf("%s: %s", rulename, msg)
|
|
if line > 0 {
|
|
msg = fmt.Sprintf("%s (line %d)", msg, line)
|
|
}
|
|
return msg
|
|
}
|
|
|
|
type LintWarnFunc func(rulename, description, url, fmtmsg string, location []parser.Range)
|
|
|
|
func ParseLintOptions(checkStr string) (*Config, error) {
|
|
checkStr = strings.TrimSpace(checkStr)
|
|
if checkStr == "" {
|
|
return &Config{}, nil
|
|
}
|
|
|
|
parts := strings.SplitN(checkStr, ";", 2)
|
|
var skipSet []string
|
|
var errorOnWarn, skipAll bool
|
|
for _, p := range parts {
|
|
k, v, ok := strings.Cut(p, "=")
|
|
if !ok {
|
|
return nil, errors.Errorf("invalid check option %q", p)
|
|
}
|
|
k = strings.TrimSpace(k)
|
|
switch k {
|
|
case "skip":
|
|
v = strings.TrimSpace(v)
|
|
if v == "all" {
|
|
skipAll = true
|
|
} else {
|
|
skipSet = strings.Split(v, ",")
|
|
for i, rule := range skipSet {
|
|
skipSet[i] = strings.TrimSpace(rule)
|
|
}
|
|
}
|
|
case "error":
|
|
v, err := strconv.ParseBool(strings.TrimSpace(v))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to parse check option %q", p)
|
|
}
|
|
errorOnWarn = v
|
|
default:
|
|
return nil, errors.Errorf("invalid check option %q", k)
|
|
}
|
|
}
|
|
return &Config{
|
|
SkipRules: skipSet,
|
|
SkipAll: skipAll,
|
|
ReturnAsError: errorOnWarn,
|
|
}, nil
|
|
}
|