Skip to content

Commit fd44eb2

Browse files
committed
Provide code actions to easily skip certain build checks
Signed-off-by: Remy Suen <remy.suen@docker.com>
1 parent 279eab8 commit fd44eb2

File tree

3 files changed

+223
-23
lines changed

3 files changed

+223
-23
lines changed

e2e-tests/publishDiagnostics_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ func testPublishDiagnostics(t *testing.T, initializeParams protocol.InitializePa
128128
"edit": "LABEL org.opencontainers.image.authors=\"x\"",
129129
"title": "Convert MAINTAINER to a org.opencontainers.image.authors LABEL",
130130
},
131+
map[string]any{
132+
"edit": "# check=skip=MaintainerDeprecated\n",
133+
"title": "Ignore this type of error with check=skip=MaintainerDeprecated",
134+
"range": map[string]any{
135+
"start": map[string]any{"line": float64(0), "character": float64(0)},
136+
"end": map[string]any{"line": float64(0), "character": float64(0)},
137+
},
138+
},
131139
},
132140
},
133141
},
@@ -150,6 +158,16 @@ func testPublishDiagnostics(t *testing.T, initializeParams protocol.InitializePa
150158
Start: protocol.Position{Line: 1, Character: 0},
151159
End: protocol.Position{Line: 1, Character: 6},
152160
},
161+
Data: []any{
162+
map[string]any{
163+
"edit": "# check=skip=JSONArgsRecommended\n",
164+
"title": "Ignore this type of error with check=skip=JSONArgsRecommended",
165+
"range": map[string]any{
166+
"start": map[string]any{"line": float64(0), "character": float64(0)},
167+
"end": map[string]any{"line": float64(0), "character": float64(0)},
168+
},
169+
},
170+
},
153171
},
154172
{
155173
Message: "The image contains 1 critical and 3 high vulnerabilities",
@@ -242,6 +260,16 @@ func testPublishDiagnostics(t *testing.T, initializeParams protocol.InitializePa
242260
Start: protocol.Position{Line: 0, Character: 0},
243261
End: protocol.Position{Line: 0, Character: 35},
244262
},
263+
Data: []any{
264+
map[string]any{
265+
"edit": "# check=skip=FromPlatformFlagConstDisallowed\n",
266+
"title": "Ignore this type of error with check=skip=FromPlatformFlagConstDisallowed",
267+
"range": map[string]any{
268+
"start": map[string]any{"line": float64(0), "character": float64(0)},
269+
"end": map[string]any{"line": float64(0), "character": float64(0)},
270+
},
271+
},
272+
},
245273
},
246274
},
247275
}, params)

internal/pkg/buildkit/service.go

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -93,39 +93,33 @@ func encloseWithQuotes(s string) string {
9393
return fmt.Sprintf(`"%v"`, s)
9494
}
9595

