From 609f960bb5db552c443558be7883be78d75f4085 Mon Sep 17 00:00:00 2001 From: David May <49894298+wass3rw3rk@users.noreply.github.com> Date: Thu, 26 Sep 2024 14:17:01 -0500 Subject: [PATCH] fix(templates): prevent nil panic (#1194) --- compiler/native/compile_test.go | 118 ++++++++++++++++++ compiler/native/parse.go | 15 +++ compiler/native/parse_test.go | 15 +++ .../testdata/golang_inline_stages_env.yml | 19 +++ .../testdata/inline_with_stages_env.yml | 27 ++++ 5 files changed, 194 insertions(+) create mode 100644 compiler/native/testdata/golang_inline_stages_env.yml create mode 100644 compiler/native/testdata/inline_with_stages_env.yml diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index 275ab925c..29a37c8d3 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -3292,6 +3292,123 @@ func Test_CompileLite(t *testing.T) { }, wantErr: false, }, + { + name: "render_inline with stages and env in template", + args: args{ + file: "testdata/inline_with_stages_env.yml", + pipelineType: "", + substitute: true, + }, + want: &yaml.Build{ + Version: "1", + Metadata: yaml.Metadata{ + RenderInline: true, + Environment: []string{"steps", "services", "secrets"}, + }, + Templates: []*yaml.Template{ + { + Name: "golang", + Source: "github.example.com/github/octocat/golang_inline_stages_env.yml", + Format: "golang", + Type: "github", + Variables: map[string]any{ + "image": string("golang:latest"), + "VELA_TEMPLATE_NAME": string("golang"), + }, + }, + }, + Environment: raw.StringSliceMap{"DONT": "break"}, + Stages: []*yaml.Stage{ + { + Name: "test", + Needs: []string{"clone"}, + Steps: []*yaml.Step{ + { + Commands: raw.StringSlice{"echo from inline"}, + Image: "alpine", + Name: "test", + Pull: "not_present", + }, + { + Commands: raw.StringSlice{"echo from inline ruleset"}, + Image: "alpine", + Name: "ruleset", + Pull: "not_present", + Ruleset: yaml.Ruleset{ + If: yaml.Rules{ + Event: []string{"push"}, + Branch: []string{"main"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + }, + { + Name: "golang_foo", + Needs: []string{"clone"}, + Steps: []*yaml.Step{ + { + Commands: raw.StringSlice{"echo hello from foo"}, + Image: "golang:latest", + Name: "golang_foo", + Pull: "not_present", + Ruleset: yaml.Ruleset{ + If: yaml.Rules{ + Event: []string{"tag"}, + Tag: []string{"v*"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + }, + { + Name: "golang_bar", + Needs: []string{"clone"}, + Steps: []*yaml.Step{ + { + Commands: raw.StringSlice{"echo hello from bar"}, + Image: "golang:latest", + Name: "golang_bar", + Pull: "not_present", + Ruleset: yaml.Ruleset{ + If: yaml.Rules{ + Event: []string{"tag"}, + Tag: []string{"v*"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + }, + { + Name: "golang_star", + Needs: []string{"clone"}, + Steps: []*yaml.Step{ + { + Commands: raw.StringSlice{"echo hello from star"}, + Image: "golang:latest", + Name: "golang_star", + Pull: "not_present", + Ruleset: yaml.Ruleset{ + If: yaml.Rules{ + Event: []string{"tag"}, + Tag: []string{"v*"}, + }, + Matcher: "filepath", + Operator: "and", + }, + }, + }, + }, + }, + }, + wantErr: false, + }, { name: "render_inline with stages - ruleset", args: args{ @@ -3827,6 +3944,7 @@ func Test_CompileLite(t *testing.T) { Metadata: yaml.Metadata{ Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Stages: []*yaml.Stage{ { Name: "foo", diff --git a/compiler/native/parse.go b/compiler/native/parse.go index a09f6964e..6da637188 100644 --- a/compiler/native/parse.go +++ b/compiler/native/parse.go @@ -12,6 +12,7 @@ import ( "github.com/go-vela/server/compiler/template/native" "github.com/go-vela/server/compiler/template/starlark" "github.com/go-vela/types/constants" + typesRaw "github.com/go-vela/types/raw" types "github.com/go-vela/types/yaml" ) @@ -100,6 +101,13 @@ func (c *client) Parse(v interface{}, pipelineType string, template *types.Templ return nil, nil, fmt.Errorf("unable to parse config: unrecognized pipeline_type of %s", c.repo.GetPipelineType()) } + // initializing Environment to prevent nil error + // as it may be modified later via templates, if + // none are defined in the base pipeline + if p.Environment == nil { + p.Environment = typesRaw.StringSliceMap{} + } + return p, raw, nil } @@ -113,6 +121,13 @@ func ParseBytes(data []byte) (*types.Build, []byte, error) { return nil, data, fmt.Errorf("unable to unmarshal yaml: %w", err) } + // initializing Environment to prevent nil error + // as it may be modified later via templates, if + // none are defined in the base pipeline + if config.Environment == nil { + config.Environment = typesRaw.StringSliceMap{} + } + return config, data, nil } diff --git a/compiler/native/parse_test.go b/compiler/native/parse_test.go index 74da7baa9..a47da0459 100644 --- a/compiler/native/parse_test.go +++ b/compiler/native/parse_test.go @@ -29,6 +29,7 @@ func TestNative_Parse_Metadata_Bytes(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -57,6 +58,7 @@ func TestNative_Parse_Metadata_File(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -103,6 +105,7 @@ func TestNative_Parse_Metadata_Path(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -126,6 +129,7 @@ func TestNative_Parse_Metadata_Reader(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -154,6 +158,7 @@ func TestNative_Parse_Metadata_String(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -179,6 +184,7 @@ func TestNative_Parse_Parameters(t *testing.T) { Metadata: yaml.Metadata{ Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Steps: yaml.StepSlice{ &yaml.Step{ Image: "plugins/docker:18.09", @@ -457,6 +463,7 @@ func TestNative_Parse_Secrets(t *testing.T) { Metadata: yaml.Metadata{ Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Secrets: yaml.SecretSlice{ &yaml.Secret{ Name: "docker_username", @@ -527,6 +534,7 @@ func TestNative_Parse_Stages(t *testing.T) { Metadata: yaml.Metadata{ Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Stages: yaml.StageSlice{ &yaml.Stage{ Name: "install", @@ -603,6 +611,7 @@ func TestNative_Parse_Steps(t *testing.T) { Metadata: yaml.Metadata{ Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Steps: yaml.StepSlice{ &yaml.Step{ Commands: []string{"./gradlew downloadDependencies"}, @@ -663,6 +672,7 @@ func TestNative_ParseBytes_Metadata(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -709,6 +719,7 @@ func TestNative_ParseFile_Metadata(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -759,6 +770,7 @@ func TestNative_ParsePath_Metadata(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -795,6 +807,7 @@ func TestNative_ParseReader_Metadata(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -836,6 +849,7 @@ func TestNative_ParseString_Metadata(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, } // run test @@ -870,6 +884,7 @@ func Test_client_Parse(t *testing.T) { Clone: nil, Environment: []string{"steps", "services", "secrets"}, }, + Environment: raw.StringSliceMap{}, Steps: yaml.StepSlice{ { Name: "foo", diff --git a/compiler/native/testdata/golang_inline_stages_env.yml b/compiler/native/testdata/golang_inline_stages_env.yml new file mode 100644 index 000000000..32be4a52f --- /dev/null +++ b/compiler/native/testdata/golang_inline_stages_env.yml @@ -0,0 +1,19 @@ +version: "1" + +environment: + DONT: break + +{{$stageList := list "foo" "bar" "star" -}} + +stages: + {{range $stage := $stageList -}} + {{ $stage }}: + steps: + - name: {{ $stage }} + image: {{ default "alpine" $.image }} + ruleset: + event: tag + tag: v* + commands: + - echo hello from {{ $stage }} + {{ end }} \ No newline at end of file diff --git a/compiler/native/testdata/inline_with_stages_env.yml b/compiler/native/testdata/inline_with_stages_env.yml new file mode 100644 index 000000000..93f5ff58a --- /dev/null +++ b/compiler/native/testdata/inline_with_stages_env.yml @@ -0,0 +1,27 @@ +version: "1" + +metadata: + render_inline: true + +templates: + - name: golang + source: github.example.com/github/octocat/golang_inline_stages_env.yml + format: golang + type: github + vars: + image: golang:latest + +stages: + test: + steps: + - name: test + image: alpine + commands: + - echo from inline + - name: ruleset + image: alpine + ruleset: + event: push + branch: main + commands: + - echo from inline ruleset \ No newline at end of file