Skip to content

Commit a248d48

Browse files
committed
dockerfile: add syntax to skip a check
Adds a syntax to skip a check by adding a comment above the area where the linter rule is triggered. Signed-off-by: Jonathan A. Sternberg <[email protected]>
1 parent 202e28f commit a248d48

File tree

9 files changed

+188
-33
lines changed

9 files changed

+188
-33
lines changed

frontend/dockerfile/dockerfile2llb/convert.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func ListTargets(ctx context.Context, dt []byte) (*targets.List, error) {
177177
for i, s := range stages {
178178
t := targets.Target{
179179
Name: s.Name,
180-
Description: s.Comment,
180+
Description: s.DocComment,
181181
Default: i == len(stages)-1,
182182
Base: s.BaseName,
183183
Platform: s.Platform,
@@ -296,6 +296,8 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS
296296

297297
// set base state for every image
298298
for i, st := range stages {
299+
lint := lint.WithMergedConfigFromComments(st.Comments)
300+
299301
nameMatch, err := shlex.ProcessWordWithMatches(st.BaseName, globalArgs)
300302
argKeys := unusedFromArgsCheckKeys(globalArgs, outline.allArgs)
301303
reportUnusedFromArgs(argKeys, nameMatch.Unmatched, st.Location, lint)
@@ -914,6 +916,8 @@ func (e *envsFromState) Keys() []string {
914916
}
915917

916918
func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
919+
opt.lint = opt.lint.WithMergedConfigFromComments(cmd.Comments())
920+
917921
d.cmdIsOnBuild = cmd.isOnBuild
918922
var err error
919923
// ARG command value could be ignored, so defer handling the expansion error

frontend/dockerfile/dockerfile2llb/outline.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (ds *dispatchState) args(visited map[string]struct{}) []outline.Arg {
7575
args = append(args, outline.Arg{
7676
Name: a.definition.Key,
7777
Value: a.value,
78-
Description: a.definition.Comment,
78+
Description: a.definition.DocComment,
7979
Location: toSourceLocation(a.location),
8080
})
8181
visited[k] = struct{}{}
@@ -153,7 +153,7 @@ func (ds *dispatchState) Outline(dt []byte) outline.Outline {
153153

154154
out := outline.Outline{
155155
Name: ds.stage.Name,
156-
Description: ds.stage.Comment,
156+
Description: ds.stage.DocComment,
157157
Sources: [][]byte{dt},
158158
Args: args,
159159
Secrets: secrets,

frontend/dockerfile/dockerfile_lint_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ ENV apikey=bar sunflower=foo
229229
ENV git_key=
230230
ENV PUBLIC_KEY=
231231
ARG public_token
232+
# check=skip=SecretsUsedInArgOrEnv // allow secret in environment
233+
ENV password=bar
234+
# check=skip=SecretsUsedInArgOrEnv // allow secret in arg
235+
ARG password
236+
# check=skip=all // is local to only this instruction
237+
ENV alternate_password=bar
238+
# check=skip=all // is local to only this instruction
239+
ARG alternate_password
232240
`)
233241
checkLinterWarnings(t, sb, &lintTestParams{
234242
Dockerfile: dockerfile,
@@ -830,6 +838,27 @@ MAINTAINER [email protected]
830838
dockerfile = []byte(`
831839
FROM scratch
832840
LABEL org.opencontainers.image.authors="[email protected]"
841+
`)
842+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
843+
844+
dockerfile = []byte(`
845+
FROM scratch
846+
# check=skip=JSONArgsRecommended
847+
CMD mycommand
848+
`)
849+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
850+
851+
dockerfile = []byte(`
852+
FROM scratch
853+
# check=skip=JSONArgsRecommended
854+
ENTRYPOINT mycommand
855+
`)
856+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
857+
858+
dockerfile = []byte(`
859+
FROM scratch
860+
# check=skip=MaintainerDeprecated
861+
833862
`)
834863
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
835864
}
@@ -1005,6 +1034,13 @@ WORKDIR /app
10051034
10061035
FROM a AS b
10071036
WORKDIR subdir/
1037+
`)
1038+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
1039+
1040+
dockerfile = []byte(`
1041+
FROM scratch
1042+
# check=skip=WorkdirRelativePath
1043+
WORKDIR app/
10081044
`)
10091045
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
10101046
}
@@ -1239,6 +1275,15 @@ FROM a AS c
12391275
},
12401276
},
12411277
})
1278+
1279+
dockerfile = []byte(`
1280+
FROM scratch
1281+
# check=skip=LegacyKeyValueFormat
1282+
ENV testkey value
1283+
# check=skip=LegacyKeyValueFormat
1284+
LABEL key value
1285+
`)
1286+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
12421287
}
12431288

