Skip to content

Commit ff320da

Browse files
committed
use array for versions
Signed-off-by: Sylwester Piskozub <sylwesterpiskozub@gmail.com>
1 parent 483c2d3 commit ff320da

5 files changed

Lines changed: 64 additions & 54 deletions

File tree

pkg/attestation/crafter/materials/cyclonedxjson.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -166,33 +166,37 @@ func (i *CyclonedxJSONCrafter) extractMetadata(m *api.Attestation_Material, meta
166166
i.logger.Debug().Err(err).Msg("error extracting main component from sbom, skipping...")
167167
}
168168

169-
// Extract all tools from the array
170-
for idx, tool := range meta.Tools {
171-
m.Annotations[AnnotationToolIndexedName(idx)] = tool.Name
172-
m.Annotations[AnnotationToolIndexedVersion(idx)] = tool.Version
169+
// Extract all tools and set annotations
170+
var tools []Tool
171+
for _, tool := range meta.Tools {
172+
tools = append(tools, Tool{Name: tool.Name, Version: tool.Version})
173173
}
174+
SetToolsAnnotation(m, tools)
174175

175-
// Maintain backward compatibility - keep legacy non-indexed keys for the first tool
176-
if len(meta.Tools) > 0 {
177-
m.Annotations[AnnotationToolNameKey] = meta.Tools[0].Name
178-
m.Annotations[AnnotationToolVersionKey] = meta.Tools[0].Version
176+
// Maintain backward compatibility - keep legacy keys for the first tool
177+
if len(tools) > 0 {
178+
m.Annotations[AnnotationToolNameKey] = tools[0].Name
179+
m.Annotations[AnnotationToolVersionKey] = tools[0].Version
179180
}
181+
180182
case *cyclonedxMetadataV15:
181183
if err := i.extractMainComponent(m, &meta.Component); err != nil {
182184
i.logger.Debug().Err(err).Msg("error extracting main component from sbom, skipping...")
183185
}
184186

185-
// Extract all tools from the array
186-
for idx, tool := range meta.Tools.Components {
187-
m.Annotations[AnnotationToolIndexedName(idx)] = tool.Name
188-
m.Annotations[AnnotationToolIndexedVersion(idx)] = tool.Version
187+
// Extract all tools and set annotations
188+
var tools []Tool
189+
for _, tool := range meta.Tools.Components {
190+
tools = append(tools, Tool{Name: tool.Name, Version: tool.Version})
189191
}
192+
SetToolsAnnotation(m, tools)
190193

191-
// Maintain backward compatibility - keep legacy non-indexed keys for the first tool
192-
if len(meta.Tools.Components) > 0 {
193-
m.Annotations[AnnotationToolNameKey] = meta.Tools.Components[0].Name
194-
m.Annotations[AnnotationToolVersionKey] = meta.Tools.Components[0].Version
194+
// Maintain backward compatibility - keep legacy keys for the first tool
195+
if len(tools) > 0 {
196+
m.Annotations[AnnotationToolNameKey] = tools[0].Name
197+
m.Annotations[AnnotationToolVersionKey] = tools[0].Version
195198
}
199+
196200
default:
197201
i.logger.Debug().Msg("unknown metadata version")
198202
}

pkg/attestation/crafter/materials/cyclonedxjson_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,12 +165,9 @@ func TestCyclonedxJSONCraft(t *testing.T) {
165165
wantMainComponentKind: "application",
166166
wantMainComponentVersion: "1.0.0",
167167
annotations: map[string]string{
168-
"chainloop.material.tool.name": "Hub",
169-
"chainloop.material.tool.version": "2025.4.2",
170-
"chainloop.material.tool.0.name": "Hub",
171-
"chainloop.material.tool.0.version": "2025.4.2",
172-
"chainloop.material.tool.1.name": "cyclonedx-core-java",
173-
"chainloop.material.tool.1.version": "5.0.5",
168+
"chainloop.material.tool.name": "Hub",
169+
"chainloop.material.tool.version": "2025.4.2",
170+
"chainloop.material.tools": `["Hub@2025.4.2","cyclonedx-core-java@5.0.5"]`,
174171
},
175172
},
176173
}

pkg/attestation/crafter/materials/materials.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package materials
1717

1818
import (
1919
"context"
20+
"encoding/json"
2021
"errors"
2122
"fmt"
2223
"io"
@@ -36,15 +37,34 @@ import (
3637

3738
const AnnotationToolNameKey = "chainloop.material.tool.name"
3839
const AnnotationToolVersionKey = "chainloop.material.tool.version"
40+
const AnnotationToolsKey = "chainloop.material.tools"
3941

40-
// AnnotationToolIndexedName returns the annotation key for an indexed tool name
41-
func AnnotationToolIndexedName(idx int) string {
42-
return fmt.Sprintf("chainloop.material.tool.%d.name", idx)
42+
// Tool represents a tool with name and version
43+
type Tool struct {
44+
Name string
45+
Version string
4346
}
4447

45-
// AnnotationToolIndexedVersion returns the annotation key for an indexed tool version
46-
func AnnotationToolIndexedVersion(idx int) string {
47-
return fmt.Sprintf("chainloop.material.tool.%d.version", idx)
48+
// SetToolsAnnotations sets the tools annotation as a JSON array in "name@version" format
49+
func SetToolsAnnotation(m *api.Attestation_Material, tools []Tool) {
50+
if len(tools) == 0 {
51+
return
52+
}
53+
54+
// Build array of "name@version" strings
55+
var toolStrings []string
56+
for _, tool := range tools {
57+
toolStr := tool.Name
58+
if tool.Version != "" {
59+
toolStr = fmt.Sprintf("%s@%s", tool.Name, tool.Version)
60+
}
61+
toolStrings = append(toolStrings, toolStr)
62+
}
63+
64+
// Marshal to JSON array
65+
if toolsJSON, err := json.Marshal(toolStrings); err == nil {
66+
m.Annotations[AnnotationToolsKey] = string(toolsJSON)
67+
}
4868
}
4969

5070
var (

pkg/attestation/crafter/materials/spdxjson.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (i *SPDXJSONCrafter) injectAnnotations(m *api.Attestation_Material, doc *sp
7474
m.Annotations = make(map[string]string)
7575

7676
// Extract all tools from the creators array
77-
toolIdx := 0
77+
var tools []Tool
7878
for _, c := range doc.CreationInfo.Creators {
7979
if c.CreatorType == "Tool" {
8080
// try to extract the tool name and version
@@ -83,22 +83,15 @@ func (i *SPDXJSONCrafter) injectAnnotations(m *api.Attestation_Material, doc *sp
8383
if parts := strings.SplitN(c.Creator, "-", 2); len(parts) == 2 {
8484
name, version = parts[0], parts[1]
8585
}
86+
tools = append(tools, Tool{Name: name, Version: version})
87+
}
88+
}
8689

87-
// Store indexed annotations for all tools
88-
m.Annotations[AnnotationToolIndexedName(toolIdx)] = name
89-
if version != "" {
90-
m.Annotations[AnnotationToolIndexedVersion(toolIdx)] = version
91-
}
92-
93-
// Maintain backward compatibility - legacy keys for first tool
94-
if toolIdx == 0 {
95-
m.Annotations[AnnotationToolNameKey] = name
96-
if version != "" {
97-
m.Annotations[AnnotationToolVersionKey] = version
98-
}
99-
}
90+
SetToolsAnnotation(m, tools)
10091

101-
toolIdx++
102-
}
92+
// Maintain backward compatibility - keep legacy keys for the first tool
93+
if len(tools) > 0 {
94+
m.Annotations[AnnotationToolNameKey] = tools[0].Name
95+
m.Annotations[AnnotationToolVersionKey] = tools[0].Version
10396
}
10497
}

pkg/attestation/crafter/materials/spdxjson_test.go

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,9 @@ func TestSPDXJSONCraft(t *testing.T) {
9494
wantDigest: "sha256:fe2636fb6c698a29a315278b762b2000efd5959afe776ee4f79f1ed523365a33",
9595
wantFilename: "sbom-spdx.json",
9696
annotations: map[string]string{
97-
"chainloop.material.tool.name": "syft",
98-
"chainloop.material.tool.version": "0.73.0",
99-
"chainloop.material.tool.0.name": "syft",
100-
"chainloop.material.tool.0.version": "0.73.0",
97+
"chainloop.material.tool.name": "syft",
98+
"chainloop.material.tool.version": "0.73.0",
99+
"chainloop.material.tools": `["syft@0.73.0"]`,
101100
},
102101
},
103102
{
@@ -106,12 +105,9 @@ func TestSPDXJSONCraft(t *testing.T) {
106105
wantDigest: "sha256:c1a61566c7c0224ac02ad9cd21d90234e5a71de26971e33df2205c1a2eb319fc",
107106
wantFilename: "sbom-spdx-multiple-tools.json",
108107
annotations: map[string]string{
109-
"chainloop.material.tool.name": "spdxgen",
110-
"chainloop.material.tool.version": "1.0.0",
111-
"chainloop.material.tool.0.name": "spdxgen",
112-
"chainloop.material.tool.0.version": "1.0.0",
113-
"chainloop.material.tool.1.name": "scanner",
114-
"chainloop.material.tool.1.version": "2.1.5",
108+
"chainloop.material.tool.name": "spdxgen",
109+
"chainloop.material.tool.version": "1.0.0",
110+
"chainloop.material.tools": `["spdxgen@1.0.0","scanner@2.1.5"]`,
115111
},
116112
},
117113
}

0 commit comments

Comments
 (0)