schema/v1/reference.go (117 lines of code) (raw):

package schema import ( "encoding/json" "fmt" "strings" "google.golang.org/protobuf/types/known/structpb" "gitlab.com/gitlab-org/step-runner/proto" ) // Reference is a reference to a step in either a Git repository or an OCI image type Reference struct { // Git corresponds to the JSON schema field "git". Git *GitReference `json:"git,omitempty" yaml:"git,omitempty" mapstructure:"git,omitempty"` // OCI corresponds to the JSON schema field "oci". OCI *OCIReference `json:"oci,omitempty" yaml:"oci,omitempty" mapstructure:"oci,omitempty"` } // UnmarshalJSON implements json.Unmarshaler. func (r *Reference) UnmarshalJSON(b []byte) error { var raw map[string]interface{} if err := json.Unmarshal(b, &raw); err != nil { return err } _, gitDefined := raw["git"] _, ociDefined := raw["oci"] if gitDefined && ociDefined { return fmt.Errorf("cannot use both git: and oci: fields, please specify only one step location") } if !gitDefined && !ociDefined { return fmt.Errorf("field git: or oci: required") } type Plain Reference var plain Plain if err := json.Unmarshal(b, &plain); err != nil { return err } *r = Reference(plain) return nil } func (r *Reference) compile(stepName string, inputs map[string]*structpb.Value, env map[string]string) (*proto.Step_Reference, error) { if r.Git == nil && r.OCI == nil { return nil, fmt.Errorf("compiling reference: git or oci not specified") } if r.Git != nil && r.OCI != nil { return nil, fmt.Errorf("compiling reference: git and oci specified") } if r.Git != nil { return r.compileGit() } return r.compileOCI(stepName, inputs, env) } func (r *Reference) compileGit() (*proto.Step_Reference, error) { url := defaultHTTPS(r.Git.Url) s := &proto.Step_Reference{ Protocol: proto.StepReferenceProtocol_git, Url: url, Filename: "step.yml", Version: r.Git.Rev, } if r.Git.Dir != nil { // nolint:staticcheck // SA1019 s.Path = strings.Split(*r.Git.Dir, "/") } if r.Git.File != nil { s.Filename = *r.Git.File } return s, nil } func (r *Reference) compileOCI(stepName string, inputs map[string]*structpb.Value, env map[string]string) (*proto.Step_Reference, error) { tag := r.OCI.Tag if tag == "" { tag = "latest" } dir := "" if r.OCI.Dir != nil { dir = *r.OCI.Dir } filename := "step.yml" if r.OCI.File != nil { filename = *r.OCI.File } fetchStepName := "fetch_step_" + stepName stepRef := &proto.Step_Reference{ Protocol: proto.StepReferenceProtocol_spec_def, SpecDef: &proto.SpecDefinition{ Spec: &proto.Spec{ Spec: &proto.Spec_Content{ OutputMethod: proto.OutputMethod_delegate, }}, Definition: &proto.Definition{ Type: proto.DefinitionType_steps, Steps: []*proto.Step{ { Name: fetchStepName, Step: &proto.Step_Reference{ Protocol: proto.StepReferenceProtocol_dist, Path: []string{"oci", "fetch"}, Filename: "step.yml", }, Inputs: map[string]*structpb.Value{ // inline the inputs "registry": structpb.NewStringValue(r.OCI.Registry), "repository": structpb.NewStringValue(r.OCI.Repository), "tag": structpb.NewStringValue(tag), "step_path": structpb.NewStringValue(dir), }, Env: env, }, { Name: stepName, Step: &proto.Step_Reference{ Protocol: proto.StepReferenceProtocol_local, StepPath: &proto.Step_Reference_PathExp{PathExp: fmt.Sprintf("${{steps.%s.outputs.fetched_step_path}}", fetchStepName)}, Filename: filename, }, Inputs: inputs, Env: env, }, }, Delegate: stepName, }, }, } return stepRef, nil }