12441289
func testRedundantTargetPlatform(t *testing.T, sb integration.Sandbox) {
@@ -1275,6 +1320,12 @@ FROM --platform=${TARGETPLATFORM} scratch
12751320
},
12761321
},
12771322
})
1323+
1324+
dockerfile = []byte(`
1325+
# check=skip=RedundantTargetPlatform
1326+
FROM --platform=$TARGETPLATFORM scratch
1327+
`)
1328+
checkLinterWarnings(t, sb, &lintTestParams{Dockerfile: dockerfile})
12781329
}
12791330

12801331
func testInvalidDefaultArgInFrom(t *testing.T, sb integration.Sandbox) {

frontend/dockerfile/dockerfile_outline_test.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ RUN true
5454
5555
FROM scratch AS third
5656
ARG ABC=a
57+
# check=skip=all
58+
ARG password
59+
# alternate_password will show up in the outline
60+
# check=skip=all
61+
ARG alternate_password
5762
5863
# target defines build target
5964
FROM third AS target
@@ -81,6 +86,11 @@ RUN exit 0
8186
8287
FROM nanoserver AS third
8388
ARG ABC=a
89+
# check=skip=all
90+
ARG password
91+
# alternate_password will show up in the outline
92+
# check=skip=all
93+
ARG alternate_password
8494
8595
# target defines build target
8696
FROM third AS target
@@ -125,7 +135,7 @@ FROM second
125135
require.Equal(t, 1, len(outline.Sources))
126136
require.Equal(t, dockerfile, outline.Sources[0])
127137

128-
require.Equal(t, 5, len(outline.Args))
138+
require.Equal(t, 7, len(outline.Args))
129139

130140
arg := outline.Args[0]
131141
require.Equal(t, "inherited", arg.Name)
@@ -155,6 +165,18 @@ FROM second
155165
require.Equal(t, "ABC", arg.Name)
156166
require.Equal(t, "a", arg.Value)
157167

168+
arg = outline.Args[5]
169+
require.Equal(t, "password", arg.Name)
170+
require.Equal(t, "", arg.Value)
171+
require.Equal(t, "", arg.Description)
172+
require.Equal(t, int32(22), arg.Location.Ranges[0].Start.Line)
173+
174+
arg = outline.Args[6]
175+
require.Equal(t, "alternate_password", arg.Name)
176+
require.Equal(t, "", arg.Value)
177+
require.Equal(t, "will show up in the outline", arg.Description)
178+
require.Equal(t, int32(25), arg.Location.Ranges[0].Start.Line)
179+
158180
called = true
159181
return nil, nil
160182
}

