Skip to content

Commit 7c26ce7

Browse files
committed
Preserve instruction flags for not_pinned_digest code actions
When executing a code action for pinning an image, we should make sure we rewrite the FROM instruction while preserving any flags that have been set. Signed-off-by: Remy Suen <[email protected]>
1 parent b68c280 commit 7c26ce7

File tree

5 files changed

+106
-22
lines changed

5 files changed

+106
-22
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ All notable changes to the Docker Language Server will be documented in this fil
1010

1111
### Fixed
1212

13+
- Dockerfile
14+
- textDocument/codeAction
15+
- preserve instruction flags when fixing a `not_pinned_digest` diagnostic ([#123](https://github.com/docker/docker-language-server/issues/123))
1316
- Compose
1417
- textDocument/completion
1518
- resolved a spacing offset issue with object or array completions ([#115](https://github.com/docker/docker-language-server/issues/115))

internal/bake/hcl/diagnosticsCollector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ func (c *BakeHCLDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder
152152
for _, diagnostic := range imageDiagnostics {
153153
if diagnostic.Kind == "critical_high_vulnerabilities" || diagnostic.Kind == "vulnerabilities" {
154154
rng := templateExpr.SrcRange
155-
diagnostics = append(diagnostics, scout.ConvertDiagnostic(diagnostic, nil, source, createProtocolRange(rng, true), nil))
155+
diagnostics = append(diagnostics, scout.ConvertDiagnostic(diagnostic, source, createProtocolRange(rng, true), nil))
156156
break
157157
}
158158
}

internal/scout/service.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/docker-language-server/internal/pkg/document"
1010
"github.com/docker/docker-language-server/internal/pkg/lsp/textdocument"
1111
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
12+
"github.com/docker/docker-language-server/internal/types"
1213
)
1314

1415
type Service interface {
@@ -69,14 +70,31 @@ func (s *ServiceImpl) CalculateDiagnostics(ctx context.Context, source string, d
6970
resp, err := s.manager.Get(&ScoutImageKey{Image: child.Next.Value})
7071
if err == nil {
7172
next := child.Next
72-
words := []string{child.Value}
73+
prefix := []string{child.Value}
74+
prefix = append(prefix, child.Flags...)
75+
suffix := []string{}
76+
next = next.Next
7377
for next != nil {
74-
words = append(words, next.Value)
78+
suffix = append(suffix, next.Value)
7579
next = next.Next
7680
}
7781

7882
for _, diagnostic := range resp.Diagnostics {
79-
lspDiagnostic := ConvertDiagnostic(diagnostic, words, source, protocol.Range{
83+
namedEdits := []types.NamedEdit{}
84+
for _, edit := range resp.Edits {
85+
if diagnostic.Kind == edit.Diagnostic {
86+
content := []string{}
87+
content = append(content, prefix...)
88+
content = append(content, edit.Edit)
89+
content = append(content, suffix...)
90+
namedEdits = append(namedEdits, types.NamedEdit{
91+
Title: edit.Title,
92+
Edit: strings.Join(content, " "),
93+
})
94+
}
95+
}
96+
97+
lspDiagnostic := ConvertDiagnostic(diagnostic, source, protocol.Range{
8098
Start: protocol.Position{
8199
Line: uint32(child.StartLine - 1),
82100
Character: 0,
@@ -85,7 +103,7 @@ func (s *ServiceImpl) CalculateDiagnostics(ctx context.Context, source string, d
85103
Line: uint32(child.EndLine - 1),
86104
Character: uint32(len(lines[child.StartLine-1])),
87105
},
88-
}, resp.Edits)
106+
}, namedEdits)
89107
lspDiagnostics = append(lspDiagnostics, lspDiagnostic)
90108
}
91109
continue

internal/scout/service_test.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ func TestCalculateDiagnostics(t *testing.T) {
3434
End: protocol.Position{Line: 0, Character: 18},
3535
},
3636
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityHint),
37+
Data: []types.NamedEdit{
38+
{
39+
Title: "Pin the base image digest",
40+
Edit: "FROM alpine:3.16.1@sha256:7580ece7963bfa863801466c0a488f11c86f85d9988051a9f9c68cb27f6b7872",
41+
},
42+
},
3743
},
3844
{
3945
Message: "The image contains 1 critical and 3 high vulnerabilities",
@@ -55,6 +61,76 @@ func TestCalculateDiagnostics(t *testing.T) {
5561
End: protocol.Position{Line: 0, Character: 18},
5662
},
5763
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityInformation),
64+
Data: []types.NamedEdit{
65+
{
66+
Title: "Update image to preferred tag (3.21.3)",
67+
Edit: "FROM alpine:3.21.3",
68+
},
69+
{
70+
Title: "Update image OS minor version (3.20.6)",
71+
Edit: "FROM alpine:3.20.6",
72+
},
73+
{
74+
Title: "Update image OS minor version (3.18.12)",
75+
Edit: "FROM alpine:3.18.12",
76+
},
77+
},
78+
},
79+
},
80+
},
81+
{
82+
name: "outdated alpine:3.16.1 with --platform flag",
83+
content: "FROM --platform=$BUILDPLATFORM alpine:3.16.1",
84+
diagnostics: []protocol.Diagnostic{
85+
{
86+
Message: "The image can be pinned to a digest",
87+
Source: types.CreateStringPointer("scout-testing-source"),
88+
Range: protocol.Range{
89+
Start: protocol.Position{Line: 0, Character: 0},
90+
End: protocol.Position{Line: 0, Character: 44},
91+
},
92+
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityHint),
93+
Data: []types.NamedEdit{
94+
{
95+
Title: "Pin the base image digest",
96+
Edit: "FROM --platform=$BUILDPLATFORM alpine:3.16.1@sha256:7580ece7963bfa863801466c0a488f11c86f85d9988051a9f9c68cb27f6b7872",
97+
},
98+
},
99+
},
100+
{
101+
Message: "The image contains 1 critical and 3 high vulnerabilities",
102+
Source: types.CreateStringPointer("scout-testing-source"),
103+
CodeDescription: &protocol.CodeDescription{
104+
HRef: "https://hub.docker.com/layers/library/alpine/3.16.1/images/sha256-9b2a28eb47540823042a2ba401386845089bb7b62a9637d55816132c4c3c36eb",
105+
},
106+
Range: protocol.Range{
107+
Start: protocol.Position{Line: 0, Character: 0},
108+
End: protocol.Position{Line: 0, Character: 44},
109+
},
110+
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityWarning),
111+
},
112+
{
113+
Message: "Tag recommendations available",
114+
Source: types.CreateStringPointer("scout-testing-source"),
115+
Range: protocol.Range{
116+
Start: protocol.Position{Line: 0, Character: 0},
117+
End: protocol.Position{Line: 0, Character: 44},
118+
},
119+
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityInformation),
120+
Data: []types.NamedEdit{
121+
{
122+
Title: "Update image to preferred tag (3.21.3)",
123+
Edit: "FROM --platform=$BUILDPLATFORM alpine:3.21.3",
124+
},
125+
{
126+
Title: "Update image OS minor version (3.20.6)",
127+
Edit: "FROM --platform=$BUILDPLATFORM alpine:3.20.6",
128+
},
129+
{
130+
Title: "Update image OS minor version (3.18.12)",
131+
Edit: "FROM --platform=$BUILDPLATFORM alpine:3.18.12",
132+
},
133+
},
58134
},
59135
},
60136
},
@@ -116,6 +192,7 @@ func TestCalculateDiagnostics(t *testing.T) {
116192
require.Equal(t, expectedDiagnostic.Severity, diagnostic.Severity)
117193
require.Equal(t, expectedDiagnostic.Source, diagnostic.Source)
118194
require.Equal(t, expectedDiagnostic.CodeDescription, diagnostic.CodeDescription)
195+
require.Equal(t, expectedDiagnostic.Data, diagnostic.Data)
119196
found = true
120197
break
121198
}

