From acbb8f116c4eae082ee6a172cb17cc0cca502eb8 Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Tue, 25 Jul 2023 13:50:29 -0600 Subject: [PATCH] fix(compiler): convert local templates to an engine field for nested templates (#916) * fix(compiler): convert local templates to an engine field for nested templates * add WithLocalTemplates test --- api/pipeline/compile.go | 2 +- api/pipeline/expand.go | 2 +- api/pipeline/validate.go | 2 +- compiler/engine.go | 5 +++- compiler/native/compile.go | 43 ++++----------------------------- compiler/native/compile_test.go | 2 +- compiler/native/expand.go | 27 ++++++++++++++++----- compiler/native/native.go | 24 ++++++++++++------ compiler/native/native_test.go | 20 +++++++++++++++ 9 files changed, 70 insertions(+), 57 deletions(-) diff --git a/api/pipeline/compile.go b/api/pipeline/compile.go index 723bf2522..8ac5fed93 100644 --- a/api/pipeline/compile.go +++ b/api/pipeline/compile.go @@ -97,7 +97,7 @@ func CompilePipeline(c *gin.Context) { compiler := compiler.FromContext(c).Duplicate().WithCommit(p.GetCommit()).WithMetadata(m).WithRepo(r).WithUser(u) // compile the pipeline - pipeline, _, err := compiler.CompileLite(p.GetData(), true, true, nil) + pipeline, _, err := compiler.CompileLite(p.GetData(), true, true) if err != nil { retErr := fmt.Errorf("unable to compile pipeline %s: %w", entry, err) diff --git a/api/pipeline/expand.go b/api/pipeline/expand.go index 05e5c7dd8..17f782fee 100644 --- a/api/pipeline/expand.go +++ b/api/pipeline/expand.go @@ -98,7 +98,7 @@ func ExpandPipeline(c *gin.Context) { compiler := compiler.FromContext(c).Duplicate().WithCommit(p.GetCommit()).WithMetadata(m).WithRepo(r).WithUser(u) // expand the templates in the pipeline - pipeline, _, err := compiler.CompileLite(p.GetData(), true, false, nil) + pipeline, _, err := compiler.CompileLite(p.GetData(), true, false) if err != nil { retErr := fmt.Errorf("unable to expand pipeline %s: %w", entry, err) diff --git a/api/pipeline/validate.go b/api/pipeline/validate.go index 42e49e2ae..45aae1ff8 100644 --- a/api/pipeline/validate.go +++ b/api/pipeline/validate.go @@ -105,7 +105,7 @@ func ValidatePipeline(c *gin.Context) { } // validate the pipeline - pipeline, _, err := compiler.CompileLite(p.GetData(), template, false, nil) + pipeline, _, err := compiler.CompileLite(p.GetData(), template, false) if err != nil { retErr := fmt.Errorf("unable to validate pipeline %s: %w", entry, err) diff --git a/compiler/engine.go b/compiler/engine.go index 5d1d32993..0296c99d4 100644 --- a/compiler/engine.go +++ b/compiler/engine.go @@ -25,7 +25,7 @@ type Engine interface { // CompileLite defines a function that produces an light executable // representation of a pipeline from an object. This calls // Parse internally to convert the object to a yaml configuration. - CompileLite(interface{}, bool, bool, []string) (*yaml.Build, *library.Pipeline, error) + CompileLite(interface{}, bool, bool) (*yaml.Build, *library.Pipeline, error) // Duplicate defines a function that // creates a clone of the Engine. @@ -130,6 +130,9 @@ type Engine interface { // WithLocal defines a function that sets // the compiler local field in the Engine. WithLocal(bool) Engine + // WithLocalTemplates defines a function that sets + // the compiler local templates field in the Engine. + WithLocalTemplates([]string) Engine // WithMetadata defines a function that sets // the compiler Metadata type in the Engine. WithMetadata(*types.Metadata) Engine diff --git a/compiler/native/compile.go b/compiler/native/compile.go index f74e19bab..d106fa480 100644 --- a/compiler/native/compile.go +++ b/compiler/native/compile.go @@ -82,7 +82,7 @@ func (c *client) Compile(v interface{}) (*pipeline.Build, *library.Pipeline, err switch { case p.Metadata.RenderInline: - newPipeline, err := c.compileInline(p, nil, c.TemplateDepth) + newPipeline, err := c.compileInline(p, c.TemplateDepth) if err != nil { return nil, _pipeline, err } @@ -105,7 +105,7 @@ func (c *client) Compile(v interface{}) (*pipeline.Build, *library.Pipeline, err } // CompileLite produces a partial of an executable pipeline from a yaml configuration. -func (c *client) CompileLite(v interface{}, template, substitute bool, localTemplates []string) (*yaml.Build, *library.Pipeline, error) { +func (c *client) CompileLite(v interface{}, template, substitute bool) (*yaml.Build, *library.Pipeline, error) { p, data, err := c.Parse(v, c.repo.GetPipelineType(), new(yaml.Template)) if err != nil { return nil, nil, err @@ -117,7 +117,7 @@ func (c *client) CompileLite(v interface{}, template, substitute bool, localTemp _pipeline.SetType(c.repo.GetPipelineType()) if p.Metadata.RenderInline { - newPipeline, err := c.compileInline(p, localTemplates, c.TemplateDepth) + newPipeline, err := c.compileInline(p, c.TemplateDepth) if err != nil { return nil, _pipeline, err } @@ -134,24 +134,6 @@ func (c *client) CompileLite(v interface{}, template, substitute bool, localTemp // create map of templates for easy lookup templates := mapFromTemplates(p.Templates) - if c.local { - for _, file := range localTemplates { - // local templates override format is : - // - // example: example:/path/to/template.yml - parts := strings.Split(file, ":") - - // make sure the template was configured - _, ok := templates[parts[0]] - if !ok { - return nil, _pipeline, fmt.Errorf("template with name %s is not configured", parts[0]) - } - - // override the source for the given template - templates[parts[0]].Source = parts[1] - } - } - switch { case len(p.Stages) > 0: // inject the templates into the steps @@ -194,7 +176,7 @@ func (c *client) CompileLite(v interface{}, template, substitute bool, localTemp } // compileInline parses and expands out inline pipelines. -func (c *client) compileInline(p *yaml.Build, localTemplates []string, depth int) (*yaml.Build, error) { +func (c *client) compileInline(p *yaml.Build, depth int) (*yaml.Build, error) { newPipeline := *p newPipeline.Templates = yaml.TemplateSlice{} @@ -206,21 +188,6 @@ func (c *client) compileInline(p *yaml.Build, localTemplates []string, depth int } for _, template := range p.Templates { - if c.local { - for _, file := range localTemplates { - // local templates override format is : - // - // example: example:/path/to/template.yml - parts := strings.Split(file, ":") - - // make sure we're referencing the proper template - if parts[0] == template.Name { - // override the source for the given template - template.Source = parts[1] - } - } - } - bytes, err := c.getTemplate(template, template.Name) if err != nil { return nil, err @@ -240,7 +207,7 @@ func (c *client) compileInline(p *yaml.Build, localTemplates []string, depth int // if template parsed contains a template reference, recurse with decremented depth if len(parsed.Templates) > 0 && parsed.Metadata.RenderInline { - parsed, err = c.compileInline(parsed, localTemplates, depth-1) + parsed, err = c.compileInline(parsed, depth-1) if err != nil { return nil, err } diff --git a/compiler/native/compile_test.go b/compiler/native/compile_test.go index 5281dccd1..3a37703d6 100644 --- a/compiler/native/compile_test.go +++ b/compiler/native/compile_test.go @@ -3422,7 +3422,7 @@ func Test_CompileLite(t *testing.T) { t.Errorf("Reading yaml file return err: %v", err) } - got, _, err := compiler.CompileLite(yaml, tt.args.template, tt.args.substitute, nil) + got, _, err := compiler.CompileLite(yaml, tt.args.template, tt.args.substitute) if (err != nil) != tt.wantErr { t.Errorf("CompileLite() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/compiler/native/expand.go b/compiler/native/expand.go index 5314cf25e..d593bc26e 100644 --- a/compiler/native/expand.go +++ b/compiler/native/expand.go @@ -206,15 +206,30 @@ func (c *client) getTemplate(tmpl *yaml.Template, name string) ([]byte, error) { switch { case c.local: - a := &afero.Afero{ - Fs: afero.NewOsFs(), - } + // iterate over locally provided templates + for _, t := range c.localTemplates { + parts := strings.Split(t, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("local templates must be provided in the form :, got %s", t) + } - bytes, err = a.ReadFile(tmpl.Source) - if err != nil { - return bytes, err + if strings.EqualFold(tmpl.Name, parts[0]) { + a := &afero.Afero{ + Fs: afero.NewOsFs(), + } + + bytes, err = a.ReadFile(parts[1]) + if err != nil { + return bytes, err + } + + return bytes, nil + } } + // no template found in provided templates, exit with error + return nil, fmt.Errorf("unable to find template %s: not supplied in list %s", tmpl.Name, c.localTemplates) + case strings.EqualFold(tmpl.Type, "github"): // parse source from template src, err := c.Github.Parse(tmpl.Source) diff --git a/compiler/native/native.go b/compiler/native/native.go index 733a32407..c394af933 100644 --- a/compiler/native/native.go +++ b/compiler/native/native.go @@ -34,14 +34,15 @@ type client struct { CloneImage string TemplateDepth int - build *library.Build - comment string - commit string - files []string - local bool - metadata *types.Metadata - repo *library.Repo - user *library.User + build *library.Build + comment string + commit string + files []string + local bool + localTemplates []string + metadata *types.Metadata + repo *library.Repo + user *library.User } // New returns a Pipeline implementation that integrates with the supported registries. @@ -161,6 +162,13 @@ func (c *client) WithLocal(local bool) compiler.Engine { return c } +// WithLocalTemplates sets the compiler local templates in the Engine. +func (c *client) WithLocalTemplates(templates []string) compiler.Engine { + c.localTemplates = templates + + return c +} + // WithMetadata sets the compiler metadata type in the Engine. func (c *client) WithMetadata(m *types.Metadata) compiler.Engine { if m != nil { diff --git a/compiler/native/native_test.go b/compiler/native/native_test.go index 62c6fb7ed..de1f60631 100644 --- a/compiler/native/native_test.go +++ b/compiler/native/native_test.go @@ -202,6 +202,26 @@ func TestNative_WithLocal(t *testing.T) { } } +func TestNative_WithLocalTemplates(t *testing.T) { + // setup types + set := flag.NewFlagSet("test", 0) + c := cli.NewContext(nil, set, nil) + + localTemplates := []string{"example:tmpl.yml", "exmpl:template.yml"} + want, _ := New(c) + want.localTemplates = []string{"example:tmpl.yml", "exmpl:template.yml"} + + // run test + got, err := New(c) + if err != nil { + t.Errorf("Unable to create new compiler: %v", err) + } + + if !reflect.DeepEqual(got.WithLocalTemplates(localTemplates), want) { + t.Errorf("WithLocalTemplates is %v, want %v", got, want) + } +} + func TestNative_WithMetadata(t *testing.T) { // setup types set := flag.NewFlagSet("test", 0)