96-
func setDiagnosticData(diagnostic *protocol.Diagnostic, instruction *parser.Node, warning lint.Warning) {
96+
func createResolutionEdit(instruction *parser.Node, warning lint.Warning) *types.NamedEdit {
9797
if instruction != nil && instruction.StartLine == int(warning.Location.Ranges[0].Start.Line) && instruction.Next != nil {
9898
if warning.RuleName == "MaintainerDeprecated" {
99-
diagnostic.Data = []types.NamedEdit{
100-
{
101-
Title: "Convert MAINTAINER to a org.opencontainers.image.authors LABEL",
102-
Edit: fmt.Sprintf(`LABEL org.opencontainers.image.authors=%v`, encloseWithQuotes(instruction.Next.Value)),
103-
},
99+
return &types.NamedEdit{
100+
Title: "Convert MAINTAINER to a org.opencontainers.image.authors LABEL",
101+
Edit: fmt.Sprintf(`LABEL org.opencontainers.image.authors=%v`, encloseWithQuotes(instruction.Next.Value)),
104102
}
105103
} else if warning.RuleName == "StageNameCasing" {
106104
stageName := instruction.Next.Next.Next.Value
107105
lowercase := strings.ToLower(stageName)
108106
words := []string{instruction.Value, instruction.Next.Value, instruction.Next.Next.Value, lowercase}
109-
diagnostic.Data = []types.NamedEdit{
110-
{
111-
Title: fmt.Sprintf("Convert stage name (%v) to lowercase (%v)", stageName, lowercase),
112-
Edit: strings.Join(words, " "),
113-
},
107+
return &types.NamedEdit{
108+
Title: fmt.Sprintf("Convert stage name (%v) to lowercase (%v)", stageName, lowercase),
109+
Edit: strings.Join(words, " "),
114110
}
115111
} else if warning.RuleName == "RedundantTargetPlatform" {
116112
words := getWords(instruction)
117113
for i := range words {
118114
if words[i] == "--platform=$TARGETPLATFORM" {
119115
words = slices.Delete(words, i, i+1)
120-
diagnostic.Data = []types.NamedEdit{
121-
{
122-
Title: "Remove unnecessary --platform flag",
123-
Edit: strings.Join(words, " "),
124-
},
125-
}
126116
break
127117
}
128118
}
119+
return &types.NamedEdit{
120+
Title: "Remove unnecessary --platform flag",
121+
Edit: strings.Join(words, " "),
122+
}
129123
} else if warning.RuleName == "ConsistentInstructionCasing" {
130124
words := getWords(instruction)
131125
suggestion := strings.ToUpper(instruction.Value)
@@ -134,14 +128,13 @@ func setDiagnosticData(diagnostic *protocol.Diagnostic, instruction *parser.Node
134128
suggestion = strings.ToLower(suggestion)
135129
}
136130
words[0] = suggestion
137-
diagnostic.Data = []types.NamedEdit{
138-
{
139-
Title: fmt.Sprintf("Convert to %v", caseSuggestion),
140-
Edit: strings.Join(words, " "),
141-
},
131+
return &types.NamedEdit{
132+
Title: fmt.Sprintf("Convert to %v", caseSuggestion),
133+
Edit: strings.Join(words, " "),
142134
}
143135
}
144136
}
137+
return nil
145138
}
146139

147140
func convertToDiagnostics(source string, doc document.DockerfileDocument, lines []string, warnings []lint.Warning) []protocol.Diagnostic {
@@ -168,12 +161,71 @@ func convertToDiagnostics(source string, doc document.DockerfileDocument, lines
168161
diagnostic.Tags = []protocol.DiagnosticTag{protocol.DiagnosticTagDeprecated}
169162
}
170163
instruction := doc.Instruction(protocol.Position{Line: uint32(warning.Location.Ranges[0].Start.Line) - 1})
171-
setDiagnosticData(diagnostic, instruction, warning)
164+
ignoreEdit := createIgnoreEdit(warning.RuleName)
165+
resolutionEdit := createResolutionEdit(instruction, warning)
166+
if resolutionEdit == nil {
167+
if ignoreEdit != nil {
168+
diagnostic.Data = []types.NamedEdit{*ignoreEdit}
169+
}
170+
} else {
171+
diagnostic.Data = []types.NamedEdit{*resolutionEdit, *ignoreEdit}
172+
}
172173
diagnostics = append(diagnostics, *diagnostic)
173174
}
174175
return diagnostics
175176
}
176177

178+
func createIgnoreEdit(ruleName string) *types.NamedEdit {
179+
switch ruleName {
180+
case "ConsistentInstructionCasing":
181+
fallthrough
182+
case "CopyIgnoredFile":
183+
fallthrough
184+
case "DuplicateStageName":
185+
fallthrough
186+
case "FromAsCasing":
187+
fallthrough
188+
case "FromPlatformFlagConstDisallowed":
189+
fallthrough
190+
case "InvalidDefaultArgInFrom":
191+
fallthrough
192+
case "InvalidDefinitionDescription":
193+
fallthrough
194+
case "JSONArgsRecommended":
195+
fallthrough
196+
case "LegacyKeyValueFormat":
197+
fallthrough
198+
case "MaintainerDeprecated":
199+
fallthrough
200+
case "MultipleInstructionsDisallowed":
201+
fallthrough
202+
case "NoEmptyContinuation":
203+
fallthrough
204+
case "RedundantTargetPlatform":
205+
fallthrough
206+
case "ReservedStageName":
207+
fallthrough
208+
case "SecretsUsedInArgOrEnv":
209+
fallthrough
210+
case "StageNameCasing":
211+
fallthrough
212+
case "UndefinedArgInFrom":
213+
fallthrough
214+
case "UndefinedVar":
215+
fallthrough
216+
case "WorkdirRelativePath":
217+
return &types.NamedEdit{
218+
Title: fmt.Sprintf("Ignore this type of error with check=skip=%v", ruleName),
219+
Edit: fmt.Sprintf("# check=skip=%v\n", ruleName),
220+
Range: &protocol.Range{
221+
Start: protocol.Position{Line: 0, Character: 0},
222+
End: protocol.Position{Line: 0, Character: 0},
223+
},
224+
}
225+
}
226+
return nil
227+
}
228+
177229
func lintWithBuildKitBinary(contextPath, source string, doc document.DockerfileDocument, content string) ([]protocol.Diagnostic, error) {
178230
var buf bytes.Buffer
179231
cmd := exec.Command("docker", "buildx", "build", "--call=check,format=json", "-f-", contextPath)

0 commit comments

Comments
 (0)