diff --git a/docs/pipeline-api.md b/docs/pipeline-api.md index f100fbf2561..dfc20eb8933 100644 --- a/docs/pipeline-api.md +++ b/docs/pipeline-api.md @@ -3300,6 +3300,21 @@ string
Name of the referenced step
+ResolverRef
ResolverRef allows referencing a StepAction in a remote location +like a git repo.
+-(Appears on:PipelineRef, TaskRef) +(Appears on:PipelineRef, Ref, TaskRef)
ResolverRef can be used to refer to a Pipeline or Task in a remote @@ -11831,6 +11846,21 @@ string
Name of the referenced step
+ResolverRef
ResolverRef allows referencing a StepAction in a remote location +like a git repo.
+-(Appears on:PipelineRef, TaskRef) +(Appears on:PipelineRef, Ref, TaskRef)
ResolverRef can be used to refer to a Pipeline or Task in a remote diff --git a/pkg/apis/pipeline/v1/container_types.go b/pkg/apis/pipeline/v1/container_types.go index e0b979ffa86..922ac1c8d11 100644 --- a/pkg/apis/pipeline/v1/container_types.go +++ b/pkg/apis/pipeline/v1/container_types.go @@ -144,6 +144,10 @@ type Step struct { type Ref struct { // Name of the referenced step Name string `json:"name,omitempty"` + // ResolverRef allows referencing a StepAction in a remote location + // like a git repo. + // +optional + ResolverRef `json:",omitempty"` } // OnErrorType defines a list of supported exiting behavior of a container on error diff --git a/pkg/apis/pipeline/v1/container_validation.go b/pkg/apis/pipeline/v1/container_validation.go new file mode 100644 index 00000000000..b741b319927 --- /dev/null +++ b/pkg/apis/pipeline/v1/container_validation.go @@ -0,0 +1,62 @@ +/* +Copyright 2022 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "context" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +// Validate ensures that a supplied Ref field is populated +// correctly. No errors are returned for a nil Ref. +func (ref *Ref) Validate(ctx context.Context) (errs *apis.FieldError) { + if ref == nil { + return + } + + switch { + case ref.Resolver != "" || ref.Params != nil: + if ref.Resolver != "" { + errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "resolver", config.BetaAPIFields).ViaField("resolver")) + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "resolver")) + } + } + if ref.Params != nil { + errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "resolver params", config.BetaAPIFields).ViaField("params")) + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "params")) + } + if ref.Resolver == "" { + errs = errs.Also(apis.ErrMissingField("resolver")) + } + errs = errs.Also(ValidateParameters(ctx, ref.Params)) + } + case ref.Name != "": + // ref name must be a valid k8s name + if errSlice := validation.IsQualifiedName(ref.Name); len(errSlice) != 0 { + errs = errs.Also(apis.ErrInvalidValue(strings.Join(errSlice, ","), "name")) + } + default: + errs = errs.Also(apis.ErrMissingField("name")) + } + return errs +} diff --git a/pkg/apis/pipeline/v1/container_validation_test.go b/pkg/apis/pipeline/v1/container_validation_test.go new file mode 100644 index 00000000000..7ab2267ebdc --- /dev/null +++ b/pkg/apis/pipeline/v1/container_validation_test.go @@ -0,0 +1,157 @@ +/* +Copyright 2023 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + cfgtesting "github.com/tektoncd/pipeline/pkg/apis/config/testing" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/test/diff" + "knative.dev/pkg/apis" +) + +func TestRef_Valid(t *testing.T) { + tests := []struct { + name string + ref *v1beta1.Ref + wc func(context.Context) context.Context + }{{ + name: "nil ref", + }, { + name: "simple ref", + ref: &v1beta1.Ref{Name: "refname"}, + }, { + name: "beta feature: valid resolver", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git"}}, + wc: cfgtesting.EnableBetaAPIFields, + }, { + name: "beta feature: valid resolver with alpha flag", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git"}}, + wc: cfgtesting.EnableAlphaAPIFields, + }, { + name: "beta feature: valid resolver with params", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git", Params: v1beta1.Params{{ + Name: "repo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "https://github.com/tektoncd/pipeline.git", + }, + }, { + Name: "branch", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "baz", + }, + }}}}, + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + if err := ts.ref.Validate(ctx); err != nil { + t.Errorf("Ref.Validate() error = %v", err) + } + }) + } +} + +func TestRef_Invalid(t *testing.T) { + tests := []struct { + name string + ref *v1beta1.Ref + wantErr *apis.FieldError + wc func(context.Context) context.Context + }{{ + name: "missing ref name", + ref: &v1beta1.Ref{}, + wantErr: apis.ErrMissingField("name"), + }, { + name: "ref params disallowed without resolver", + ref: &v1beta1.Ref{ + ResolverRef: v1beta1.ResolverRef{ + Params: v1beta1.Params{}, + }, + }, + wantErr: apis.ErrMissingField("resolver"), + }, { + name: "ref resolver disallowed in conjunction with ref name", + ref: &v1beta1.Ref{ + Name: "foo", + ResolverRef: v1beta1.ResolverRef{ + Resolver: "git", + }, + }, + wantErr: apis.ErrMultipleOneOf("name", "resolver"), + }, { + name: "ref params disallowed in conjunction with ref name", + ref: &v1beta1.Ref{ + Name: "bar", + ResolverRef: v1beta1.ResolverRef{ + Params: v1beta1.Params{{ + Name: "foo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "bar", + }, + }}, + }, + }, + wantErr: apis.ErrMultipleOneOf("name", "params").Also(apis.ErrMissingField("resolver")), + }, { + name: "invalid ref name", + ref: &v1beta1.Ref{Name: "_foo"}, + wantErr: &apis.FieldError{ + Message: `invalid value: name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`, + Paths: []string{"name"}, + }, + }, { + name: "ref param object requires beta", + ref: &v1beta1.Ref{ + ResolverRef: v1beta1.ResolverRef{ + Resolver: "some-resolver", + Params: v1beta1.Params{{ + Name: "foo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeObject, + ObjectVal: map[string]string{"bar": "baz"}, + }, + }}, + }, + }, + wc: cfgtesting.EnableStableAPIFields, + wantErr: apis.ErrGeneric("resolver requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"").Also( + apis.ErrGeneric("resolver params requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"")).Also( + apis.ErrGeneric("object type parameter requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"")), + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + err := ts.ref.Validate(ctx) + if d := cmp.Diff(ts.wantErr.Error(), err.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/apis/pipeline/v1/task_validation.go b/pkg/apis/pipeline/v1/task_validation.go index cdad33d7a80..9c68b538deb 100644 --- a/pkg/apis/pipeline/v1/task_validation.go +++ b/pkg/apis/pipeline/v1/task_validation.go @@ -268,6 +268,7 @@ func validateStep(ctx context.Context, s Step, names sets.String) (errs *apis.Fi if !config.FromContextOrDefaults(ctx).FeatureFlags.EnableStepActions { return apis.ErrGeneric("feature flag %s should be set to true to reference StepActions in Steps.", config.EnableStepActions) } + s.Ref.Validate(ctx) if s.Image != "" { errs = errs.Also(&apis.FieldError{ Message: "image cannot be used with Ref", diff --git a/pkg/apis/pipeline/v1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1/zz_generated.deepcopy.go index c59e2c4f615..e3307f4720e 100644 --- a/pkg/apis/pipeline/v1/zz_generated.deepcopy.go +++ b/pkg/apis/pipeline/v1/zz_generated.deepcopy.go @@ -1011,6 +1011,7 @@ func (in *Provenance) DeepCopy() *Provenance { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Ref) DeepCopyInto(out *Ref) { *out = *in + in.ResolverRef.DeepCopyInto(&out.ResolverRef) return } @@ -1282,7 +1283,7 @@ func (in *Step) DeepCopyInto(out *Step) { if in.Ref != nil { in, out := &in.Ref, &out.Ref *out = new(Ref) - **out = **in + (*in).DeepCopyInto(*out) } return } diff --git a/pkg/apis/pipeline/v1beta1/container_conversion.go b/pkg/apis/pipeline/v1beta1/container_conversion.go index 8e7171efa5e..0ec816b2548 100644 --- a/pkg/apis/pipeline/v1beta1/container_conversion.go +++ b/pkg/apis/pipeline/v1beta1/container_conversion.go @@ -24,10 +24,16 @@ import ( func (r Ref) convertTo(ctx context.Context, sink *v1.Ref) { sink.Name = r.Name + new := v1.ResolverRef{} + r.ResolverRef.convertTo(ctx, &new) + sink.ResolverRef = new } func (r *Ref) convertFrom(ctx context.Context, source v1.Ref) { r.Name = source.Name + new := ResolverRef{} + new.convertFrom(ctx, source.ResolverRef) + r.ResolverRef = new } func (s Step) convertTo(ctx context.Context, sink *v1.Step) { diff --git a/pkg/apis/pipeline/v1beta1/container_types.go b/pkg/apis/pipeline/v1beta1/container_types.go index 539e01233f4..0bf6f502664 100644 --- a/pkg/apis/pipeline/v1beta1/container_types.go +++ b/pkg/apis/pipeline/v1beta1/container_types.go @@ -238,6 +238,10 @@ type Step struct { type Ref struct { // Name of the referenced step Name string `json:"name,omitempty"` + // ResolverRef allows referencing a StepAction in a remote location + // like a git repo. + // +optional + ResolverRef `json:",omitempty"` } // OnErrorType defines a list of supported exiting behavior of a container on error diff --git a/pkg/apis/pipeline/v1beta1/container_validation.go b/pkg/apis/pipeline/v1beta1/container_validation.go new file mode 100644 index 00000000000..b8fb610e8f2 --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/container_validation.go @@ -0,0 +1,62 @@ +/* +Copyright 2022 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "context" + "strings" + + "github.com/tektoncd/pipeline/pkg/apis/config" + "k8s.io/apimachinery/pkg/util/validation" + "knative.dev/pkg/apis" +) + +// Validate ensures that a supplied Ref field is populated +// correctly. No errors are returned for a nil Ref. +func (ref *Ref) Validate(ctx context.Context) (errs *apis.FieldError) { + if ref == nil { + return + } + + switch { + case ref.Resolver != "" || ref.Params != nil: + if ref.Resolver != "" { + errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "resolver", config.BetaAPIFields).ViaField("resolver")) + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "resolver")) + } + } + if ref.Params != nil { + errs = errs.Also(config.ValidateEnabledAPIFields(ctx, "resolver params", config.BetaAPIFields).ViaField("params")) + if ref.Name != "" { + errs = errs.Also(apis.ErrMultipleOneOf("name", "params")) + } + if ref.Resolver == "" { + errs = errs.Also(apis.ErrMissingField("resolver")) + } + errs = errs.Also(ValidateParameters(ctx, ref.Params)) + } + case ref.Name != "": + // Ref name must be a valid k8s name + if errSlice := validation.IsQualifiedName(ref.Name); len(errSlice) != 0 { + errs = errs.Also(apis.ErrInvalidValue(strings.Join(errSlice, ","), "name")) + } + default: + errs = errs.Also(apis.ErrMissingField("name")) + } + return errs +} diff --git a/pkg/apis/pipeline/v1beta1/container_validation_test.go b/pkg/apis/pipeline/v1beta1/container_validation_test.go new file mode 100644 index 00000000000..fa395734bc8 --- /dev/null +++ b/pkg/apis/pipeline/v1beta1/container_validation_test.go @@ -0,0 +1,157 @@ +/* +Copyright 2023 The Tekton Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + cfgtesting "github.com/tektoncd/pipeline/pkg/apis/config/testing" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/test/diff" + "knative.dev/pkg/apis" +) + +func TestRef_Valid(t *testing.T) { + tests := []struct { + name string + ref *v1beta1.Ref + wc func(context.Context) context.Context + }{{ + name: "nil ref", + }, { + name: "simple ref", + ref: &v1beta1.Ref{Name: "refname"}, + }, { + name: "beta feature: valid resolver", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git"}}, + wc: cfgtesting.EnableBetaAPIFields, + }, { + name: "beta feature: valid resolver with alpha flag", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git"}}, + wc: cfgtesting.EnableAlphaAPIFields, + }, { + name: "beta feature: valid resolver with params", + ref: &v1beta1.Ref{ResolverRef: v1beta1.ResolverRef{Resolver: "git", Params: v1beta1.Params{{ + Name: "repo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "https://github.com/tektoncd/pipeline.git", + }, + }, { + Name: "branch", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "baz", + }, + }}}}, + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + if err := ts.ref.Validate(ctx); err != nil { + t.Errorf("Ref.Validate() error = %v", err) + } + }) + } +} + +func TestRef_Invalid(t *testing.T) { + tests := []struct { + name string + ref *v1beta1.Ref + wantErr *apis.FieldError + wc func(context.Context) context.Context + }{{ + name: "missing ref name", + ref: &v1beta1.Ref{}, + wantErr: apis.ErrMissingField("name"), + }, { + name: "ref params disallowed without resolver", + ref: &v1beta1.Ref{ + ResolverRef: v1beta1.ResolverRef{ + Params: v1beta1.Params{}, + }, + }, + wantErr: apis.ErrMissingField("resolver"), + }, { + name: "ref resolver disallowed in conjunction with ref name", + ref: &v1beta1.Ref{ + Name: "foo", + ResolverRef: v1beta1.ResolverRef{ + Resolver: "git", + }, + }, + wantErr: apis.ErrMultipleOneOf("name", "resolver"), + }, { + name: "ref params disallowed in conjunction with ref name", + ref: &v1beta1.Ref{ + Name: "bar", + ResolverRef: v1beta1.ResolverRef{ + Params: v1beta1.Params{{ + Name: "foo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeString, + StringVal: "bar", + }, + }}, + }, + }, + wantErr: apis.ErrMultipleOneOf("name", "params").Also(apis.ErrMissingField("resolver")), + }, { + name: "invalid ref name", + ref: &v1beta1.Ref{Name: "_foo"}, + wantErr: &apis.FieldError{ + Message: `invalid value: name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`, + Paths: []string{"name"}, + }, + }, { + name: "ref param object requires beta", + ref: &v1beta1.Ref{ + ResolverRef: v1beta1.ResolverRef{ + Resolver: "some-resolver", + Params: v1beta1.Params{{ + Name: "foo", + Value: v1beta1.ParamValue{ + Type: v1beta1.ParamTypeObject, + ObjectVal: map[string]string{"bar": "baz"}, + }, + }}, + }, + }, + wc: cfgtesting.EnableStableAPIFields, + wantErr: apis.ErrGeneric("resolver requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"").Also( + apis.ErrGeneric("resolver params requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"")).Also( + apis.ErrGeneric("object type parameter requires \"enable-api-fields\" feature gate to be \"alpha\" or \"beta\" but it is \"stable\"")), + }} + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + ctx := context.Background() + if ts.wc != nil { + ctx = ts.wc(ctx) + } + err := ts.ref.Validate(ctx) + if d := cmp.Diff(ts.wantErr.Error(), err.Error()); d != "" { + t.Error(diff.PrintWantGot(d)) + } + }) + } +} diff --git a/pkg/apis/pipeline/v1beta1/task_conversion_test.go b/pkg/apis/pipeline/v1beta1/task_conversion_test.go index 1136be3d78b..0603d0a299a 100644 --- a/pkg/apis/pipeline/v1beta1/task_conversion_test.go +++ b/pkg/apis/pipeline/v1beta1/task_conversion_test.go @@ -83,6 +83,24 @@ spec: - ref: name: "step-action" ` + + remoteStepActionTaskYAML := ` +metadata: + name: foo + namespace: bar +spec: + steps: + - ref: + resolver: "git" + params: + - name: url + value: https://github.com/tektoncd/catalog.git + - name: pathInRepo + value: /stepaction/sample/sample.yaml + - name: revision + value: main +` + taskWithAllNoDeprecatedFieldsYAML := ` metadata: name: foo @@ -106,7 +124,7 @@ spec: volumeMounts: volumeDevices: imagePullPolicy: IfNotPresent - securityContext: + securityContext: privileged: true script: "echo 'hello world'" timeout: 1h @@ -131,7 +149,7 @@ spec: volumeMounts: volumeDevices: imagePullPolicy: IfNotPresent - securityContext: + securityContext: privileged: true sidecars: - name: sidecar @@ -148,7 +166,7 @@ spec: volumeMounts: volumeDevices: imagePullPolicy: IfNotPresent - securityContext: + securityContext: privileged: true script: "echo 'hello world'" timeout: 1h @@ -273,6 +291,9 @@ spec: stepActionTaskV1beta1 := parse.MustParseV1beta1Task(t, stepActionTaskYAML) stepActionTaskV1 := parse.MustParseV1Task(t, stepActionTaskYAML) + remoteStepActionTaskV1beta1 := parse.MustParseV1beta1Task(t, remoteStepActionTaskYAML) + remoteStepActionTaskV1 := parse.MustParseV1Task(t, remoteStepActionTaskYAML) + taskWithAllNoDeprecatedFieldsV1beta1 := parse.MustParseV1beta1Task(t, taskWithAllNoDeprecatedFieldsYAML) taskWithAllNoDeprecatedFieldsV1 := parse.MustParseV1Task(t, taskWithAllNoDeprecatedFieldsYAML) @@ -309,6 +330,10 @@ spec: name: "step action in task", v1beta1Task: stepActionTaskV1beta1, v1Task: stepActionTaskV1, + }, { + name: "remote step action in task", + v1beta1Task: remoteStepActionTaskV1beta1, + v1Task: remoteStepActionTaskV1, }, { name: "task conversion deprecated fields", v1beta1Task: taskWithDeprecatedFieldsV1beta1, diff --git a/pkg/apis/pipeline/v1beta1/task_validation.go b/pkg/apis/pipeline/v1beta1/task_validation.go index a4a38800fe7..c5925fd81f9 100644 --- a/pkg/apis/pipeline/v1beta1/task_validation.go +++ b/pkg/apis/pipeline/v1beta1/task_validation.go @@ -274,6 +274,7 @@ func validateStep(ctx context.Context, s Step, names sets.String) (errs *apis.Fi if !config.FromContextOrDefaults(ctx).FeatureFlags.EnableStepActions { return apis.ErrGeneric("feature flag %s should be set to true to reference StepActions in Steps.", config.EnableStepActions) } + s.Ref.Validate(ctx) if s.Image != "" { errs = errs.Also(&apis.FieldError{ Message: "image cannot be used with Ref", diff --git a/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go b/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go index db21dad9f79..710d589033b 100644 --- a/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/pipeline/v1beta1/zz_generated.deepcopy.go @@ -1458,6 +1458,7 @@ func (in *Provenance) DeepCopy() *Provenance { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Ref) DeepCopyInto(out *Ref) { *out = *in + in.ResolverRef.DeepCopyInto(&out.ResolverRef) return } @@ -1754,7 +1755,7 @@ func (in *Step) DeepCopyInto(out *Step) { if in.Ref != nil { in, out := &in.Ref, &out.Ref *out = new(Ref) - **out = **in + (*in).DeepCopyInto(*out) } return }