Skip to content

Commit

Permalink
dockerfile: add hint suggestions to UndeclaredArgInFrom
Browse files Browse the repository at this point in the history
Signed-off-by: Tonis Tiigi <[email protected]>
  • Loading branch information
tonistiigi committed May 14, 2024
1 parent 05782a7 commit 555ea7d
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 25 deletions.
11 changes: 6 additions & 5 deletions frontend/dockerfile/dockerfile2llb/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
// set base state for every image
for i, st := range stages {
nameMatch, err := shlex.ProcessWordWithMatches(st.BaseName, metaArgsToMap(optMetaArgs))
reportUnusedFromArgs(nameMatch.Unmatched, st.Location, opt.Warn)
reportUnusedFromArgs(metaArgsKeys(optMetaArgs), nameMatch.Unmatched, st.Location, opt.Warn)
used := nameMatch.Matched

if err != nil {
Expand All @@ -285,7 +285,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS

if v := st.Platform; v != "" {
platMatch, err := shlex.ProcessWordWithMatches(v, metaArgsToMap(optMetaArgs))
reportUnusedFromArgs(platMatch.Unmatched, st.Location, opt.Warn)
reportUnusedFromArgs(metaArgsKeys(optMetaArgs), platMatch.Unmatched, st.Location, opt.Warn)

if err != nil {
return nil, parser.WithLocation(errors.Wrapf(err, "failed to process arguments for platform %s", platMatch.Result), st.Location)
Expand Down Expand Up @@ -2169,9 +2169,10 @@ func toPBLocation(sourceIndex int, location []parser.Range) pb.Location {
}
}

func reportUnusedFromArgs(unmatched map[string]struct{}, location []parser.Range, warn linter.LintWarnFunc) {
func reportUnusedFromArgs(values []string, unmatched map[string]struct{}, location []parser.Range, warn linter.LintWarnFunc) {
for arg := range unmatched {
msg := linter.RuleUndeclaredArgInFrom.Format(arg)
suggest, _ := suggest.Search(arg, values, true)
msg := linter.RuleUndeclaredArgInFrom.Format(arg, suggest)
linter.RuleUndeclaredArgInFrom.Run(warn, location, msg)
}
}
Expand Down Expand Up @@ -2199,7 +2200,7 @@ func validateUsedOnce(c instructions.Command, loc *instructionTracker, warn lint
func wrapSuggestAny(err error, keys map[string]struct{}, options []string) error {
for k := range keys {
var ok bool
err, ok = suggest.WrapErrorMaybe(err, k, options, true)
ok, err = suggest.WrapErrorMaybe(err, k, options, true)
if ok {
break
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/dockerfile/dockerfile_lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ COPY Dockerfile .
{
RuleName: "UndeclaredArgInFrom",
Description: "FROM command must use declared ARGs",
Detail: "FROM argument 'BULIDPLATFORM' is not declared",
Detail: "FROM argument 'BULIDPLATFORM' is not declared (did you mean BUILDPLATFORM?)",
Level: 1,
Line: 2,
},
Expand All @@ -484,7 +484,7 @@ COPY Dockerfile .
{
RuleName: "UndeclaredArgInFrom",
Description: "FROM command must use declared ARGs",
Detail: "FROM argument 'MYARCH' is not declared",
Detail: "FROM argument 'MYARCH' is not declared (did you mean MY_ARCH?)",
Level: 1,
Line: 4,
},
Expand Down
10 changes: 7 additions & 3 deletions frontend/dockerfile/linter/ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,15 @@ var (
return "Maintainer instruction is deprecated in favor of using label"
},
}
RuleUndeclaredArgInFrom = LinterRule[func(string) string]{
RuleUndeclaredArgInFrom = LinterRule[func(string, string) string]{
Name: "UndeclaredArgInFrom",
Description: "FROM command must use declared ARGs",
Format: func(baseArg string) string {
return fmt.Sprintf("FROM argument '%s' is not declared", baseArg)
Format: func(baseArg, suggest string) string {
out := fmt.Sprintf("FROM argument '%s' is not declared", baseArg)
if suggest != "" {
out += fmt.Sprintf(" (did you mean %s?)", suggest)
}
return out
},
}
RuleWorkdirRelativePath = LinterRule[func(workdir string) string]{
Expand Down
34 changes: 19 additions & 15 deletions util/suggest/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@ import (
"github.com/agext/levenshtein"
)

// WrapError wraps error with a suggestion for fixing it
func WrapError(err error, val string, options []string, caseSensitive bool) error {
err, _ = WrapErrorMaybe(err, val, options, caseSensitive)
return err
}

func WrapErrorMaybe(err error, val string, options []string, caseSensitive bool) (error, bool) {
if err == nil {
return nil, false
}
func Search(val string, options []string, caseSensitive bool) (string, bool) {
orig := val
if !caseSensitive {
val = strings.ToLower(val)
Expand All @@ -28,7 +19,7 @@ func WrapErrorMaybe(err error, val string, options []string, caseSensitive bool)
}
if val == opt {
// exact match means error was unrelated to the value
return err, false
return "", false
}
dist := levenshtein.Distance(val, opt, nil)
if dist < mindist {
Expand All @@ -40,15 +31,28 @@ func WrapErrorMaybe(err error, val string, options []string, caseSensitive bool)
mindist = dist
}
}
return match, match != ""
}

if match == "" {
return err, false
// WrapError wraps error with a suggestion for fixing it
func WrapError(err error, val string, options []string, caseSensitive bool) error {
_, err = WrapErrorMaybe(err, val, options, caseSensitive)
return err
}

func WrapErrorMaybe(err error, val string, options []string, caseSensitive bool) (bool, error) {
if err == nil {
return false, nil
}
match, ok := Search(val, options, caseSensitive)
if match == "" || !ok {
return false, err
}

return &suggestError{
return true, &suggestError{
err: err,
match: match,
}, true
}
}

type suggestError struct {
Expand Down

0 comments on commit 555ea7d

Please sign in to comment.