Skip to content

Commit

Permalink
fix(compiler): surface template warnings and StringSliceMap YAML erro…
Browse files Browse the repository at this point in the history
…rs (#1251)

* fix(compiler): treat top level anchors like nested anchors

* fix(compiler): surface template warnings and StringSliceMap YAML errors

* brackets surrounding template name for clarity
  • Loading branch information
ecrupper authored Feb 4, 2025
1 parent 1c3c159 commit cf8aea3
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 64 deletions.
4 changes: 2 additions & 2 deletions compiler/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ type Engine interface {

// ExpandStages defines a function that injects the template
// for each templated step in every stage in a yaml configuration.
ExpandStages(context.Context, *yaml.Build, map[string]*yaml.Template, *pipeline.RuleData) (*yaml.Build, error)
ExpandStages(context.Context, *yaml.Build, map[string]*yaml.Template, *pipeline.RuleData, []string) (*yaml.Build, []string, error)
// ExpandSteps defines a function that injects the template
// for each templated step in a yaml configuration with the provided template depth.
ExpandSteps(context.Context, *yaml.Build, map[string]*yaml.Template, *pipeline.RuleData, int) (*yaml.Build, error)
ExpandSteps(context.Context, *yaml.Build, map[string]*yaml.Template, *pipeline.RuleData, []string, int) (*yaml.Build, []string, error)

// Init Compiler Interface Functions

Expand Down
26 changes: 20 additions & 6 deletions compiler/native/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,13 @@ func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipel
switch {
case len(p.Stages) > 0:
// inject the templates into the steps
p, err = c.ExpandStages(ctx, p, templates, ruleData)
p, warnings, err = c.ExpandStages(ctx, p, templates, ruleData, _pipeline.GetWarnings())
if err != nil {
return nil, _pipeline, err
}

_pipeline.SetWarnings(warnings)

if substitute {
// inject the substituted environment variables into the steps
p.Stages, err = c.SubstituteStages(p.Stages)
Expand Down Expand Up @@ -193,11 +195,13 @@ func (c *client) CompileLite(ctx context.Context, v interface{}, ruleData *pipel

case len(p.Steps) > 0:
// inject the templates into the steps
p, err = c.ExpandSteps(ctx, p, templates, ruleData, c.GetTemplateDepth())
p, warnings, err = c.ExpandSteps(ctx, p, templates, ruleData, _pipeline.GetWarnings(), c.GetTemplateDepth())
if err != nil {
return nil, _pipeline, err
}

_pipeline.SetWarnings(warnings)

if substitute {
// inject the substituted environment variables into the steps
p.Steps, err = c.SubstituteSteps(p.Steps)
Expand Down Expand Up @@ -334,7 +338,10 @@ func (c *client) compileInline(ctx context.Context, p *yaml.Build, depth int) (*

// compileSteps executes the workflow for converting a YAML pipeline into an executable struct.
func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api.Pipeline, tmpls map[string]*yaml.Template, r *pipeline.RuleData) (*pipeline.Build, *api.Pipeline, error) {
var err error
var (
warnings []string
err error
)

// check if the pipeline disabled the clone
if p.Metadata.Clone == nil || *p.Metadata.Clone {
Expand All @@ -358,11 +365,13 @@ func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api
}

// inject the templates into the steps
p, err = c.ExpandSteps(ctx, p, tmpls, r, c.GetTemplateDepth())
p, warnings, err = c.ExpandSteps(ctx, p, tmpls, r, _pipeline.GetWarnings(), c.GetTemplateDepth())
if err != nil {
return nil, _pipeline, err
}

_pipeline.SetWarnings(warnings)

if c.ModificationService.Endpoint != "" {
// send config to external endpoint for modification
//
Expand Down Expand Up @@ -437,7 +446,10 @@ func (c *client) compileSteps(ctx context.Context, p *yaml.Build, _pipeline *api

// compileStages executes the workflow for converting a YAML pipeline into an executable struct.
func (c *client) compileStages(ctx context.Context, p *yaml.Build, _pipeline *api.Pipeline, tmpls map[string]*yaml.Template, r *pipeline.RuleData) (*pipeline.Build, *api.Pipeline, error) {
var err error
var (
warnings []string
err error
)

// check if the pipeline disabled the clone
if p.Metadata.Clone == nil || *p.Metadata.Clone {
Expand All @@ -455,11 +467,13 @@ func (c *client) compileStages(ctx context.Context, p *yaml.Build, _pipeline *ap
}

// inject the templates into the stages
p, err = c.ExpandStages(ctx, p, tmpls, r)
p, warnings, err = c.ExpandStages(ctx, p, tmpls, r, _pipeline.GetWarnings())
if err != nil {
return nil, _pipeline, err
}

_pipeline.SetWarnings(warnings)

if c.ModificationService.Endpoint != "" {
// send config to external endpoint for modification
//
Expand Down
43 changes: 25 additions & 18 deletions compiler/native/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@ import (

// ExpandStages injects the template for each
// templated step in every stage in a yaml configuration.
func (c *client) ExpandStages(ctx context.Context, s *yaml.Build, tmpls map[string]*yaml.Template, r *pipeline.RuleData) (*yaml.Build, error) {
func (c *client) ExpandStages(ctx context.Context, s *yaml.Build, tmpls map[string]*yaml.Template, r *pipeline.RuleData, warnings []string) (*yaml.Build, []string, error) {
var (
p *yaml.Build
err error
)

if len(tmpls) == 0 {
return s, nil
return s, warnings, nil
}

// iterate through all stages
for _, stage := range s.Stages {
// inject the templates into the steps for the stage
p, err := c.ExpandSteps(ctx, &yaml.Build{Steps: stage.Steps, Secrets: s.Secrets, Services: s.Services, Environment: s.Environment}, tmpls, r, c.GetTemplateDepth())
p, warnings, err = c.ExpandSteps(ctx, &yaml.Build{Steps: stage.Steps, Secrets: s.Secrets, Services: s.Services, Environment: s.Environment}, tmpls, r, warnings, c.GetTemplateDepth())
if err != nil {
return nil, err
return nil, warnings, err
}

stage.Steps = p.Steps
Expand All @@ -40,23 +45,23 @@ func (c *client) ExpandStages(ctx context.Context, s *yaml.Build, tmpls map[stri
s.Environment = p.Environment
}

return s, nil
return s, warnings, nil
}

// ExpandSteps injects the template for each
// templated step in a yaml configuration.
//
//nolint:funlen,gocyclo // ignore function length
func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[string]*yaml.Template, r *pipeline.RuleData, depth int) (*yaml.Build, error) {
func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[string]*yaml.Template, r *pipeline.RuleData, warnings []string, depth int) (*yaml.Build, []string, error) {
if len(tmpls) == 0 {
return s, nil
return s, warnings, nil
}

// return if max template depth has been reached
if depth == 0 {
retErr := fmt.Errorf("max template depth of %d exceeded", c.GetTemplateDepth())

return s, retErr
return s, warnings, retErr
}

steps := yaml.StepSlice{}
Expand All @@ -81,7 +86,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
// lookup step template name
tmpl, ok := tmpls[step.Template.Name]
if !ok {
return s, fmt.Errorf("missing template source for template %s in pipeline for step %s", step.Template.Name, step.Name)
return s, warnings, fmt.Errorf("missing template source for template %s in pipeline for step %s", step.Template.Name, step.Name)
}

// if ruledata is nil (CompileLite), continue with expansion
Expand All @@ -94,7 +99,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin

pipeline, err := pipeline.Purge(r)
if err != nil {
return nil, fmt.Errorf("unable to purge pipeline: %w", err)
return nil, warnings, fmt.Errorf("unable to purge pipeline: %w", err)
}

// if step purged, do not proceed with expansion
Expand All @@ -115,7 +120,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
// inject environment information for template
step, err := c.EnvironmentStep(step, envGlobalSteps)
if err != nil {
return s, err
return s, warnings, err
}

var (
Expand All @@ -126,7 +131,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
if bytes, found = c.TemplateCache[tmpl.Source]; !found {
bytes, err = c.getTemplate(ctx, tmpl, step.Template.Name)
if err != nil {
return s, err
return s, warnings, err
}
}

Expand All @@ -138,23 +143,25 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
// inject template name into variables
step.Template.Variables["VELA_TEMPLATE_NAME"] = step.Template.Name

tmplBuild, _, err := c.mergeTemplate(bytes, tmpl, step)
tmplBuild, tmplWarnings, err := c.mergeTemplate(bytes, tmpl, step)
if err != nil {
return s, err
return s, warnings, err
}

warnings = append(warnings, tmplWarnings...)

// if template references other templates, expand again
if len(tmplBuild.Templates) != 0 {
// if the tmplBuild has render_inline but the parent build does not, abort
if tmplBuild.Metadata.RenderInline && !s.Metadata.RenderInline {
return s, fmt.Errorf("cannot use render_inline inside a called template (%s)", step.Template.Name)
return s, warnings, fmt.Errorf("cannot use render_inline inside a called template (%s)", step.Template.Name)
}

templates = append(templates, tmplBuild.Templates...)

tmplBuild, err = c.ExpandSteps(ctx, tmplBuild, mapFromTemplates(tmplBuild.Templates), r, depth-1)
tmplBuild, warnings, err = c.ExpandSteps(ctx, tmplBuild, mapFromTemplates(tmplBuild.Templates), r, warnings, depth-1)
if err != nil {
return s, err
return s, warnings, err
}
}

Expand Down Expand Up @@ -217,7 +224,7 @@ func (c *client) ExpandSteps(ctx context.Context, s *yaml.Build, tmpls map[strin
s.Environment = environment
s.Templates = templates

return s, nil
return s, warnings, nil
}

// ExpandDeployment injects the template for a
Expand Down
Loading

0 comments on commit cf8aea3

Please sign in to comment.