You've already forked woodpecker
mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-11-23 21:44:44 +02:00
140 lines
4.0 KiB
Go
140 lines
4.0 KiB
Go
|
|
// Copyright 2025 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 constraint
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/bmatcuk/doublestar/v4"
|
||
|
|
"gopkg.in/yaml.v3"
|
||
|
|
|
||
|
|
yamlBaseTypes "go.woodpecker-ci.org/woodpecker/v3/pipeline/frontend/yaml/types/base"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Path defines a runtime constrain for exclude & include paths.
|
||
|
|
type Path struct {
|
||
|
|
Include []string `yaml:"include,omitempty"`
|
||
|
|
Exclude []string `yaml:"exclude,omitempty"`
|
||
|
|
IgnoreMessage string `yaml:"ignore_message,omitempty"`
|
||
|
|
OnEmpty yamlBaseTypes.BoolTrue `yaml:"on_empty,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// UnmarshalYAML unmarshal the constraint.
|
||
|
|
func (c *Path) UnmarshalYAML(value *yaml.Node) error {
|
||
|
|
out1 := struct {
|
||
|
|
Include yamlBaseTypes.StringOrSlice `yaml:"include"`
|
||
|
|
Exclude yamlBaseTypes.StringOrSlice `yaml:"exclude"`
|
||
|
|
IgnoreMessage string `yaml:"ignore_message"`
|
||
|
|
OnEmpty yamlBaseTypes.BoolTrue `yaml:"on_empty"`
|
||
|
|
}{}
|
||
|
|
|
||
|
|
var out2 yamlBaseTypes.StringOrSlice
|
||
|
|
|
||
|
|
err1 := value.Decode(&out1)
|
||
|
|
err2 := value.Decode(&out2)
|
||
|
|
|
||
|
|
c.Exclude = out1.Exclude
|
||
|
|
c.IgnoreMessage = out1.IgnoreMessage
|
||
|
|
c.OnEmpty = out1.OnEmpty
|
||
|
|
c.Include = append( //nolint:gocritic
|
||
|
|
out1.Include,
|
||
|
|
out2...,
|
||
|
|
)
|
||
|
|
|
||
|
|
if err1 != nil && err2 != nil {
|
||
|
|
y, _ := yaml.Marshal(value)
|
||
|
|
return fmt.Errorf("could not parse condition: %s", y)
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// MarshalYAML implements custom Yaml marshaling.
|
||
|
|
func (c Path) MarshalYAML() (any, error) {
|
||
|
|
// if only Include is set return simple syntax
|
||
|
|
if len(c.Exclude) == 0 &&
|
||
|
|
len(c.IgnoreMessage) == 0 &&
|
||
|
|
c.OnEmpty.Bool() {
|
||
|
|
if len(c.Include) == 0 {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return yamlBaseTypes.StringOrSlice(c.Include), nil
|
||
|
|
}
|
||
|
|
// we can not return type Path as it would lead to infinite recursion :/
|
||
|
|
return struct {
|
||
|
|
Include yamlBaseTypes.StringOrSlice `yaml:"include,omitempty"`
|
||
|
|
Exclude yamlBaseTypes.StringOrSlice `yaml:"exclude,omitempty"`
|
||
|
|
IgnoreMessage string `yaml:"ignore_message,omitempty"`
|
||
|
|
OnEmpty yamlBaseTypes.BoolTrue `yaml:"on_empty,omitempty"`
|
||
|
|
}{
|
||
|
|
Include: c.Include,
|
||
|
|
Exclude: c.Exclude,
|
||
|
|
IgnoreMessage: c.IgnoreMessage,
|
||
|
|
OnEmpty: c.OnEmpty,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Match returns true if file paths in string slice matches the include and not exclude patterns
|
||
|
|
// or if commit message contains ignore message.
|
||
|
|
func (c *Path) Match(v []string, message string) bool {
|
||
|
|
// ignore file pattern matches if the commit message contains a pattern
|
||
|
|
if len(c.IgnoreMessage) > 0 && strings.Contains(strings.ToLower(message), strings.ToLower(c.IgnoreMessage)) {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
// return value based on 'on_empty', if there are no commit files (empty commit)
|
||
|
|
if len(v) == 0 {
|
||
|
|
return c.OnEmpty.Bool()
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(c.Exclude) > 0 && c.Excludes(v) {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
if len(c.Include) > 0 && !c.Includes(v) {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
// Includes returns true if the string matches any of the include patterns.
|
||
|
|
func (c *Path) Includes(v []string) bool {
|
||
|
|
for _, pattern := range c.Include {
|
||
|
|
for _, file := range v {
|
||
|
|
if ok, _ := doublestar.Match(pattern, file); ok {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
// Excludes returns true if all of the strings match any of the exclude patterns.
|
||
|
|
func (c *Path) Excludes(v []string) bool {
|
||
|
|
for _, file := range v {
|
||
|
|
matched := false
|
||
|
|
for _, pattern := range c.Exclude {
|
||
|
|
if ok, _ := doublestar.Match(pattern, file); ok {
|
||
|
|
matched = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !matched {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
}
|