feat(conf): support prefix scope matching and release config fallback
All checks were successful
build / build (pull_request) Successful in 2m14s
build / build (push) Successful in 2m17s
build / trigger-build-image (push) Has been skipped
build / trigger-build-image (pull_request) Has been skipped

This commit is contained in:
张泊明518370910136 2026-07-28 00:56:44 -07:00
parent 62b1538b6a
commit edf5c617be
2 changed files with 177 additions and 33 deletions

View File

@ -152,50 +152,115 @@ func hintValidScopes(confRoot, confName string) {
"valid scopes", validScopes)
}
func GetConfPath(confRoot, confName, fallbackConfName, msg, tag string) (
confPath string, confStat fs.FileInfo,
conventionalCommit *ConventionalCommit, err error,
) {
confPath, conventionalCommit, err = parseMsg(confRoot, confName, msg, tag)
if err != nil {
slog.Error("parse msg", "error", err)
// no fallback when tag is specified, it is triggered by release.yaml
if os.IsNotExist(err) {
slog.Info("tag is not empty, no fallback conf")
hintValidScopes(confRoot, confName)
return confPath, confStat, conventionalCommit, err
}
// fallback to conf file in conf root on parse error
confPath = filepath.Join(confRoot, fallbackConfName)
slog.Info("fallback to conf", "path", confPath)
func findBestMatchingScope(confRoot, targetScope, confName string) string {
if targetScope == "" {
return ""
}
confStat, err = os.Stat(confPath)
if err != nil {
if os.IsNotExist(err) {
hintValidScopes(confRoot, confName)
}
slog.Error("stat conf", "error", err)
// fallback to conf file in conf root on conf not exist
confPath = filepath.Join(confRoot, fallbackConfName)
slog.Info("fallback to conf", "path", confPath)
confStat, err = os.Stat(confPath)
confRoot = filepath.Clean(confRoot)
bestMatch := ""
_ = filepath.WalkDir(confRoot, func(path string, d fs.DirEntry, err error) error {
if err != nil {
slog.Error("stat fallback conf", "error", err)
return confPath, confStat, conventionalCommit, err
return nil
}
}
// Check file ownership
if d.IsDir() {
relPath, err := filepath.Rel(confRoot, path)
if err != nil || relPath == "." || relPath == "" || strings.HasPrefix(relPath, "..") {
return nil
}
confPath := filepath.Join(path, confName)
if stat, err := os.Stat(confPath); err == nil && !stat.IsDir() {
if strings.HasPrefix(targetScope, relPath) {
if len(relPath) > len(bestMatch) {
bestMatch = relPath
}
}
}
}
return nil
})
return bestMatch
}
func checkFileOwnership(confPath string, confStat fs.FileInfo) error {
if stat, ok := confStat.Sys().(*syscall.Stat_t); ok {
uid := int(stat.Uid)
currentUID := os.Getuid()
if uid != currentUID {
err = fmt.Errorf("insecure configuration file: owned by uid %d, expected %d", uid, currentUID)
slog.Error("insecure conf file", "path", confPath, "uid", uid, "currentUID", currentUID)
return confPath, confStat, conventionalCommit, err
return fmt.Errorf("insecure configuration file: owned by uid %d, expected %d", uid, currentUID)
}
}
return nil
}
func GetConfPath(confRoot, confName, fallbackConfName, msg, tag string) (
confPath string, confStat fs.FileInfo,
conventionalCommit *ConventionalCommit, err error,
) {
if fallbackConfName == "" {
fallbackConfName = confName
}
confPath, conventionalCommit, err = parseMsg(confRoot, confName, msg, tag)
if err == nil && conventionalCommit != nil && conventionalCommit.Scope != "" {
confStat, err = os.Stat(confPath)
if err == nil && !confStat.IsDir() {
if err := checkFileOwnership(confPath, confStat); err != nil {
return confPath, confStat, conventionalCommit, err
}
return confPath, confStat, conventionalCommit, nil
}
}
return confPath, confStat, conventionalCommit, err
targetScope := ""
if tag != "" {
targetScope = tag
} else if conventionalCommit != nil {
targetScope = conventionalCommit.Scope
}
if targetScope != "" {
if matchedScope := findBestMatchingScope(confRoot, targetScope, confName); matchedScope != "" {
matchedPath := filepath.Join(confRoot, matchedScope, confName)
if stat, err := os.Stat(matchedPath); err == nil && !stat.IsDir() {
slog.Info("matched scope by prefix", "targetScope", targetScope, "matchedScope", matchedScope)
if conventionalCommit == nil {
conventionalCommit = &ConventionalCommit{
Scope: matchedScope,
Group: "all",
}
} else {
conventionalCommit.Scope = matchedScope
}
if err := checkFileOwnership(matchedPath, stat); err != nil {
return matchedPath, stat, conventionalCommit, err
}
return matchedPath, stat, conventionalCommit, nil
}
}
}
fallbackPath := filepath.Join(confRoot, fallbackConfName)
slog.Info("fallback to conf", "path", fallbackPath)
confStat, err = os.Stat(fallbackPath)
if err != nil || confStat.IsDir() {
if os.IsNotExist(err) || (confStat != nil && confStat.IsDir()) {
hintValidScopes(confRoot, confName)
}
slog.Error("stat fallback conf", "error", err)
if confStat != nil && confStat.IsDir() {
err = fmt.Errorf("fallback conf path is a directory: %s", fallbackPath)
}
return fallbackPath, confStat, conventionalCommit, err
}
if err := checkFileOwnership(fallbackPath, confStat); err != nil {
return fallbackPath, confStat, conventionalCommit, err
}
return fallbackPath, confStat, conventionalCommit, nil
}
func MatchGroups(conf *Conf, conventionalCommit *ConventionalCommit) []string {

View File

@ -1,6 +1,8 @@
package conf
import (
"os"
"path/filepath"
"reflect"
"testing"
)
@ -130,3 +132,80 @@ func TestParseConventionalCommit(t *testing.T) {
})
}
}
func TestGetConfPath(t *testing.T) {
tmpDir := t.TempDir()
// 1. Setup layout: tmpDir/alpha/conf.json, tmpDir/conf.json
alphaDir := filepath.Join(tmpDir, "alpha")
if err := os.MkdirAll(alphaDir, 0755); err != nil {
t.Fatalf("failed to create alpha dir: %v", err)
}
alphaConfPath := filepath.Join(alphaDir, "conf.json")
if err := os.WriteFile(alphaConfPath, []byte("{}"), 0644); err != nil {
t.Fatalf("failed to write alpha conf: %v", err)
}
rootConfPath := filepath.Join(tmpDir, "conf.json")
if err := os.WriteFile(rootConfPath, []byte("{}"), 0644); err != nil {
t.Fatalf("failed to write root conf: %v", err)
}
t.Run("Exact Scope Match", func(t *testing.T) {
gotPath, _, cc, err := GetConfPath(tmpDir, "conf.json", "conf.json", "", "alpha")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gotPath != alphaConfPath {
t.Errorf("got path %s, want %s", gotPath, alphaConfPath)
}
if cc.Scope != "alpha" {
t.Errorf("got scope %s, want alpha", cc.Scope)
}
})
t.Run("Prefix Scope Match for Floating Release", func(t *testing.T) {
gotPath, _, cc, err := GetConfPath(tmpDir, "conf.json", "conf.json", "", "alpha1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gotPath != alphaConfPath {
t.Errorf("got path %s, want %s", gotPath, alphaConfPath)
}
if cc.Scope != "alpha" {
t.Errorf("got scope %s, want alpha", cc.Scope)
}
})
t.Run("Fallback to Root Conf when Scope Not Found", func(t *testing.T) {
gotPath, _, cc, err := GetConfPath(tmpDir, "conf.json", "conf.json", "", "beta")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gotPath != rootConfPath {
t.Errorf("got path %s, want %s", gotPath, rootConfPath)
}
if cc.Scope != "beta" {
t.Errorf("got scope %s, want beta", cc.Scope)
}
})
t.Run("Fallback to Root Conf with Empty Fallback Conf Name", func(t *testing.T) {
gotPath, _, _, err := GetConfPath(tmpDir, "conf.json", "", "", "gamma")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gotPath != rootConfPath {
t.Errorf("got path %s, want %s", gotPath, rootConfPath)
}
})
t.Run("No Conf Found Error", func(t *testing.T) {
emptyTmpDir := t.TempDir()
_, _, _, err := GetConfPath(emptyTmpDir, "conf.json", "conf.json", "", "alpha")
if err == nil {
t.Fatalf("expected error when no conf found, got nil")
}
})
}