2017-03-05 07:56:08 +00:00
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-06-04 22:15:07 +00:00
|
|
|
"path"
|
2022-01-17 13:43:30 +00:00
|
|
|
"strings"
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
backend_types "github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
2023-06-04 22:15:07 +00:00
|
|
|
"github.com/woodpecker-ci/woodpecker/pipeline/frontend/metadata"
|
2023-06-06 07:14:21 +00:00
|
|
|
yaml_types "github.com/woodpecker-ci/woodpecker/pipeline/frontend/yaml/types"
|
2022-04-06 13:30:49 +00:00
|
|
|
"github.com/woodpecker-ci/woodpecker/shared/constant"
|
2017-03-05 07:56:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// TODO(bradrydzewski) compiler should handle user-defined volumes from YAML
|
|
|
|
// TODO(bradrydzewski) compiler should handle user-defined networks from YAML
|
|
|
|
|
2022-01-17 13:43:30 +00:00
|
|
|
const (
|
|
|
|
windowsPrefix = "windows/"
|
|
|
|
|
2022-04-06 13:30:49 +00:00
|
|
|
defaultCloneName = "clone"
|
2022-01-17 13:43:30 +00:00
|
|
|
|
|
|
|
networkDriverNAT = "nat"
|
|
|
|
networkDriverBridge = "bridge"
|
2022-05-17 15:26:35 +00:00
|
|
|
|
|
|
|
nameServices = "services"
|
|
|
|
namePipeline = "pipeline"
|
2022-01-17 13:43:30 +00:00
|
|
|
)
|
|
|
|
|
2022-10-03 17:25:43 +00:00
|
|
|
// Registry represents registry credentials
|
2017-04-06 16:04:25 +00:00
|
|
|
type Registry struct {
|
|
|
|
Hostname string
|
|
|
|
Username string
|
|
|
|
Password string
|
|
|
|
Email string
|
|
|
|
Token string
|
|
|
|
}
|
|
|
|
|
2017-04-10 10:39:50 +00:00
|
|
|
type Secret struct {
|
2022-10-27 02:21:07 +00:00
|
|
|
Name string
|
|
|
|
Value string
|
|
|
|
Match []string
|
|
|
|
PluginOnly bool
|
|
|
|
}
|
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
func (s *Secret) Available(container *yaml_types.Container) bool {
|
2022-10-27 02:21:07 +00:00
|
|
|
return (len(s.Match) == 0 || matchImage(container.Image, s.Match...)) && (!s.PluginOnly || container.IsPlugin())
|
2017-04-10 10:39:50 +00:00
|
|
|
}
|
|
|
|
|
2022-10-03 17:25:43 +00:00
|
|
|
type secretMap map[string]Secret
|
|
|
|
|
|
|
|
func (sm secretMap) toStringMap() map[string]string {
|
|
|
|
m := make(map[string]string, len(sm))
|
|
|
|
for k, v := range sm {
|
|
|
|
m[k] = v.Value
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2017-06-22 19:06:28 +00:00
|
|
|
type ResourceLimit struct {
|
|
|
|
MemSwapLimit int64
|
|
|
|
MemLimit int64
|
|
|
|
ShmSize int64
|
|
|
|
CPUQuota int64
|
|
|
|
CPUShares int64
|
|
|
|
CPUSet string
|
|
|
|
}
|
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
// Compiler compiles the yaml
|
|
|
|
type Compiler struct {
|
2022-02-10 16:05:19 +00:00
|
|
|
local bool
|
|
|
|
escalated []string
|
|
|
|
prefix string
|
|
|
|
volumes []string
|
|
|
|
networks []string
|
|
|
|
env map[string]string
|
|
|
|
cloneEnv map[string]string
|
|
|
|
base string
|
|
|
|
path string
|
2023-06-04 22:15:07 +00:00
|
|
|
metadata metadata.Metadata
|
2022-02-10 16:05:19 +00:00
|
|
|
registries []Registry
|
2022-10-03 17:25:43 +00:00
|
|
|
secrets secretMap
|
2022-02-10 16:05:19 +00:00
|
|
|
cacher Cacher
|
|
|
|
reslimit ResourceLimit
|
|
|
|
defaultCloneImage string
|
2023-03-20 20:17:49 +00:00
|
|
|
trustedPipeline bool
|
|
|
|
netrcOnlyTrusted bool
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new Compiler with options.
|
|
|
|
func New(opts ...Option) *Compiler {
|
2017-04-10 10:39:50 +00:00
|
|
|
compiler := &Compiler{
|
2021-11-25 20:30:03 +00:00
|
|
|
env: map[string]string{},
|
|
|
|
cloneEnv: map[string]string{},
|
|
|
|
secrets: map[string]Secret{},
|
2017-04-10 10:39:50 +00:00
|
|
|
}
|
2017-03-05 07:56:08 +00:00
|
|
|
for _, opt := range opts {
|
|
|
|
opt(compiler)
|
|
|
|
}
|
|
|
|
return compiler
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compile compiles the YAML configuration to the pipeline intermediate
|
|
|
|
// representation configuration format.
|
2023-06-06 07:14:21 +00:00
|
|
|
func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, error) {
|
|
|
|
config := new(backend_types.Config)
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2023-07-02 21:45:22 +00:00
|
|
|
if match, err := conf.When.Match(c.metadata, true, c.env); !match && err == nil {
|
2022-09-26 07:27:20 +00:00
|
|
|
// This pipeline does not match the configured filter so return an empty config and stop further compilation.
|
|
|
|
// An empty pipeline will just be skipped completely.
|
2022-10-05 23:49:23 +00:00
|
|
|
return config, nil
|
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
2022-09-26 07:27:20 +00:00
|
|
|
}
|
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
// create a default volume
|
2023-06-06 07:14:21 +00:00
|
|
|
config.Volumes = append(config.Volumes, &backend_types.Volume{
|
2017-03-05 07:56:08 +00:00
|
|
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
|
|
|
Driver: "local",
|
|
|
|
})
|
|
|
|
|
|
|
|
// create a default network
|
2022-05-30 23:12:18 +00:00
|
|
|
if strings.HasPrefix(c.metadata.Sys.Platform, windowsPrefix) {
|
2023-06-06 07:14:21 +00:00
|
|
|
config.Networks = append(config.Networks, &backend_types.Network{
|
2019-04-06 13:44:04 +00:00
|
|
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
2022-01-17 13:43:30 +00:00
|
|
|
Driver: networkDriverNAT,
|
2019-04-06 13:44:04 +00:00
|
|
|
})
|
|
|
|
} else {
|
2023-06-06 07:14:21 +00:00
|
|
|
config.Networks = append(config.Networks, &backend_types.Network{
|
2019-04-06 13:44:04 +00:00
|
|
|
Name: fmt.Sprintf("%s_default", c.prefix),
|
2022-01-17 13:43:30 +00:00
|
|
|
Driver: networkDriverBridge,
|
2019-04-06 13:44:04 +00:00
|
|
|
})
|
|
|
|
}
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2020-11-19 07:42:18 +00:00
|
|
|
// create secrets for mask
|
|
|
|
for _, sec := range c.secrets {
|
2023-06-06 07:14:21 +00:00
|
|
|
config.Secrets = append(config.Secrets, &backend_types.Secret{
|
2020-11-19 07:42:18 +00:00
|
|
|
Name: sec.Name,
|
|
|
|
Value: sec.Value,
|
|
|
|
Mask: true,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
// overrides the default workspace paths when specified
|
|
|
|
// in the YAML file.
|
|
|
|
if len(conf.Workspace.Base) != 0 {
|
|
|
|
c.base = conf.Workspace.Base
|
|
|
|
}
|
|
|
|
if len(conf.Workspace.Path) != 0 {
|
|
|
|
c.path = conf.Workspace.Path
|
|
|
|
}
|
|
|
|
|
2023-03-20 20:17:49 +00:00
|
|
|
cloneImage := constant.DefaultCloneImage
|
|
|
|
if len(c.defaultCloneImage) > 0 {
|
|
|
|
cloneImage = c.defaultCloneImage
|
|
|
|
}
|
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
// add default clone step
|
2023-06-06 07:14:21 +00:00
|
|
|
if !c.local && len(conf.Clone.ContainerList) == 0 && !conf.SkipClone {
|
2022-08-15 09:52:30 +00:00
|
|
|
cloneSettings := map[string]interface{}{"depth": "0"}
|
2023-06-04 22:15:07 +00:00
|
|
|
if c.metadata.Curr.Event == metadata.EventTag {
|
2022-08-15 09:52:30 +00:00
|
|
|
cloneSettings["tags"] = "true"
|
|
|
|
}
|
2023-06-06 07:14:21 +00:00
|
|
|
container := &yaml_types.Container{
|
2022-01-17 13:43:30 +00:00
|
|
|
Name: defaultCloneName,
|
2022-02-10 16:05:19 +00:00
|
|
|
Image: cloneImage,
|
2022-08-15 09:52:30 +00:00
|
|
|
Settings: cloneSettings,
|
2021-11-25 20:30:03 +00:00
|
|
|
Environment: c.cloneEnv,
|
2017-07-19 00:53:10 +00:00
|
|
|
}
|
2017-03-05 07:56:08 +00:00
|
|
|
name := fmt.Sprintf("%s_clone", c.prefix)
|
2022-01-17 13:43:30 +00:00
|
|
|
step := c.createProcess(name, container, defaultCloneName)
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
stage := new(backend_types.Stage)
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Name = name
|
2022-01-17 13:43:30 +00:00
|
|
|
stage.Alias = defaultCloneName
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
|
|
|
|
config.Stages = append(config.Stages, stage)
|
2021-10-16 00:54:28 +00:00
|
|
|
} else if !c.local && !conf.SkipClone {
|
2023-06-06 07:14:21 +00:00
|
|
|
for i, container := range conf.Clone.ContainerList {
|
2023-07-02 21:45:22 +00:00
|
|
|
if match, err := container.When.Match(c.metadata, false, c.env); !match && err == nil {
|
2017-03-05 07:56:08 +00:00
|
|
|
continue
|
2022-10-05 23:49:23 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
2022-10-05 23:49:23 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
stage := new(backend_types.Stage)
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Name = fmt.Sprintf("%s_clone_%v", c.prefix, i)
|
|
|
|
stage.Alias = container.Name
|
|
|
|
|
|
|
|
name := fmt.Sprintf("%s_clone_%d", c.prefix, i)
|
2022-01-17 13:43:30 +00:00
|
|
|
step := c.createProcess(name, container, defaultCloneName)
|
2023-03-20 20:17:49 +00:00
|
|
|
|
|
|
|
// only inject netrc if it's a trusted repo or a trusted plugin
|
|
|
|
if !c.netrcOnlyTrusted || c.trustedPipeline || (container.IsPlugin() && container.IsTrustedCloneImage()) {
|
|
|
|
for k, v := range c.cloneEnv {
|
|
|
|
step.Environment[k] = v
|
|
|
|
}
|
2021-11-25 20:30:03 +00:00
|
|
|
}
|
2023-03-20 20:17:49 +00:00
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
|
|
|
|
config.Stages = append(config.Stages, stage)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-22 19:06:28 +00:00
|
|
|
c.setupCache(conf, config)
|
|
|
|
|
2017-03-05 07:56:08 +00:00
|
|
|
// add services steps
|
2023-06-06 07:14:21 +00:00
|
|
|
if len(conf.Services.ContainerList) != 0 {
|
|
|
|
stage := new(backend_types.Stage)
|
2022-05-17 15:26:35 +00:00
|
|
|
stage.Name = fmt.Sprintf("%s_%s", c.prefix, nameServices)
|
|
|
|
stage.Alias = nameServices
|
2017-03-05 07:56:08 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
for i, container := range conf.Services.ContainerList {
|
2023-07-02 21:45:22 +00:00
|
|
|
if match, err := container.When.Match(c.metadata, false, c.env); !match && err == nil {
|
2017-05-14 17:28:17 +00:00
|
|
|
continue
|
2022-10-05 23:49:23 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
2017-05-14 17:28:17 +00:00
|
|
|
}
|
|
|
|
|
2022-05-17 15:26:35 +00:00
|
|
|
name := fmt.Sprintf("%s_%s_%d", c.prefix, nameServices, i)
|
|
|
|
step := c.createProcess(name, container, nameServices)
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
}
|
|
|
|
config.Stages = append(config.Stages, stage)
|
|
|
|
}
|
|
|
|
|
|
|
|
// add pipeline steps. 1 pipeline step per stage, at the moment
|
2023-06-06 07:14:21 +00:00
|
|
|
var stage *backend_types.Stage
|
2017-03-05 07:56:08 +00:00
|
|
|
var group string
|
2023-06-06 07:14:21 +00:00
|
|
|
for i, container := range conf.Steps.ContainerList {
|
2022-01-05 20:50:23 +00:00
|
|
|
// Skip if local and should not run local
|
2022-08-14 17:32:49 +00:00
|
|
|
if c.local && !container.When.IsLocal() {
|
2017-05-06 01:15:47 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-07-02 21:45:22 +00:00
|
|
|
if match, err := container.When.Match(c.metadata, false, c.env); !match && err == nil {
|
2017-03-05 07:56:08 +00:00
|
|
|
continue
|
2022-10-05 23:49:23 +00:00
|
|
|
} else if err != nil {
|
|
|
|
return nil, err
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if stage == nil || group != container.Group || container.Group == "" {
|
|
|
|
group = container.Group
|
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
stage = new(backend_types.Stage)
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Name = fmt.Sprintf("%s_stage_%v", c.prefix, i)
|
|
|
|
stage.Alias = container.Name
|
2022-05-20 03:20:17 +00:00
|
|
|
config.Stages = append(config.Stages, stage)
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
name := fmt.Sprintf("%s_step_%d", c.prefix, i)
|
2022-05-17 15:26:35 +00:00
|
|
|
step := c.createProcess(name, container, namePipeline)
|
2017-03-05 07:56:08 +00:00
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
}
|
|
|
|
|
2017-06-22 19:06:28 +00:00
|
|
|
c.setupCacheRebuild(conf, config)
|
|
|
|
|
2022-10-05 23:49:23 +00:00
|
|
|
return config, nil
|
2017-03-05 07:56:08 +00:00
|
|
|
}
|
2017-06-22 19:06:28 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
func (c *Compiler) setupCache(conf *yaml_types.Workflow, ir *backend_types.Config) {
|
2017-06-22 19:06:28 +00:00
|
|
|
if c.local || len(conf.Cache) == 0 || c.cacher == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-04 22:15:07 +00:00
|
|
|
container := c.cacher.Restore(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
2017-06-22 19:06:28 +00:00
|
|
|
name := fmt.Sprintf("%s_restore_cache", c.prefix)
|
2017-07-21 21:52:52 +00:00
|
|
|
step := c.createProcess(name, container, "cache")
|
2017-06-22 19:06:28 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
stage := new(backend_types.Stage)
|
2017-06-22 19:06:28 +00:00
|
|
|
stage.Name = name
|
|
|
|
stage.Alias = "restore_cache"
|
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
|
|
|
|
ir.Stages = append(ir.Stages, stage)
|
|
|
|
}
|
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
func (c *Compiler) setupCacheRebuild(conf *yaml_types.Workflow, ir *backend_types.Config) {
|
2023-06-04 22:15:07 +00:00
|
|
|
if c.local || len(conf.Cache) == 0 || c.metadata.Curr.Event != metadata.EventPush || c.cacher == nil {
|
2017-06-22 19:06:28 +00:00
|
|
|
return
|
|
|
|
}
|
2023-06-04 22:15:07 +00:00
|
|
|
container := c.cacher.Rebuild(path.Join(c.metadata.Repo.Owner, c.metadata.Repo.Name), c.metadata.Curr.Commit.Branch, conf.Cache)
|
2017-06-22 19:06:28 +00:00
|
|
|
|
|
|
|
name := fmt.Sprintf("%s_rebuild_cache", c.prefix)
|
2017-07-21 21:52:52 +00:00
|
|
|
step := c.createProcess(name, container, "cache")
|
2017-06-22 19:06:28 +00:00
|
|
|
|
2023-06-06 07:14:21 +00:00
|
|
|
stage := new(backend_types.Stage)
|
2017-06-22 19:06:28 +00:00
|
|
|
stage.Name = name
|
|
|
|
stage.Alias = "rebuild_cache"
|
|
|
|
stage.Steps = append(stage.Steps, step)
|
|
|
|
|
|
|
|
ir.Stages = append(ir.Stages, stage)
|
|
|
|
}
|