woodpecker/engine/compiler/compile.go

146 lines
3.6 KiB
Go

package compiler
import (
"github.com/drone/drone/engine/runner"
"github.com/drone/drone/engine/runner/parse"
yaml "github.com/drone/drone/engine/compiler/parse"
)
// Compiler compiles the Yaml file to the intermediate representation.
type Compiler struct {
trans []Transform
}
func New() *Compiler {
return &Compiler{}
}
// Transforms sets the compiler transforms use to transform the intermediate
// representation during compilation.
func (c *Compiler) Transforms(trans []Transform) *Compiler {
c.trans = append(c.trans, trans...)
return c
}
// CompileString compiles the Yaml configuration string and returns
// the intermediate representation for the interpreter.
func (c *Compiler) CompileString(in string) (*runner.Spec, error) {
return c.Compile([]byte(in))
}
// CompileString compiles the Yaml configuration file and returns
// the intermediate representation for the interpreter.
func (c *Compiler) Compile(in []byte) (*runner.Spec, error) {
root, err := yaml.Parse(in)
if err != nil {
return nil, err
}
if err := root.Walk(c.walk); err != nil {
return nil, err
}
config := &runner.Spec{}
tree := parse.NewTree()
// pod section
if root.Pod != nil {
node, ok := root.Pod.(*yaml.ContainerNode)
if ok {
config.Containers = append(config.Containers, &node.Container)
tree.Append(parse.NewRunNode().SetName(node.Container.Name).SetDetach(true))
}
}
// cache section
if root.Cache != nil {
node, ok := root.Cache.(*yaml.ContainerNode)
if ok && !node.Disabled {
config.Containers = append(config.Containers, &node.Container)
tree.Append(parse.NewRunNode().SetName(node.Container.Name))
}
}
// clone section
if root.Clone != nil {
node, ok := root.Clone.(*yaml.ContainerNode)
if ok && !node.Disabled {
config.Containers = append(config.Containers, &node.Container)
tree.Append(parse.NewRunNode().SetName(node.Container.Name))
}
}
// services section
for _, container := range root.Services {
node, ok := container.(*yaml.ContainerNode)
if !ok || node.Disabled {
continue
}
config.Containers = append(config.Containers, &node.Container)
tree.Append(parse.NewRunNode().SetName(node.Container.Name).SetDetach(true))
}
// pipeline section
for i, container := range root.Script {
node, ok := container.(*yaml.ContainerNode)
if !ok || node.Disabled {
continue
}
config.Containers = append(config.Containers, &node.Container)
// step 1: lookahead to see if any status=failure exist
list := parse.NewListNode()
for ii, next := range root.Script {
if i >= ii {
continue
}
node, ok := next.(*yaml.ContainerNode)
if !ok || node.Disabled || !node.OnFailure() {
continue
}
list.Append(
parse.NewRecoverNode().SetBody(
parse.NewRunNode().SetName(
node.Container.Name,
),
),
)
}
// step 2: if yes, collect these and append to "error" node
if len(list.Body) == 0 {
tree.Append(parse.NewRunNode().SetName(node.Container.Name))
} else {
errorNode := parse.NewErrorNode()
errorNode.SetBody(parse.NewRunNode().SetName(node.Container.Name))
errorNode.SetDefer(list)
tree.Append(errorNode)
}
}
config.Nodes = tree
return config, nil
}
func (c *Compiler) walk(node yaml.Node) (err error) {
for _, trans := range c.trans {
switch v := node.(type) {
case *yaml.BuildNode:
err = trans.VisitBuild(v)
case *yaml.ContainerNode:
err = trans.VisitContainer(v)
case *yaml.NetworkNode:
err = trans.VisitNetwork(v)
case *yaml.VolumeNode:
err = trans.VisitVolume(v)
case *yaml.RootNode:
err = trans.VisitRoot(v)
}
if err != nil {
break
}
}
return err
}