mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2026-03-16 01:19:02 +01:00
Improve service syntax related docs and tests nits (#5991)
This commit is contained in:
@@ -46,8 +46,8 @@ Service containers generally expose environment variables to customize service s
|
||||
- name: database
|
||||
image: mysql
|
||||
+ environment:
|
||||
+ - MYSQL_DATABASE=test
|
||||
+ - MYSQL_ALLOW_EMPTY_PASSWORD=yes
|
||||
+ MYSQL_DATABASE: test
|
||||
+ MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
|
||||
- name: cache
|
||||
image: redis
|
||||
@@ -102,8 +102,8 @@ services:
|
||||
- name: database
|
||||
image: mysql
|
||||
environment:
|
||||
- MYSQL_DATABASE=test
|
||||
- MYSQL_ROOT_PASSWORD=example
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_ROOT_PASSWORD: example
|
||||
steps:
|
||||
- name: get-version
|
||||
image: ubuntu
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/pipeline/errors"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/yaml"
|
||||
@@ -185,7 +186,7 @@ func TestLintErrors(t *testing.T) {
|
||||
|
||||
for _, test := range testdata {
|
||||
conf, err := yaml.ParseString(test.from)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
lerr := linter.New().Lint([]*linter.WorkflowConfig{{
|
||||
File: test.from,
|
||||
|
||||
@@ -8,6 +8,14 @@ steps:
|
||||
services:
|
||||
database:
|
||||
image: mysql
|
||||
ports:
|
||||
- 3306
|
||||
environment:
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
|
||||
|
||||
cache:
|
||||
image: redis
|
||||
directory: /tmp/
|
||||
ports:
|
||||
- '6379'
|
||||
|
||||
@@ -183,7 +183,6 @@ steps:
|
||||
- go test
|
||||
build:
|
||||
image: golang
|
||||
network_mode: container:name
|
||||
commands:
|
||||
- go build
|
||||
when:
|
||||
@@ -191,7 +190,8 @@ steps:
|
||||
depends_on: []
|
||||
notify:
|
||||
image: slack
|
||||
channel: dev
|
||||
settings:
|
||||
channel: dev
|
||||
when:
|
||||
event: failure
|
||||
services:
|
||||
@@ -252,7 +252,7 @@ func TestReSerialize(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
workBin, err := yaml.Marshal(work1)
|
||||
work1Bin, err := yaml.Marshal(work1)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fail()
|
||||
}
|
||||
@@ -282,7 +282,58 @@ func TestReSerialize(t *testing.T) {
|
||||
DRIVER: next
|
||||
PLATFORM: linux
|
||||
skip_clone: false
|
||||
`, string(workBin))
|
||||
`, string(work1Bin))
|
||||
|
||||
work2, err := ParseString(sampleYaml)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
workBin2, err := yaml.Marshal(work2)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
// TODO: fix "steps.[1].depends_on: []" to be re-serialized!
|
||||
assert.EqualValues(t, `when:
|
||||
- event:
|
||||
- tester
|
||||
- tester2
|
||||
- branch: tester
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/github.com/octocat/hello-world
|
||||
steps:
|
||||
- name: test
|
||||
image: golang
|
||||
commands:
|
||||
- go install
|
||||
- go test
|
||||
- name: build
|
||||
image: golang
|
||||
commands: go build
|
||||
when:
|
||||
event: push
|
||||
- name: notify
|
||||
image: slack
|
||||
settings:
|
||||
channel: dev
|
||||
when:
|
||||
event: failure
|
||||
services:
|
||||
- name: database
|
||||
image: mysql
|
||||
labels:
|
||||
com.example.team: frontend
|
||||
com.example.type: build
|
||||
depends_on:
|
||||
- lint
|
||||
- test
|
||||
runs_on:
|
||||
- success
|
||||
- failure
|
||||
skip_clone: false
|
||||
`, string(workBin2))
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
|
||||
@@ -15,110 +15,46 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/yaml/constraint"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/yaml/types/base"
|
||||
"go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/yaml/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
// ContainerList denotes an ordered collection of containers.
|
||||
ContainerList struct {
|
||||
ContainerList []*Container
|
||||
}
|
||||
// Container defines a container.
|
||||
type Container struct {
|
||||
// common
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Image string `yaml:"image,omitempty"`
|
||||
Pull bool `yaml:"pull,omitempty"`
|
||||
Commands base.StringOrSlice `yaml:"commands,omitempty"`
|
||||
Entrypoint base.StringOrSlice `yaml:"entrypoint,omitempty"`
|
||||
Directory string `yaml:"directory,omitempty"`
|
||||
Settings map[string]any `yaml:"settings,omitempty"`
|
||||
Environment map[string]any `yaml:"environment,omitempty"`
|
||||
// flow control
|
||||
DependsOn base.StringOrSlice `yaml:"depends_on,omitempty"`
|
||||
When constraint.When `yaml:"when,omitempty"`
|
||||
Failure string `yaml:"failure,omitempty"`
|
||||
Detached bool `yaml:"detach,omitempty"`
|
||||
// state
|
||||
Volumes Volumes `yaml:"volumes,omitempty"`
|
||||
// network
|
||||
Ports []string `yaml:"ports,omitempty"`
|
||||
DNS base.StringOrSlice `yaml:"dns,omitempty"`
|
||||
DNSSearch base.StringOrSlice `yaml:"dns_search,omitempty"`
|
||||
// backend specific
|
||||
BackendOptions map[string]any `yaml:"backend_options,omitempty"`
|
||||
|
||||
// Container defines a container.
|
||||
Container struct {
|
||||
// common
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Image string `yaml:"image,omitempty"`
|
||||
Pull bool `yaml:"pull,omitempty"`
|
||||
Commands base.StringOrSlice `yaml:"commands,omitempty"`
|
||||
Entrypoint base.StringOrSlice `yaml:"entrypoint,omitempty"`
|
||||
Directory string `yaml:"directory,omitempty"`
|
||||
Settings map[string]any `yaml:"settings,omitempty"`
|
||||
Environment map[string]any `yaml:"environment,omitempty"`
|
||||
// flow control
|
||||
DependsOn base.StringOrSlice `yaml:"depends_on,omitempty"`
|
||||
When constraint.When `yaml:"when,omitempty"`
|
||||
Failure string `yaml:"failure,omitempty"`
|
||||
Detached bool `yaml:"detach,omitempty"`
|
||||
// state
|
||||
Volumes Volumes `yaml:"volumes,omitempty"`
|
||||
// network
|
||||
Ports []string `yaml:"ports,omitempty"`
|
||||
DNS base.StringOrSlice `yaml:"dns,omitempty"`
|
||||
DNSSearch base.StringOrSlice `yaml:"dns_search,omitempty"`
|
||||
// backend specific
|
||||
BackendOptions map[string]any `yaml:"backend_options,omitempty"`
|
||||
// ACTIVE DEVELOPMENT BELOW
|
||||
|
||||
// ACTIVE DEVELOPMENT BELOW
|
||||
// Docker and Kubernetes Specific
|
||||
Privileged bool `yaml:"privileged,omitempty"`
|
||||
|
||||
// Docker and Kubernetes Specific
|
||||
Privileged bool `yaml:"privileged,omitempty"`
|
||||
|
||||
// Undocumented
|
||||
Devices []string `yaml:"devices,omitempty"`
|
||||
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
|
||||
NetworkMode string `yaml:"network_mode,omitempty"`
|
||||
Tmpfs []string `yaml:"tmpfs,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaler interface.
|
||||
func (c *ContainerList) UnmarshalYAML(value *yaml.Node) error {
|
||||
switch value.Kind {
|
||||
// We support maps ...
|
||||
case yaml.MappingNode:
|
||||
c.ContainerList = 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. Therefor use value.Content
|
||||
// and take the map values i%2=1
|
||||
for i, n := range value.Content {
|
||||
if i%2 == 1 {
|
||||
container := &Container{}
|
||||
if err := n.Decode(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if container.Name == "" {
|
||||
container.Name = fmt.Sprintf("%v", value.Content[i-1].Value)
|
||||
}
|
||||
|
||||
c.ContainerList = append(c.ContainerList, container)
|
||||
}
|
||||
}
|
||||
|
||||
// ... and lists
|
||||
case yaml.SequenceNode:
|
||||
c.ContainerList = 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.ContainerList = append(c.ContainerList, container)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("yaml node type[%d]: '%s' not supported", value.Kind, value.Tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements custom Yaml marshaling.
|
||||
func (c ContainerList) MarshalYAML() (any, error) {
|
||||
return c.ContainerList, nil
|
||||
// Undocumented
|
||||
Devices []string `yaml:"devices,omitempty"`
|
||||
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
|
||||
NetworkMode string `yaml:"network_mode,omitempty"`
|
||||
Tmpfs []string `yaml:"tmpfs,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Container) IsPlugin() bool {
|
||||
|
||||
79
pipeline/frontend/yaml/types/container_list.go
Normal file
79
pipeline/frontend/yaml/types/container_list.go
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2026 Woodpecker Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// ContainerList contains ordered collection of containers.
|
||||
type ContainerList struct {
|
||||
ContainerList []*Container
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the Unmarshaler interface.
|
||||
func (c *ContainerList) UnmarshalYAML(value *yaml.Node) error {
|
||||
switch value.Kind {
|
||||
// We support maps ...
|
||||
case yaml.MappingNode:
|
||||
c.ContainerList = 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. Therefor use value.Content
|
||||
// and take the map values i%2=1
|
||||
for i, n := range value.Content {
|
||||
if i%2 == 1 {
|
||||
container := &Container{}
|
||||
if err := n.Decode(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if container.Name == "" {
|
||||
container.Name = fmt.Sprintf("%v", value.Content[i-1].Value)
|
||||
}
|
||||
|
||||
c.ContainerList = append(c.ContainerList, container)
|
||||
}
|
||||
}
|
||||
|
||||
// ... and lists
|
||||
case yaml.SequenceNode:
|
||||
c.ContainerList = 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.ContainerList = append(c.ContainerList, container)
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("yaml node type[%d]: '%s' not supported", value.Kind, value.Tag)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements custom Yaml marshaling.
|
||||
func (c ContainerList) MarshalYAML() (any, error) {
|
||||
return c.ContainerList, nil
|
||||
}
|
||||
Reference in New Issue
Block a user