Skip to content

Commit 94ee93a

Browse files
committed
dockerfile: add hint suggestions to UndefinedVar
Signed-off-by: Tonis Tiigi <[email protected]>
1 parent 555ea7d commit 94ee93a

File tree

3 files changed

+59
-12
lines changed

3 files changed

+59
-12
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
766766
}
767767

768768
newword, unmatched, err := opt.shlex.ProcessWord(word, env)
769-
reportUnmatchedVariables(cmd, d.buildArgs, unmatched, &opt)
769+
reportUnmatchedVariables(cmd, d.buildArgs, env, unmatched, &opt)
770770
return newword, err
771771
})
772772
if err != nil {
@@ -782,7 +782,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
782782
lex := shell.NewLex('\\')
783783
lex.SkipProcessQuotes = true
784784
newword, unmatched, err := lex.ProcessWord(word, env)
785-
reportUnmatchedVariables(cmd, d.buildArgs, unmatched, &opt)
785+
reportUnmatchedVariables(cmd, d.buildArgs, env, unmatched, &opt)
786786
return newword, err
787787
})
788788
if err != nil {
@@ -2103,19 +2103,25 @@ func validateStageNames(stages []instructions.Stage, warn linter.LintWarnFunc) {
21032103
}
21042104
}
21052105

2106-
func reportUnmatchedVariables(cmd instructions.Command, buildArgs []instructions.KeyValuePairOptional, unmatched map[string]struct{}, opt *dispatchOpt) {
2106+
func reportUnmatchedVariables(cmd instructions.Command, buildArgs []instructions.KeyValuePairOptional, env []string, unmatched map[string]struct{}, opt *dispatchOpt) {
2107+
for _, buildArg := range buildArgs {
2108+
delete(unmatched, buildArg.Key)
2109+
}
21072110
if len(unmatched) == 0 {
21082111
return
21092112
}
2110-
for _, buildArg := range buildArgs {
2111-
delete(unmatched, buildArg.Key)
2113+
options := metaArgsKeys(opt.metaArgs)
2114+
for _, envVar := range env {
2115+
key, _ := parseKeyValue(envVar)
2116+
options = append(options, key)
21122117
}
21132118
for cmdVar := range unmatched {
2114-
_, nonEnvOk := nonEnvArgs[cmdVar]
2115-
if !nonEnvOk {
2116-
msg := linter.RuleUndefinedVar.Format(cmdVar)
2117-
linter.RuleUndefinedVar.Run(opt.lintWarn, cmd.Location(), msg)
2119+
if _, nonEnvOk := nonEnvArgs[cmdVar]; nonEnvOk {
2120+
continue
21182121
}
2122+
match, _ := suggest.Search(cmdVar, options, true)
2123+
msg := linter.RuleUndefinedVar.Format(cmdVar, match)
2124+
linter.RuleUndefinedVar.Run(opt.lintWarn, cmd.Location(), msg)
21192125
}
21202126
}
21212127

frontend/dockerfile/dockerfile_lint_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,43 @@ RUN echo $foo
582582
},
583583
},
584584
})
585+
586+
dockerfile = []byte(`
587+
FROM alpine
588+
ARG DIR_BINARIES=binaries/
589+
ARG DIR_ASSETS=assets/
590+
ARG DIR_CONFIG=config/
591+
COPY $DIR_ASSET .
592+
`)
593+
checkLinterWarnings(t, sb, &lintTestParams{
594+
Dockerfile: dockerfile,
595+
Warnings: []expectedLintWarning{
596+
{
597+
RuleName: "UndefinedVar",
598+
Description: "Variables should be defined before their use",
599+
Detail: "Usage of undefined variable '$DIR_ASSET' (did you mean $DIR_ASSETS?)",
600+
Level: 1,
601+
Line: 6,
602+
},
603+
},
604+
})
605+
606+
dockerfile = []byte(`
607+
FROM alpine
608+
ENV PATH=$PAHT:/tmp/bin
609+
`)
610+
checkLinterWarnings(t, sb, &lintTestParams{
611+
Dockerfile: dockerfile,
612+
Warnings: []expectedLintWarning{
613+
{
614+
RuleName: "UndefinedVar",
615+
Description: "Variables should be defined before their use",
616+
Detail: "Usage of undefined variable '$PAHT' (did you mean $PATH?)",
617+
Level: 1,
618+
Line: 3,
619+
},
620+
},
621+
})
585622
}
586623

587624
func testMultipleInstructionsDisallowed(t *testing.T, sb integration.Sandbox) {

frontend/dockerfile/linter/ruleset.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,15 @@ var (
9595
return fmt.Sprintf("Usage of undefined variable '$%s'", arg)
9696
},
9797
}
98-
RuleUndefinedVar = LinterRule[func(string) string]{
98+
RuleUndefinedVar = LinterRule[func(string, string) string]{
9999
Name: "UndefinedVar",
100100
Description: "Variables should be defined before their use",
101-
Format: func(arg string) string {
102-
return fmt.Sprintf("Usage of undefined variable '$%s'", arg)
101+
Format: func(arg, suggest string) string {
102+
out := fmt.Sprintf("Usage of undefined variable '$%s'", arg)
103+
if suggest != "" {
104+
out += fmt.Sprintf(" (did you mean $%s?)", suggest)
105+
}
106+
return out
103107
},
104108
}
105109
RuleMultipleInstructionsDisallowed = LinterRule[func(instructionName string) string]{

0 commit comments

Comments
 (0)