Remove old pipeline options (#4016)

This commit is contained in:
qwerty287 2024-08-15 18:58:51 +02:00 committed by GitHub
parent 8251ec198b
commit aafd217cce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 100 additions and 574 deletions

View file

@ -6,15 +6,15 @@ Some versions need some changes to the server configuration or the pipeline conf
- Removed `WOODPECKER_DEV_OAUTH_HOST` and `WOODPECKER_DEV_GITEA_OAUTH_URL` use `WOODPECKER_EXPERT_FORGE_OAUTH_HOST`
- Compatibility mode of deprecated `pipeline:`, `platform:` and `branches:` pipeline config options are now removed and pipeline will now fail if still in use.
- Deprecated `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
- Removed `steps.[name].group` in favor of `steps.[name].depends_on` (see [workflow syntax](./20-usage/20-workflow-syntax.md#depends_on) to learn how to set dependencies)
- Removed `WOODPECKER_ROOT_PATH` and `WOODPECKER_ROOT_URL` config variables. Use `WOODPECKER_HOST` with a path instead
- Pipelines without a config file will now be skipped instead of failing
- Removed implicitly defined `regcred` image pull secret name. Set it explicitly via `WOODPECKER_BACKEND_K8S_PULL_SECRET_NAMES`
- Deprecated `includes` and `excludes` support from **event** filter
- Deprecated uppercasing all secret env vars, instead, the value of the `secrets` property is used. [Read more](./20-usage/40-secrets.md#use-secrets-in-commands)
- Deprecated alternative names for secrets, use `environment` with `from_secret`
- Deprecated slice definition for env vars
- Deprecated `environment` filter, use `when.evaluate`
- Removed `includes` and `excludes` support from **event** filter
- Removed uppercasing all secret env vars, instead, the value of the `secrets` property is used. [Read more](./20-usage/40-secrets.md#use-secrets-in-commands)
- Removed alternative names for secrets, use `environment` with `from_secret`
- Removed slice definition for env vars
- Removed `environment` filter, use `when.evaluate`
- Removed `WOODPECKER_WEBHOOK_HOST` in favor of `WOODPECKER_EXPERT_WEBHOOK_HOST`
- Migrated to rfc9421 for webhook signatures
- Renamed `start_time`, `end_time`, `created_at`, `started_at`, `finished_at` and `reviewed_at` JSON fields to `started`, `finished`, `created`, `started`, `finished`, `reviewed`

View file

@ -275,7 +275,6 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
step: step,
position: pos,
name: container.Name,
group: container.Group,
dependsOn: container.DependsOn,
})
}

View file

@ -150,63 +150,65 @@ func TestCompilerCompile(t *testing.T) {
},
},
{
name: "workflow with three steps and one group",
name: "workflow with three steps",
fronConf: &yaml_types.Workflow{Steps: yaml_types.ContainerList{ContainerList: []*yaml_types.Container{{
Name: "echo env",
Image: "bash",
Commands: []string{"env"},
}, {
Name: "parallel echo 1",
Group: "parallel",
Image: "bash",
Commands: []string{"echo 1"},
}, {
Name: "parallel echo 2",
Group: "parallel",
Image: "bash",
Commands: []string{"echo 2"},
}}}},
backConf: &backend_types.Config{
Networks: defaultNetworks,
Volumes: defaultVolumes,
Stages: []*backend_types.Stage{defaultCloneStage, {
Steps: []*backend_types.Step{{
Name: "echo env",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"env"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
ExtraHosts: []backend_types.HostAlias{},
}},
}, {
Steps: []*backend_types.Step{{
Name: "parallel echo 1",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"echo 1"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
ExtraHosts: []backend_types.HostAlias{},
Stages: []*backend_types.Stage{
defaultCloneStage, {
Steps: []*backend_types.Step{{
Name: "echo env",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"env"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
ExtraHosts: []backend_types.HostAlias{},
}},
}, {
Name: "parallel echo 2",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"echo 2"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
ExtraHosts: []backend_types.HostAlias{},
}},
}},
Steps: []*backend_types.Step{{
Name: "parallel echo 1",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"echo 1"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
ExtraHosts: []backend_types.HostAlias{},
}},
}, {
Steps: []*backend_types.Step{{
Name: "parallel echo 2",
Type: backend_types.StepTypeCommands,
Image: "bash",
Commands: []string{"echo 2"},
OnSuccess: true,
Failure: "fail",
Volumes: []string{defaultVolumes[0].Name + ":/test"},
WorkingDir: "/test/src/github.com/octocat/hello-world",
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
ExtraHosts: []backend_types.HostAlias{},
}},
},
},
},
},
{
@ -274,7 +276,7 @@ func TestCompilerCompile(t *testing.T) {
Name: "step",
Image: "bash",
Commands: []string{"env"},
Secrets: yaml_types.Secrets{Secrets: []*yaml_types.Secret{{Source: "missing", Target: "missing"}}},
Secrets: []string{"missing"},
}}}},
backConf: nil,
expectedErr: "secret \"missing\" not found",

View file

@ -125,20 +125,17 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
return nil, err
}
for _, requested := range container.Secrets.Secrets {
secretValue, err := getSecretValue(requested.Source)
for _, requested := range container.Secrets {
secretValue, err := getSecretValue(requested)
if err != nil {
return nil, err
}
toUpperTarget := strings.ToUpper(requested.Target)
if !environmentAllowed(toUpperTarget, stepType) {
if !environmentAllowed(requested, stepType) {
continue
}
environment[requested.Target] = secretValue
// TODO: deprecated, remove in 3.x
environment[toUpperTarget] = secretValue
environment[requested] = secretValue
}
if utils.MatchImage(container.Image, c.escalated...) && container.IsPlugin() {

View file

@ -24,7 +24,6 @@ type dagCompilerStep struct {
step *backend_types.Step
position int
name string
group string
dependsOn []string
}
@ -51,25 +50,16 @@ func (c dagCompiler) compile() ([]*backend_types.Stage, error) {
if c.isDAG() {
return c.compileByDependsOn()
}
return c.compileByGroup()
return c.compileSequence()
}
func (c dagCompiler) compileByGroup() ([]*backend_types.Stage, error) {
func (c dagCompiler) compileSequence() ([]*backend_types.Stage, error) {
stages := make([]*backend_types.Stage, 0, len(c.steps))
var currentStage *backend_types.Stage
var currentGroup string
for _, s := range c.steps {
// create a new stage if current step is in a new group compared to last one
if currentStage == nil || currentGroup != s.group || s.group == "" {
currentGroup = s.group
currentStage = new(backend_types.Stage)
stages = append(stages, currentStage)
}
// add step to current stage
currentStage.Steps = append(currentStage.Steps, s.step)
stages = append(stages, &backend_types.Stage{
Steps: []*backend_types.Step{s.step},
})
}
return stages, nil

View file

@ -85,7 +85,6 @@ func TestConvertDAGToStages(t *testing.T) {
"echo env": {
position: 0,
name: "echo env",
group: "",
step: &backend_types.Step{
UUID: "01HJDPEW6R7J0JBE3F1T7Q0TYX",
Type: "commands",
@ -96,7 +95,6 @@ func TestConvertDAGToStages(t *testing.T) {
"echo 1": {
position: 1,
name: "echo 1",
group: "",
dependsOn: []string{"echo env", "echo 2"},
step: &backend_types.Step{
UUID: "01HJDPF770QGRZER8RF79XVS4M",
@ -108,7 +106,6 @@ func TestConvertDAGToStages(t *testing.T) {
"echo 2": {
position: 2,
name: "echo 2",
group: "",
step: &backend_types.Step{
UUID: "01HJDPFF5RMEYZW0YTGR1Y1ZR0",
Type: "commands",

View file

@ -18,6 +18,7 @@ import (
"fmt"
"maps"
"path"
"slices"
"strings"
"github.com/bmatcuk/doublestar/v4"
@ -37,20 +38,18 @@ type (
}
Constraint struct {
Ref List
Repo List
Instance List
Platform List
Environment List
Branch List
Cron List
Status List
Matrix Map
Local yamlBaseTypes.BoolTrue
Path Path
Evaluate string `yaml:"evaluate,omitempty"`
// TODO: change to StringOrSlice in 3.x
Event List
Ref List
Repo List
Instance List
Platform List
Branch List
Cron List
Status List
Matrix Map
Local yamlBaseTypes.BoolTrue
Path Path
Evaluate string `yaml:"evaluate,omitempty"`
Event yamlBaseTypes.StringOrSlice
}
// List defines a runtime constraint for exclude & include string slices.
@ -164,8 +163,7 @@ func (c *Constraint) Match(m metadata.Metadata, global bool, env map[string]stri
}
match = match && c.Platform.Match(m.Sys.Platform) &&
c.Environment.Match(m.Curr.DeployTo) &&
c.Event.Match(m.Curr.Event) &&
(len(c.Event) == 0 || slices.Contains(c.Event, m.Curr.Event)) &&
c.Repo.Match(path.Join(m.Repo.Owner, m.Repo.Name)) &&
c.Ref.Match(m.Curr.Commit.Ref) &&
c.Instance.Match(m.Sys.Host)

View file

@ -145,7 +145,7 @@ func (l *Linter) lintSettings(config *WorkflowConfig, c *types.Container, field
if len(c.Environment) != 0 {
return newLinterError("Should not configure both environment and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
}
if len(c.Secrets.Secrets) != 0 {
if len(c.Secrets) != 0 {
return newLinterError("Should not configure both secrets and settings", config.File, fmt.Sprintf("%s.%s", field, c.Name), true)
}
return nil
@ -218,102 +218,6 @@ func (l *Linter) lintDeprecations(config *WorkflowConfig) (err error) {
return err
}
for _, step := range parsed.Steps.ContainerList {
if step.Group != "" {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "Please use depends_on instead of deprecated 'group' setting",
Data: errors.DeprecationErrorData{
File: config.File,
Field: "steps." + step.Name + ".group",
Docs: "https://woodpecker-ci.org/docs/next/usage/workflow-syntax#depends_on",
},
IsWarning: true,
})
}
}
for i, c := range parsed.When.Constraints {
if len(c.Event.Exclude) != 0 {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "Please only use allow lists for events",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("when[%d].event", i),
Docs: "https://woodpecker-ci.org/docs/usage/workflow-syntax#event-1",
},
IsWarning: true,
})
}
}
for _, step := range parsed.Steps.ContainerList {
for i, c := range step.When.Constraints {
if len(c.Event.Exclude) != 0 {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "Please only use allow lists for events",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("steps.%s.when[%d].event", step.Name, i),
Docs: "https://woodpecker-ci.org/docs/usage/workflow-syntax#event",
},
IsWarning: true,
})
}
}
}
for _, step := range parsed.Steps.ContainerList {
for i, c := range step.Secrets.Secrets {
if c.Source != c.Target {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "Secrets alternative names are deprecated, use environment with from_secret",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("steps.%s.secrets[%d]", step.Name, i),
Docs: "https://woodpecker-ci.org/docs/usage/secrets#use-secrets-in-settings-and-environment",
},
IsWarning: true,
})
}
}
}
for i, c := range parsed.When.Constraints {
if !c.Environment.IsEmpty() {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "environment filters are deprecated, use evaluate with CI_PIPELINE_DEPLOY_TARGET",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("when[%d].environment", i),
Docs: "https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate",
},
IsWarning: true,
})
}
}
for _, step := range parsed.Steps.ContainerList {
for i, c := range step.When.Constraints {
if !c.Environment.IsEmpty() {
err = multierr.Append(err, &errorTypes.PipelineError{
Type: errorTypes.PipelineErrorTypeDeprecation,
Message: "environment filters are deprecated, use evaluate with CI_PIPELINE_DEPLOY_TARGET",
Data: errors.DeprecationErrorData{
File: config.File,
Field: fmt.Sprintf("steps.%s.when[%d].environment", step.Name, i),
Docs: "https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate",
},
IsWarning: true,
})
}
}
}
return err
}
@ -326,7 +230,7 @@ func (l *Linter) lintBadHabits(config *WorkflowConfig) (err error) {
rootEventFilters := len(parsed.When.Constraints) > 0
for _, c := range parsed.When.Constraints {
if len(c.Event.Include) == 0 {
if len(c.Event) == 0 {
rootEventFilters = false
break
}
@ -340,7 +244,7 @@ func (l *Linter) lintBadHabits(config *WorkflowConfig) (err error) {
} else {
stepEventIndex := -1
for i, c := range step.When.Constraints {
if len(c.Event.Include) == 0 {
if len(c.Event) == 0 {
stepEventIndex = i
break
}

View file

@ -162,7 +162,7 @@ func TestLintErrors(t *testing.T) {
want: "Cannot configure both entrypoint and settings",
},
{
from: "steps: { build: { image: golang, settings: { test: 'true' }, environment: [ 'TEST=true' ] } }",
from: "steps: { build: { image: golang, settings: { test: 'true' }, environment: { 'TEST': 'true' } } }",
want: "Should not configure both environment and settings",
},
{

View file

@ -38,15 +38,6 @@ steps:
commands:
- go test
environment-array:
image: golang
environment:
- CGO=0
- GOOS=linux
- GOARCH=amd64
commands:
- go test
secrets:
image: docker
commands:
@ -54,14 +45,7 @@ steps:
- echo $DOCKER_PASSWORD
secrets:
- docker_username
- source: docker_prod_password
target: docker_password
group:
group: test
image: golang
commands:
- go test
- docker_prod_password
detached:
image: redis

View file

@ -37,14 +37,6 @@ steps:
- deployment
- release
when-event-exclude-pull_request_closed:
image: alpine
commands:
- echo "test"
when:
event:
exclude: pull_request_closed
when-ref:
image: alpine
commands:
@ -78,7 +70,6 @@ steps:
commands:
- echo "test"
when:
environment: production
event: deployment
when-matrix:

View file

@ -163,18 +163,7 @@
"event": {
"description": "Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#event",
"default": [],
"oneOf": [
{
"type": "array",
"minLength": 1,
"items": {
"$ref": "#/definitions/event_enum"
}
},
{
"$ref": "#/definitions/event_enum"
}
]
"$ref": "#/definitions/event_constraint_list"
},
"ref": {
"description": "Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#ref",
@ -188,10 +177,6 @@
"description": "Execute a step only on a specific platform. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#platform",
"$ref": "#/definitions/constraint_list"
},
"environment": {
"description": "Execute a step only for a specific environment. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#environment",
"$ref": "#/definitions/constraint_list"
},
"instance": {
"description": "Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#instance",
"$ref": "#/definitions/constraint_list"
@ -291,10 +276,6 @@
"volumes": {
"$ref": "#/definitions/step_volumes"
},
"group": {
"description": "deprecated, use depends_on",
"type": "string"
},
"depends_on": {
"description": "Execute a step after another step has finished.",
"oneOf": [
@ -377,10 +358,6 @@
"volumes": {
"$ref": "#/definitions/step_volumes"
},
"group": {
"description": "deprecated, use depends_on",
"type": "string"
},
"depends_on": {
"description": "Execute a step after another step has finished.",
"oneOf": [
@ -457,10 +434,6 @@
"volumes": {
"$ref": "#/definitions/step_volumes"
},
"group": {
"description": "deprecated, use depends_on",
"type": "string"
},
"depends_on": {
"description": "Execute a step after another step has finished.",
"oneOf": [
@ -551,10 +524,6 @@
"description": "Execute a step only on a specific platform. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#platform",
"$ref": "#/definitions/constraint_list"
},
"environment": {
"description": "Execute a step only for a specific environment. Read more: https://woodpecker-ci.org/docs/usage/workflow-syntax#environment",
"$ref": "#/definitions/constraint_list"
},
"matrix": {
"description": "Read more: https://woodpecker-ci.org/docs/usage/matrix-workflows",
"type": "object",
@ -624,40 +593,6 @@
"items": {
"$ref": "#/definitions/event_enum"
}
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"include": {
"oneOf": [
{
"$ref": "#/definitions/event_enum"
},
{
"type": "array",
"minLength": 1,
"items": {
"$ref": "#/definitions/event_enum"
}
}
]
},
"exclude": {
"oneOf": [
{
"$ref": "#/definitions/event_enum"
},
{
"type": "array",
"minLength": 1,
"items": {
"$ref": "#/definitions/event_enum"
}
}
]
}
}
}
]
},
@ -739,43 +674,16 @@
},
"step_environment": {
"description": "Pass environment variables to a pipeline step. Read more: https://woodpecker-ci.org/docs/usage/environment",
"oneOf": [
{
"type": "array",
"items": {
"type": "string"
},
"minLength": 1
},
{
"type": "object",
"additionalProperties": {
"type": ["boolean", "string", "number", "array", "object"]
}
}
]
"type": "object",
"additionalProperties": {
"type": ["boolean", "string", "number", "array", "object"]
}
},
"step_secrets": {
"description": "Pass secrets to a pipeline step at runtime. Read more: https://woodpecker-ci.org/docs/usage/secrets",
"type": "array",
"items": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"required": ["source", "target"],
"properties": {
"source": {
"type": "string"
},
"target": {
"type": "string"
}
}
}
]
"type": "string"
},
"minLength": 1
},

View file

@ -15,6 +15,7 @@
package yaml
import (
"slices"
"testing"
"github.com/franela/goblin"
@ -35,7 +36,7 @@ func TestParse(t *testing.T) {
g.Fail(err)
}
g.Assert(out.When.Constraints[0].Event.Match("tester")).Equal(true)
g.Assert(slices.Contains(out.When.Constraints[0].Event, "tester")).Equal(true)
g.Assert(out.Workspace.Base).Equal("/go")
g.Assert(out.Workspace.Path).Equal("src/github.com/octocat/hello-world")
@ -85,7 +86,7 @@ func TestParse(t *testing.T) {
g.Assert(len(out.Steps.ContainerList[0].When.Constraints)).Equal(0)
g.Assert(out.Steps.ContainerList[1].Name).Equal("notify_success")
g.Assert(out.Steps.ContainerList[1].Image).Equal("plugins/slack")
g.Assert(out.Steps.ContainerList[1].When.Constraints[0].Event.Include).Equal([]string{"success"})
g.Assert(out.Steps.ContainerList[1].When.Constraints[0].Event).Equal(yaml_base_types.StringOrSlice{"success"})
})
matchConfig, err := ParseString(sampleYaml)

View file

@ -1,51 +0,0 @@
// Copyright 2023 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 base
import (
"errors"
"fmt"
"strings"
)
// SliceOrMap represents a map of strings, string slice are converted into a map.
type SliceOrMap map[string]any
// UnmarshalYAML implements the Unmarshaler interface.
func (s *SliceOrMap) UnmarshalYAML(unmarshal func(any) error) error {
var sliceType []any
if err := unmarshal(&sliceType); err == nil {
parts := map[string]any{}
for _, s := range sliceType {
if str, ok := s.(string); ok {
str := strings.TrimSpace(str)
key, val, _ := strings.Cut(str, "=")
parts[key] = val
} else {
return fmt.Errorf("cannot unmarshal '%v' of type %T into a string value", s, s)
}
}
*s = parts
return nil
}
var mapType map[string]any
if err := unmarshal(&mapType); err == nil {
*s = mapType
return nil
}
return errors.New("failed to unmarshal SliceOrMap")
}

View file

@ -1,58 +0,0 @@
// Copyright 2024 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 base
import (
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
type StructSliceOrMap struct {
Foos SliceOrMap `yaml:"foos,omitempty"`
Bars []string `yaml:"bars"`
}
func TestSliceOrMapYaml(t *testing.T) {
str := `{foos: [bar=baz, far=faz]}`
s := StructSliceOrMap{}
assert.NoError(t, yaml.Unmarshal([]byte(str), &s))
assert.Equal(t, SliceOrMap{"bar": "baz", "far": "faz"}, s.Foos)
d, err := yaml.Marshal(&s)
assert.NoError(t, err)
s2 := StructSliceOrMap{}
assert.NoError(t, yaml.Unmarshal(d, &s2))
assert.Equal(t, SliceOrMap{"bar": "baz", "far": "faz"}, s2.Foos)
}
func TestStr2SliceOrMapPtrMap(t *testing.T) {
s := map[string]*StructSliceOrMap{"udav": {
Foos: SliceOrMap{"io.rancher.os.bar": "baz", "io.rancher.os.far": "true"},
Bars: []string{},
}}
d, err := yaml.Marshal(&s)
assert.NoError(t, err)
s2 := map[string]*StructSliceOrMap{}
assert.NoError(t, yaml.Unmarshal(d, &s2))
assert.Equal(t, s, s2)
}

View file

@ -39,7 +39,6 @@ type (
Detached bool `yaml:"detach,omitempty"`
Directory string `yaml:"directory,omitempty"`
Failure string `yaml:"failure,omitempty"`
Group string `yaml:"group,omitempty"`
Image string `yaml:"image,omitempty"`
Name string `yaml:"name,omitempty"`
Pull bool `yaml:"pull,omitempty"`
@ -49,10 +48,8 @@ type (
Ports []string `yaml:"ports,omitempty"`
DependsOn base.StringOrSlice `yaml:"depends_on,omitempty"`
// TODO: make []string in 3.x
Secrets Secrets `yaml:"secrets,omitempty"`
// TODO: make map[string]any in 3.x
Environment base.SliceOrMap `yaml:"environment,omitempty"`
Secrets []string `yaml:"secrets,omitempty"`
Environment map[string]any `yaml:"environment,omitempty"`
// Docker and Kubernetes Specific
Privileged bool `yaml:"privileged,omitempty"`
@ -125,7 +122,7 @@ func (c *Container) IsPlugin() bool {
return len(c.Commands) == 0 &&
len(c.Entrypoint) == 0 &&
len(c.Environment) == 0 &&
len(c.Secrets.Secrets) == 0
len(c.Secrets) == 0
}
func (c *Container) IsTrustedCloneImage() bool {

View file

@ -41,8 +41,8 @@ dns: 8.8.8.8
dns_search: example.com
entrypoint: [/bin/sh, -c]
environment:
- RACK_ENV=development
- SHOW=true
RACK_ENV: development
SHOW: true
extra_hosts:
- somehost:162.242.195.82
- otherhost:50.31.209.229
@ -88,7 +88,7 @@ func TestUnmarshalContainer(t *testing.T) {
DNS: base.StringOrSlice{"8.8.8.8"},
DNSSearch: base.StringOrSlice{"example.com"},
Entrypoint: []string{"/bin/sh", "-c"},
Environment: base.SliceOrMap{"RACK_ENV": "development", "SHOW": "true"},
Environment: map[string]any{"RACK_ENV": "development", "SHOW": true},
ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229", "ipv6:2001:db8::10"},
Image: "golang:latest",
MemLimit: base.MemStringOrInt(1024),
@ -114,9 +114,7 @@ func TestUnmarshalContainer(t *testing.T) {
},
},
{
Event: constraint.List{
Include: []string{"cron"},
},
Event: base.StringOrSlice{"cron"},
Cron: constraint.List{
Include: []string{"job1"},
},
@ -166,7 +164,6 @@ func TestUnmarshalContainers(t *testing.T) {
},
{
from: `publish-agent:
group: bundle
image: print/env
settings:
repo: woodpeckerci/woodpecker-agent
@ -179,16 +176,9 @@ func TestUnmarshalContainers(t *testing.T) {
event: push`,
want: []*Container{
{
Name: "publish-agent",
Image: "print/env",
Group: "bundle",
Secrets: Secrets{Secrets: []*Secret{{
Source: "docker_username",
Target: "docker_username",
}, {
Source: "docker_password",
Target: "docker_password",
}}},
Name: "publish-agent",
Image: "print/env",
Secrets: []string{"docker_username", "docker_password"},
Settings: map[string]any{
"repo": "woodpeckerci/woodpecker-agent",
"dockerfile": "docker/Dockerfile.agent",
@ -198,7 +188,7 @@ func TestUnmarshalContainers(t *testing.T) {
When: constraint.When{
Constraints: []constraint.Constraint{
{
Event: constraint.List{Include: []string{"push"}},
Event: base.StringOrSlice{"push"},
Branch: constraint.List{Include: []string{"${CI_REPO_DEFAULT_BRANCH}"}},
},
},
@ -208,7 +198,6 @@ func TestUnmarshalContainers(t *testing.T) {
},
{
from: `publish-cli:
group: docker
image: print/env
settings:
repo: woodpeckerci/woodpecker-cli
@ -221,7 +210,6 @@ func TestUnmarshalContainers(t *testing.T) {
{
Name: "publish-cli",
Image: "print/env",
Group: "docker",
Settings: map[string]any{
"repo": "woodpeckerci/woodpecker-cli",
"dockerfile": "docker/Dockerfile.cli",
@ -230,7 +218,7 @@ func TestUnmarshalContainers(t *testing.T) {
When: constraint.When{
Constraints: []constraint.Constraint{
{
Event: constraint.List{Include: []string{"push"}},
Event: base.StringOrSlice{"push"},
Branch: constraint.List{Include: []string{"${CI_REPO_DEFAULT_BRANCH}"}},
},
},
@ -252,11 +240,11 @@ func TestUnmarshalContainers(t *testing.T) {
When: constraint.When{
Constraints: []constraint.Constraint{
{
Event: constraint.List{Include: []string{"push"}},
Event: base.StringOrSlice{"push"},
Branch: constraint.List{Include: []string{"${CI_REPO_DEFAULT_BRANCH}"}},
},
{
Event: constraint.List{Include: []string{"pull_request"}},
Event: base.StringOrSlice{"pull_request"},
},
},
},

View file

@ -1,48 +0,0 @@
// Copyright 2023 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 "gopkg.in/yaml.v3"
type (
// Secrets defines a collection of secrets.
Secrets struct {
Secrets []*Secret
}
// Secret defines a container secret.
Secret struct {
Source string `yaml:"source"`
Target string `yaml:"target"`
}
)
// UnmarshalYAML implements the Unmarshaler interface.
func (s *Secrets) UnmarshalYAML(value *yaml.Node) error {
y, _ := yaml.Marshal(value)
var secrets []string
err := yaml.Unmarshal(y, &secrets)
if err == nil {
for _, str := range secrets {
s.Secrets = append(s.Secrets, &Secret{
Source: str,
Target: str,
})
}
return nil
}
return yaml.Unmarshal(y, &s.Secrets)
}

View file

@ -1,73 +0,0 @@
// Copyright 2023 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 (
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestUnmarshalSecrets(t *testing.T) {
testdata := []struct {
from string
want []*Secret
}{
{
from: "[ mysql_username, mysql_password]",
want: []*Secret{
{
Source: "mysql_username",
Target: "mysql_username",
},
{
Source: "mysql_password",
Target: "mysql_password",
},
},
},
{
from: "[ { source: mysql_prod_username, target: mysql_username } ]",
want: []*Secret{
{
Source: "mysql_prod_username",
Target: "mysql_username",
},
},
},
{
from: "[ { source: mysql_prod_username, target: mysql_username }, { source: redis_username, target: redis_username } ]",
want: []*Secret{
{
Source: "mysql_prod_username",
Target: "mysql_username",
},
{
Source: "redis_username",
Target: "redis_username",
},
},
},
}
for _, test := range testdata {
in := []byte(test.from)
got := Secrets{}
err := yaml.Unmarshal(in, &got)
assert.NoError(t, err)
assert.EqualValues(t, test.want, got.Secrets, "problem parsing secrets %q", test.from)
}
}