frontend/dockerfile/instructions/commands.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ func (kvp *KeyValuePair) String() string {
2424

2525
// KeyValuePairOptional is identical to KeyValuePair, but allows for optional values.
2626
type KeyValuePairOptional struct {
27-
Key string
28-
Value *string
29-
Comment string
27+
Key string
28+
Value *string
29+
DocComment string
3030
}
3131

3232
func (kvpo *KeyValuePairOptional) String() string {
@@ -49,6 +49,7 @@ func (kvpo *KeyValuePairOptional) ValueString() string {
4949
type Command interface {
5050
Name() string
5151
Location() []parser.Range
52+
Comments() []string
5253
}
5354

5455
// KeyValuePairs is a slice of KeyValuePair
@@ -59,6 +60,7 @@ type withNameAndCode struct {
5960
code string
6061
name string
6162
location []parser.Range
63+
comments []string
6264
}
6365

6466
func (c *withNameAndCode) String() string {
@@ -75,8 +77,17 @@ func (c *withNameAndCode) Location() []parser.Range {
7577
return c.location
7678
}
7779

80+
func (c *withNameAndCode) Comments() []string {
81+
return c.comments
82+
}
83+
7884
func newWithNameAndCode(req parseRequest) withNameAndCode {
79-
return withNameAndCode{code: strings.TrimSpace(req.original), name: req.command, location: req.location}
85+
return withNameAndCode{
86+
code: strings.TrimSpace(req.original),
87+
name: req.command,
88+
location: req.location,
89+
comments: req.comments,
90+
}
8091
}
8192

8293
// SingleWordExpander is a provider for variable expansion where a single word
@@ -511,10 +522,11 @@ type Stage struct {
511522
BaseName string // name of the base stage or source
512523
Platform string // platform of base source to use
513524

514-
Comment string // doc-comment directly above the stage
525+
DocComment string // doc-comment directly above the stage
515526

516527
SourceCode string // contents of the defining FROM command
517528
Location []parser.Range // location of the defining FROM command
529+
Comments []string
518530
}
519531

520532
// AddCommand appends a command to the stage.

frontend/dockerfile/instructions/parse.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ func ParseInstruction(node *parser.Node) (v any, err error) {
7272

7373
// ParseInstruction converts an AST to a typed instruction (either a command or a build stage beginning when encountering a `FROM` statement)
7474
func ParseInstructionWithLinter(node *parser.Node, lint *linter.Linter) (v any, err error) {
75+
lint = lint.WithMergedConfigFromComments(node.PrevComment)
76+
7577
defer func() {
7678
if err != nil {
7779
err = parser.WithLocation(err, node.Location())
@@ -424,7 +426,8 @@ func parseFrom(req parseRequest) (*Stage, error) {
424426
Commands: []Command{},
425427
Platform: flPlatform.Value,
426428
Location: req.location,
427-
Comment: getComment(req.comments, stageName),
429+
Comments: req.comments,
430+
DocComment: getDocComment(req.comments, stageName),
428431
}, nil
429432
}
430433

@@ -775,7 +778,7 @@ func parseArg(req parseRequest) (*ArgCommand, error) {
775778
} else {
776779
kvpo.Key = arg
777780
}
778-
kvpo.Comment = getComment(req.comments, kvpo.Key)
781+
kvpo.DocComment = getDocComment(req.comments, kvpo.Key)
779782
pairs[i] = kvpo
780783
}
781784

@@ -831,7 +834,7 @@ func errTooManyArguments(command string) error {
831834
return errors.Errorf("Bad input to %s, too many arguments", command)
832835
}
833836

834-
func getComment(comments []string, name string) string {
837+
func getDocComment(comments []string, name string) string {
835838
if name == "" {
836839
return ""
837840
}

frontend/dockerfile/instructions/parse_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,18 +193,18 @@ ARG bar baz=123
193193
stages, meta, err := Parse(ast.AST, nil)
194194
require.NoError(t, err)
195195

196-
require.Equal(t, "defines first stage", stages[0].Comment)
196+
require.Equal(t, "defines first stage", stages[0].DocComment)
197197
require.Equal(t, "foo", meta[0].Args[0].Key)
198-
require.Equal(t, "sets foo", meta[0].Args[0].Comment)
198+
require.Equal(t, "sets foo", meta[0].Args[0].DocComment)
199199

200200
st := stages[0]
201201

202202
require.Equal(t, "foo", st.Commands[0].(*ArgCommand).Args[0].Key)
203-
require.Equal(t, "", st.Commands[0].(*ArgCommand).Args[0].Comment)
203+
require.Equal(t, "", st.Commands[0].(*ArgCommand).Args[0].DocComment)
204204
require.Equal(t, "bar", st.Commands[1].(*ArgCommand).Args[0].Key)
205-
require.Equal(t, "defines bar", st.Commands[1].(*ArgCommand).Args[0].Comment)
205+
require.Equal(t, "defines bar", st.Commands[1].(*ArgCommand).Args[0].DocComment)
206206
require.Equal(t, "baz", st.Commands[1].(*ArgCommand).Args[1].Key)
207-
require.Equal(t, "is something else", st.Commands[1].(*ArgCommand).Args[1].Comment)
207+
require.Equal(t, "is something else", st.Commands[1].(*ArgCommand).Args[1].DocComment)
208208
}
209209

210210
func TestErrorCases(t *testing.T) {

0 commit comments

Comments
 (0)