Skip to content

Commit db4ac21

Browse files
chitrangpateltekton-robot
authored andcommitted
Add isBuildArtifact field to Artifacts
This PR adds isBuildArtifact field to Artifacts. This field will allow Tekton Chains to understand user's desire and appropriate add the artifact as a subject or a byProduct in the SLSA provenance.
1 parent a9b204e commit db4ac21

12 files changed

+156
-28
lines changed

docs/artifacts.md

+49
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,55 @@ spec:
155155

156156
It is recommended to use [purl format](https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst) for artifacts uri as shown in the example.
157157

158+
### Output Artifacts in SLSA Provenance
159+
160+
Artifacts are classified as either:
161+
162+
- Build Outputs - packages, images, etc. that are being published by the build.
163+
- Build Byproducts - logs, caches, etc. that are incidental artifacts that are produced by the build.
164+
165+
By default, Tekton Chains will consider all output artifacts as `byProducts` when generating in the [SLSA provenance](https://slsa.dev/spec/v1.0/provenance). In order to treat an artifact as a [subject](https://slsa.dev/spec/v1.0/provenance#schema) of the build, you must set a boolean field `"buildOutput": true` for the output artifact.
166+
167+
e.g.
168+
```yaml
169+
apiVersion: tekton.dev/v1
170+
kind: TaskRun
171+
metadata:
172+
generateName: step-artifacts-
173+
spec:
174+
taskSpec:
175+
description: |
176+
A simple task that populates artifacts to TaskRun stepState
177+
steps:
178+
- name: artifacts-producer
179+
image: bash:latest
180+
script: |
181+
cat > $(artifacts.path) << EOF
182+
{
183+
"outputs":[
184+
{
185+
"name":"image",
186+
"buildOutput": true,
187+
"values":[
188+
{
189+
"uri":"pkg:oci/nginx:stable-alpine3.17-slim?repository_url=docker.io/library",
190+
"digest":{
191+
"sha256":"df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48",
192+
"sha1":"95588b8f34c31eb7d62c92aaa4e6506639b06ef2"
193+
}
194+
}
195+
]
196+
}
197+
]
198+
}
199+
EOF
200+
```
201+
202+
This informs Tekton Chains your desire to handle the artifact.
203+
204+
> [!TIP]
205+
> When authoring a `StepAction` or a `Task`, you can parametrize this field to allow users to indicate their desire depending on what they are uploading - this can be useful for actions that may produce either a build output or a byproduct depending on the context!
206+
158207
### Passing Artifacts between Steps
159208
You can pass artifacts from one step to the next using:
160209
- Specific Artifact: `$(steps.<step-name>.inputs.<artifact-category-name>)` or `$(steps.<step-name>.outputs.<artifact-category-name>)`

docs/pipeline-api.md

+26-2
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,7 @@ string
13701370
</em>
13711371
</td>
13721372
<td>
1373+
<p>The artifact&rsquo;s identifying category name</p>
13731374
</td>
13741375
</tr>
13751376
<tr>
@@ -1382,7 +1383,18 @@ string
13821383
</em>
13831384
</td>
13841385
<td>
1385-
<p>The artifact&rsquo;s identifying category name</p>
1386+
<p>A collection of values related to the artifact</p>
1387+
</td>
1388+
</tr>
1389+
<tr>
1390+
<td>
1391+
<code>buildOutput</code><br/>
1392+
<em>
1393+
bool
1394+
</em>
1395+
</td>
1396+
<td>
1397+
<p>Indicate if the artifact is a build output or a by-product</p>
13861398
</td>
13871399
</tr>
13881400
</tbody>
@@ -10047,6 +10059,7 @@ string
1004710059
</em>
1004810060
</td>
1004910061
<td>
10062+
<p>The artifact&rsquo;s identifying category name</p>
1005010063
</td>
1005110064
</tr>
1005210065
<tr>
@@ -10059,7 +10072,18 @@ string
1005910072
</em>
1006010073
</td>
1006110074
<td>
10062-
<p>The artifact&rsquo;s identifying category name</p>
10075+
<p>A collection of values related to the artifact</p>
10076+
</td>
10077+
</tr>
10078+
<tr>
10079+
<td>
10080+
<code>buildOutput</code><br/>
10081+
<em>
10082+
bool
10083+
</em>
10084+
</td>
10085+
<td>
10086+
<p>Indicate if the artifact is a build output or a by-product</p>
1006310087
</td>
1006410088
</tr>
1006510089
</tbody>

examples/v1/taskruns/alpha/produce-consume-artifacts.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ spec:
66
taskSpec:
77
description: |
88
A simple task that populates artifacts to TaskRun stepState
9+
params:
10+
- name: buildOutput
11+
default: true
912
steps:
1013
- name: artifacts-producer
1114
image: mirror.gcr.io/bash
@@ -28,6 +31,7 @@ spec:
2831
"outputs":[
2932
{
3033
"name":"image",
34+
"buildOutput": $(params.buildOutput),
3135
"values":[
3236
{
3337
"uri":"pkg:github/package-url/purl-spec@244fd47e07d1004f0aed9c",

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module github.com/tektoncd/pipeline
22

33
go 1.22
4+
45
toolchain go1.22.5
56

67
require (

pkg/apis/pipeline/v1/artifact_types.go

+24-10
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ type Algorithm string
2626
// Artifact represents an artifact within a system, potentially containing multiple values
2727
// associated with it.
2828
type Artifact struct {
29-
Name string `json:"name,omitempty"` // The artifact's identifying category name
30-
Values []ArtifactValue `json:"values,omitempty"` // A collection of values related to the artifact
29+
// The artifact's identifying category name
30+
Name string `json:"name,omitempty"`
31+
// A collection of values related to the artifact
32+
Values []ArtifactValue `json:"values,omitempty"`
33+
// Indicate if the artifact is a build output or a by-product
34+
BuildOutput bool `json:"buildOutput,omitempty"`
3135
}
3236

3337
// ArtifactValue represents a specific value or data element within an Artifact.
@@ -82,35 +86,45 @@ func (a *Artifacts) Merge(another Artifacts) {
8286
})
8387
}
8488

85-
outputMap := make(map[string][]ArtifactValue)
89+
outputMap := make(map[string]Artifact)
8690
var newOutputs []Artifact
8791
for _, v := range a.Outputs {
88-
outputMap[v.Name] = v.Values
92+
outputMap[v.Name] = v
8993
}
9094

9195
for _, v := range another.Outputs {
9296
_, ok := outputMap[v.Name]
9397
if !ok {
94-
outputMap[v.Name] = []ArtifactValue{}
98+
outputMap[v.Name] = Artifact{Name: v.Name, Values: []ArtifactValue{}, BuildOutput: v.BuildOutput}
99+
}
100+
// only update buildOutput to true.
101+
// Do not convert to false if it was true before.
102+
if v.BuildOutput {
103+
art := outputMap[v.Name]
104+
art.BuildOutput = v.BuildOutput
105+
outputMap[v.Name] = art
95106
}
96107
for _, vv := range v.Values {
97108
exists := false
98-
for _, av := range outputMap[v.Name] {
109+
for _, av := range outputMap[v.Name].Values {
99110
if cmp.Equal(vv, av) {
100111
exists = true
101112
break
102113
}
103114
}
104115
if !exists {
105-
outputMap[v.Name] = append(outputMap[v.Name], vv)
116+
art := outputMap[v.Name]
117+
art.Values = append(art.Values, vv)
118+
outputMap[v.Name] = art
106119
}
107120
}
108121
}
109122

110-
for k, v := range outputMap {
123+
for _, v := range outputMap {
111124
newOutputs = append(newOutputs, Artifact{
112-
Name: k,
113-
Values: v,
125+
Name: v.Name,
126+
Values: v.Values,
127+
BuildOutput: v.BuildOutput,
114128
})
115129
}
116130
a.Inputs = newInputs

pkg/apis/pipeline/v1/artifact_types_test.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ func TestArtifactsMerge(t *testing.T) {
9595
},
9696
Outputs: []Artifact{
9797
{
98-
Name: "output1",
98+
Name: "output1",
99+
BuildOutput: true,
99100
Values: []ArtifactValue{
100101
{
101102
Digest: map[Algorithm]string{"sha256": "698c4539633943f7889f41605003d7fa63833722ebd2b37c7e75df1d3d06941a"},
@@ -104,7 +105,8 @@ func TestArtifactsMerge(t *testing.T) {
104105
},
105106
},
106107
{
107-
Name: "output2",
108+
Name: "output2",
109+
BuildOutput: true,
108110
Values: []ArtifactValue{
109111
{
110112
Digest: map[Algorithm]string{"sha256": "7e406d83706c7193df3e38b66d350e55df6f13d2a28a1d35917a043533a70f5c"},
@@ -150,7 +152,8 @@ func TestArtifactsMerge(t *testing.T) {
150152
},
151153
Outputs: []Artifact{
152154
{
153-
Name: "output1",
155+
Name: "output1",
156+
BuildOutput: true,
154157
Values: []ArtifactValue{
155158
{
156159
Digest: map[Algorithm]string{"sha256": "47de7a85905970a45132f48a9247879a15c483477e23a637504694e611135b40e"},
@@ -163,7 +166,8 @@ func TestArtifactsMerge(t *testing.T) {
163166
},
164167
},
165168
{
166-
Name: "output2",
169+
Name: "output2",
170+
BuildOutput: true,
167171
Values: []ArtifactValue{
168172
{
169173
Digest: map[Algorithm]string{"sha256": "7e406d83706c7193df3e38b66d350e55df6f13d2a28a1d35917a043533a70f5c"},

pkg/apis/pipeline/v1/openapi_generated.go

+11-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/pipeline/v1/swagger.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,16 @@
155155
"description": "TaskRunStepArtifact represents an artifact produced or used by a step within a task run. It directly uses the Artifact type for its structure.",
156156
"type": "object",
157157
"properties": {
158+
"buildOutput": {
159+
"description": "Indicate if the artifact is a build output or a by-product",
160+
"type": "boolean"
161+
},
158162
"name": {
163+
"description": "The artifact's identifying category name",
159164
"type": "string"
160165
},
161166
"values": {
162-
"description": "The artifact's identifying category name",
167+
"description": "A collection of values related to the artifact",
163168
"type": "array",
164169
"items": {
165170
"default": {},

pkg/apis/pipeline/v1beta1/artifact_types.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ type Algorithm string
66
// Artifact represents an artifact within a system, potentially containing multiple values
77
// associated with it.
88
type Artifact struct {
9-
Name string `json:"name,omitempty"` // The artifact's identifying category name
10-
Values []ArtifactValue `json:"values,omitempty"` // A collection of values related to the artifact
9+
// The artifact's identifying category name
10+
Name string `json:"name,omitempty"`
11+
// A collection of values related to the artifact
12+
Values []ArtifactValue `json:"values,omitempty"`
13+
// Indicate if the artifact is a build output or a by-product
14+
BuildOutput bool `json:"buildOutput,omitempty"`
1115
}
1216

1317
// ArtifactValue represents a specific value or data element within an Artifact.

pkg/apis/pipeline/v1beta1/openapi_generated.go

+11-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/pipeline/v1beta1/swagger.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,16 @@
155155
"description": "TaskRunStepArtifact represents an artifact produced or used by a step within a task run. It directly uses the Artifact type for its structure.",
156156
"type": "object",
157157
"properties": {
158+
"buildOutput": {
159+
"description": "Indicate if the artifact is a build output or a by-product",
160+
"type": "boolean"
161+
},
158162
"name": {
163+
"description": "The artifact's identifying category name",
159164
"type": "string"
160165
},
161166
"values": {
162-
"description": "The artifact's identifying category name",
167+
"description": "A collection of values related to the artifact",
163168
"type": "array",
164169
"items": {
165170
"default": {},

test/artifacts_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ spec:
117117
}}, taskrun.Status.Steps[0].Inputs); d != "" {
118118
t.Fatalf(`The expected stepState Inputs does not match created taskrun stepState Inputs. Here is the diff: %v`, d)
119119
}
120-
if d := cmp.Diff([]v1.TaskRunStepArtifact{{Name: "image",
120+
if d := cmp.Diff([]v1.TaskRunStepArtifact{{Name: "image", BuildOutput: true,
121121
Values: []v1.ArtifactValue{{Digest: map[v1.Algorithm]string{"sha1": "95588b8f34c31eb7d62c92aaa4e6506639b06ef2", "sha256": "df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48"},
122122
Uri: "pkg:balba",
123123
}},
@@ -191,7 +191,7 @@ spec:
191191
}}, taskrun.Status.Steps[0].Inputs); d != "" {
192192
t.Fatalf(`The expected stepState Inputs does not match created taskrun stepState Inputs. Here is the diff: %v`, d)
193193
}
194-
if d := cmp.Diff([]v1.TaskRunStepArtifact{{Name: "build-result",
194+
if d := cmp.Diff([]v1.TaskRunStepArtifact{{Name: "build-result", BuildOutput: false,
195195
Values: []v1.ArtifactValue{{Digest: map[v1.Algorithm]string{"sha1": "95588b8f34c31eb7d62c92aaa4e6506639b06ef2", "sha256": "df85b9e3983fe2ce20ef76ad675ecf435cc99fc9350adc54fa230bae8c32ce48"},
196196
Uri: "pkg:balba",
197197
}},
@@ -425,6 +425,7 @@ spec:
425425
"outputs":[
426426
{
427427
"name":"image",
428+
"buildOutput":true,
428429
"values":[
429430
{
430431
"uri":"pkg:balba",
@@ -523,6 +524,7 @@ spec:
523524
"outputs":[
524525
{
525526
"name":"build-result",
527+
"buildOutput":false,
526528
"values":[
527529
{
528530
"uri":"pkg:balba",

0 commit comments

Comments
 (0)