internal/scout/types.go

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package scout
22

33
import (
4-
"strings"
5-
64
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
75
"github.com/docker/docker-language-server/internal/types"
86
)
@@ -63,7 +61,7 @@ func (k *ScoutImageKey) CacheKey() string {
6361
return k.Image
6462
}
6563

66-
func ConvertDiagnostic(diagnostic Diagnostic, words []string, source string, rng protocol.Range, edits []Edit) protocol.Diagnostic {
64+
func ConvertDiagnostic(diagnostic Diagnostic, source string, rng protocol.Range, edits []types.NamedEdit) protocol.Diagnostic {
6765
lspDiagnostic := protocol.Diagnostic{}
6866
lspDiagnostic.Code = &protocol.IntegerOrString{Value: diagnostic.Kind}
6967
lspDiagnostic.Message = diagnostic.Message
@@ -86,20 +84,8 @@ func ConvertDiagnostic(diagnostic Diagnostic, words []string, source string, rng
8684
lspDiagnostic.Severity = types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityHint)
8785
}
8886

89-
includedEdits := []types.NamedEdit{}
90-
for _, edit := range edits {
91-
if lspDiagnostic.Code.Value == edit.Diagnostic {
92-
words[1] = edit.Edit
93-
edit.Edit = strings.Join(words, " ")
94-
includedEdits = append(includedEdits, types.NamedEdit{
95-
Title: edit.Title,
96-
Edit: edit.Edit,
97-
})
98-
}
99-
}
100-
101-
if len(includedEdits) > 0 {
102-
lspDiagnostic.Data = includedEdits
87+
if len(edits) > 0 {
88+
lspDiagnostic.Data = edits
10389
}
10490
return lspDiagnostic
10591
}

0 commit comments

Comments
 (0)