feat: add regex filter for replication

Signed-off-by: TMHBOFH <itsystem.bofh@gmail.com>
This commit is contained in:
TMHBOFH 2025-07-08 16:28:50 +02:00
parent 0cf2d7545d
commit 9596eff009
2 changed files with 79 additions and 3 deletions

View File

@ -16,6 +16,7 @@ package util
import ( import (
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/bmatcuk/doublestar" "github.com/bmatcuk/doublestar"
@ -26,6 +27,14 @@ func Match(pattern, str string) (bool, error) {
if len(pattern) == 0 { if len(pattern) == 0 {
return true, nil return true, nil
} }
if strings.HasPrefix(pattern, "regex:") {
regexPattern := strings.TrimPrefix(pattern, "regex:")
reg, err := regexp.Compile(regexPattern)
if err != nil {
return false, fmt.Errorf("invalid regex pattern: %w", err)
}
return reg.MatchString(str), nil
}
return doublestar.Match(pattern, str) return doublestar.Match(pattern, str)
} }

View File

@ -83,11 +83,63 @@ func TestMatch(t *testing.T) {
str: "v2.7.0", str: "v2.7.0",
match: true, match: true,
}, },
// New regex-based tests
{
pattern: "regex:^v1\\.[0-9]+$",
str: "v1.0",
match: true,
},
{
pattern: "regex:^v1\\.[0-9]+$",
str: "v1.10",
match: true,
},
{
pattern: "regex:^v1\\.[0-9]+$",
str: "v2.0",
match: false,
},
{
pattern: "regex:^feature/.+$",
str: "feature/abc",
match: true,
},
{
pattern: "regex:^feature/.+$",
str: "bugfix/abc",
match: false,
},
{
pattern: "regex:^(v|release)-\\d+\\.\\d+(\\.\\d+)?$",
str: "v-1.2.3",
match: true,
},
{
pattern: "regex:^(v|release)-\\d+\\.\\d+(\\.\\d+)?$",
str: "release-2.0",
match: true,
},
{
pattern: "regex:^(v|release)-\\d+\\.\\d+(\\.\\d+)?$",
str: "v-2",
match: false,
},
{
pattern: "regex:^hotfix-(issue|bug)-[0-9]{4,}$",
str: "hotfix-bug-1234",
match: true,
},
{
pattern: "regex:^hotfix-(issue|bug)-[0-9]{4,}$",
str: "hotfix-feature-1234",
match: false,
},
} }
for _, c := range cases { for i, c := range cases {
fmt.Printf("running case %d ...\n", i)
match, err := Match(c.pattern, c.str) match, err := Match(c.pattern, c.str)
require.Nil(t, err) require.Nil(t, err, "unexpected error for pattern: %s", c.pattern)
assert.Equal(t, c.match, match) assert.Equal(t, c.match, match, "pattern: %s, str: %s", c.pattern, c.str)
} }
} }
@ -144,6 +196,21 @@ func TestIsSpecificPathComponent(t *testing.T) {
} }
} }
func TestMatch_InvalidRegex(t *testing.T) {
invalidPatterns := []string{
"regex:^v[0-9+$", // missing closing bracket
"regex:(abc", // unclosed group
"regex:*abc", // invalid quantifier at start
"regex:[a-z", // incomplete character class
}
for i, pattern := range invalidPatterns {
fmt.Printf("[TestMatch_InvalidRegex case %d] pattern=%q\n", i, pattern)
_, err := Match(pattern, "test-tag")
assert.Error(t, err, "expected error for invalid pattern: %s", pattern)
}
}
func TestIsSpecificPath(t *testing.T) { func TestIsSpecificPath(t *testing.T) {
cases := []struct { cases := []struct {
path string path string