Skip to content

Commit b2a5117

Browse files
authored
feat(dependency-track): support global annotations in project name (#285)
Signed-off-by: Miguel Martinez Trivino <[email protected]>
1 parent d625c2f commit b2a5117

File tree

3 files changed

+55
-13
lines changed

3 files changed

+55
-13
lines changed

app/controlplane/plugins/core/dependency-track/v1/extension.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"strings"
2324
"text/template"
2425

2526
schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1"
@@ -182,7 +183,9 @@ func doExecute(ctx context.Context, req *sdk.ExecutionRequest, sbom *sdk.Execute
182183
return errors.New("invalid attachment configuration")
183184
}
184185

185-
projectName, err := resolveProjectName(attachmentConfig.ProjectName, sbom.Annotations)
186+
// Calculate the project name based on the template
187+
188+
projectName, err := resolveProjectName(attachmentConfig.ProjectName, req.Input.Attestation.Predicate.GetAnnotations(), sbom.Annotations)
186189
if err != nil {
187190
// If we can't find the annotation for example, we skip the SBOM
188191
l.Infow("msg", "failed to resolve project name, SKIPPING", "err", err, "materialName", sbom.Name)
@@ -227,18 +230,33 @@ func doExecute(ctx context.Context, req *sdk.ExecutionRequest, sbom *sdk.Execute
227230
}
228231

229232
type interpolationContext struct {
230-
Material *interpolationContextMaterial
233+
Material *annotations
234+
Attestation *annotations
231235
}
232-
type interpolationContextMaterial struct {
236+
type annotations struct {
233237
Annotations map[string]string
234238
}
235239

240+
// Make annotations keys case insensitive
241+
// that way you can define templates such as {{ material.annotations.myAnnotation }} or {{ material.annotations.MyAnnotation }} and they will both work
242+
func toCaseInsensitive(in map[string]string) map[string]string {
243+
for k, v := range in {
244+
in[strings.Title(k)] = v
245+
}
246+
247+
return in
248+
}
249+
236250
// Resolve the project name template.
237251
// We currently support the following template variables:
238-
// - material.annotations.<key>
252+
// - {{ .Attestation.Annotations.<key> }} for global annotations
253+
// - {{ .Material.Annotations.<key> }} for material annotations
239254
// For example, project-name => {{ material.annotations.my_annotation }}
240-
func resolveProjectName(projectNameTpl string, annotations map[string]string) (string, error) {
241-
data := interpolationContext{&interpolationContextMaterial{annotations}}
255+
func resolveProjectName(projectNameTpl string, attAnnotations, sbomAnnotations map[string]string) (string, error) {
256+
data := &interpolationContext{
257+
Material: &annotations{toCaseInsensitive(sbomAnnotations)},
258+
Attestation: &annotations{toCaseInsensitive(attAnnotations)},
259+
}
242260

243261
// The project name can contain template variables, useful to include annotations for example
244262
// We do fail if the key can't be found

app/controlplane/plugins/core/dependency-track/v1/extension_test.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ func TestResolveProjectName(t *testing.T) {
112112
projectName: "{{.Material.Annotations.Hello}}",
113113
want: "hola",
114114
},
115+
{
116+
name: "lower case",
117+
projectName: "{{.Material.Annotations.hello}}",
118+
want: "hola",
119+
},
115120
{
116121
name: "interpolated string",
117122
projectName: "{{.Material.Annotations.Hello}}-project",
@@ -124,21 +129,35 @@ func TestResolveProjectName(t *testing.T) {
124129
wantErr: true,
125130
},
126131
{
127-
name: "non-existing-case",
128-
projectName: "{{.Material.Annotations.hello}}",
129-
wantErr: true,
132+
name: "interpolated string global",
133+
projectName: "project-{{.Attestation.Annotations.version}}",
134+
want: "project-1.2.3",
130135
},
136+
{
137+
name: "interpolated combination global",
138+
projectName: "project-{{.Material.Annotations.Hello}}-{{.Attestation.Annotations.version}}",
139+
want: "project-hola-1.2.3",
140+
},
141+
{
142+
name: "uppercase global",
143+
projectName: "{{.Attestation.Annotations.Version}}",
144+
want: "1.2.3",
145+
},
146+
}
147+
148+
sbomAnnotation := map[string]string{
149+
"hello": "hola",
150+
"world": "mundo",
131151
}
132152

133-
data := map[string]string{
134-
"Hello": "hola",
135-
"World": "mundo",
153+
attAnnotation := map[string]string{
154+
"version": "1.2.3",
136155
}
137156

138157
for _, tc := range testCases {
139158
t.Run(tc.name, func(t *testing.T) {
140159
var err error
141-
got, err := resolveProjectName(tc.projectName, data)
160+
got, err := resolveProjectName(tc.projectName, attAnnotation, sbomAnnotation)
142161
if tc.wantErr {
143162
assert.Error(t, err)
144163
return

internal/attestation/renderer/chainloop/chainloop.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const builderIDFmt = "chainloop.dev/cli/%s@%s"
3535

3636
// NormalizablePredicate represents a common interface of how to extract materials and env vars
3737
type NormalizablePredicate interface {
38+
GetAnnotations() map[string]string
3839
GetEnvVars() map[string]string
3940
GetMaterials() []*NormalizedMaterial
4041
GetRunLink() string
@@ -195,3 +196,7 @@ func (p *ProvenancePredicateCommon) GetEnvVars() map[string]string {
195196
func (p *ProvenancePredicateCommon) GetRunLink() string {
196197
return p.RunnerURL
197198
}
199+
200+
func (p *ProvenancePredicateCommon) GetAnnotations() map[string]string {
201+
return p.Annotations
202+
}

0 commit comments

Comments
 (0)