Add support for steps to be a list (instead of dict) (#826)

- Support for pipeline/containers as list
- Support for container name in logs (step.Name)

Co-authored-by: Zav Shotan <zshotan@bloomberg.net>
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
Zav Shotan 2022-06-13 17:13:09 -04:00 committed by GitHub
parent cdbba4c306
commit 81dcdea2be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 26 deletions

View file

@ -145,8 +145,15 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section
cpuSet = c.reslimit.CPUSet cpuSet = c.reslimit.CPUSet
} }
stepName := container.Name
if len(stepName) == 0 {
stepName = name
} else {
stepName = section + "." + stepName
}
return &backend.Step{ return &backend.Step{
Name: name, Name: stepName,
Alias: container.Name, Alias: container.Name,
Image: image, Image: image,
Pull: container.Pull, Pull: container.Pull,

View file

@ -65,17 +65,18 @@ type (
// UnmarshalYAML implements the Unmarshaler interface. // UnmarshalYAML implements the Unmarshaler interface.
func (c *Containers) UnmarshalYAML(value *yaml.Node) error { func (c *Containers) UnmarshalYAML(value *yaml.Node) error {
containers := map[string]Container{} switch value.Kind {
err := value.Decode(&containers) // We support mapps ...
if err != nil { case yaml.MappingNode:
return err c.Containers = make([]*Container, 0, len(value.Content)/2+1)
} // We cannot use decode on specific values
// since if we try to load from a map, the order
// will not be kept. Therefore use value.Content
// and take the map values i%2=1
for i, n := range value.Content { for i, n := range value.Content {
if i%2 == 1 { if i%2 == 1 {
container := Container{} container := &Container{}
err := n.Decode(&container) if err := n.Decode(container); err != nil {
if err != nil {
return err return err
} }
@ -83,9 +84,29 @@ func (c *Containers) UnmarshalYAML(value *yaml.Node) error {
container.Name = fmt.Sprintf("%v", value.Content[i-1].Value) container.Name = fmt.Sprintf("%v", value.Content[i-1].Value)
} }
c.Containers = append(c.Containers, &container) c.Containers = append(c.Containers, container)
} }
} }
// ... and lists
case yaml.SequenceNode:
c.Containers = make([]*Container, 0, len(value.Content))
for i, n := range value.Content {
container := &Container{}
if err := n.Decode(container); err != nil {
return err
}
if container.Name == "" {
container.Name = fmt.Sprintf("step-%d", i)
}
c.Containers = append(c.Containers, container)
}
default:
return fmt.Errorf("yaml node type[%d]: '%s' not supported", value.Kind, value.Tag)
}
return nil return nil
} }

View file

@ -7,7 +7,7 @@ import (
) )
func TestLint(t *testing.T) { func TestLint(t *testing.T) {
testdata := ` testdatas := []struct{ Title, Data string }{{Title: "map", Data: `
pipeline: pipeline:
build: build:
image: docker image: docker
@ -28,15 +28,35 @@ services:
image: redis image: redis
entrypoint: [ /bin/redis-server ] entrypoint: [ /bin/redis-server ]
command: [ -v ] command: [ -v ]
` `}, {Title: "list", Data: `
pipeline:
- name: build
image: docker
privileged: true
network_mode: host
volumes:
- /tmp:/tmp
commands:
- go build
- go test
- name: publish
image: plugins/docker
repo: foo/bar
settings:
foo: bar
`}}
conf, err := yaml.ParseString(testdata) for _, testd := range testdatas {
t.Run(testd.Title, func(t *testing.T) {
conf, err := yaml.ParseString(testd.Data)
if err != nil { if err != nil {
t.Fatalf("Cannot unmarshal yaml %q. Error: %s", testdata, err) t.Fatalf("Cannot unmarshal yaml %q. Error: %s", testd, err)
} }
if err := New(WithTrusted(true)).Lint(conf); err != nil { if err := New(WithTrusted(true)).Lint(conf); err != nil {
t.Errorf("Expected lint returns no errors, got %q", err) t.Errorf("Expected lint returns no errors, got %q", err)
} }
})
}
} }
func TestLintErrors(t *testing.T) { func TestLintErrors(t *testing.T) {

View file

@ -90,11 +90,10 @@
}, },
"pipeline": { "pipeline": {
"description": "The pipeline section defines a list of steps which will be executed serially, in the order in which they are defined. Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax", "description": "The pipeline section defines a list of steps which will be executed serially, in the order in which they are defined. Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax",
"type": "object", "oneOf": [
"additionalProperties": { { "type": "object", "additionalProperties": { "$ref": "#/definitions/step" }, "minProperties": 1 },
"$ref": "#/definitions/step" { "type": "array", "items": { "$ref": "#/definitions/step" }, "minLength": 1 }
}, ]
"minProperties": 1
}, },
"step": { "step": {
"description": "Every step of your pipeline executes arbitrary commands inside a specified docker container. Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax#steps", "description": "Every step of your pipeline executes arbitrary commands inside a specified docker container. Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax#steps",