141 lines
3.9 KiB
Go
141 lines
3.9 KiB
Go
// src/config.go
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// LabelerRule represents a rule mapping file patterns to a label area.
|
|
type LabelerRule struct {
|
|
ChangedFiles []ChangedFile `yaml:"changed-files"`
|
|
}
|
|
|
|
// ChangedFile represents a single file pattern rule.
|
|
type ChangedFile struct {
|
|
AnyGlobToAnyFile string `yaml:"any-glob-to-any-file"`
|
|
}
|
|
|
|
// LabelerConfig represents the entire labeler configuration.
|
|
type LabelerConfig map[string][]LabelerRule
|
|
|
|
// LoadLabelerConfig loads and parses the labeler.yaml file.
|
|
func LoadLabelerConfig(path string) (LabelerConfig, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read labeler config file: %w", err)
|
|
}
|
|
|
|
var config LabelerConfig
|
|
if err := yaml.Unmarshal(data, &config); err != nil {
|
|
return nil, fmt.Errorf("failed to parse labeler config file: %w", err)
|
|
}
|
|
|
|
return config, nil
|
|
}
|
|
|
|
// LoadLabelsConfig loads and parses the labels.yaml file.
|
|
func LoadLabelsConfig(path string) ([]ForgejoLabel, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read labels config file: %w", err)
|
|
}
|
|
|
|
var labels []ForgejoLabel
|
|
if err := yaml.Unmarshal(data, &labels); err != nil {
|
|
return nil, fmt.Errorf("failed to parse labels config file: %w", err)
|
|
}
|
|
|
|
return labels, nil
|
|
}
|
|
|
|
// GetLabelPatterns returns a map of label areas to their corresponding file patterns.
|
|
func (lc LabelerConfig) GetLabelPatterns() map[string][]string {
|
|
patterns := make(map[string][]string)
|
|
for area, rules := range lc {
|
|
for _, rule := range rules {
|
|
for _, cf := range rule.ChangedFiles {
|
|
patterns[area] = append(patterns[area], cf.AnyGlobToAnyFile)
|
|
}
|
|
}
|
|
}
|
|
return patterns
|
|
}
|
|
|
|
// DetermineLabels determines which labels should be applied based on changed files.
|
|
// `changedFiles` is a list of file paths that have been changed.
|
|
func DetermineLabels(labelerConfig LabelerConfig, changedFiles []string) ([]string, error) {
|
|
patterns := labelerConfig.GetLabelPatterns()
|
|
labelSet := make(map[string]struct{})
|
|
|
|
for area, globs := range patterns {
|
|
for _, glob := range globs {
|
|
for _, file := range changedFiles {
|
|
matched, err := filepath.Match(glob, file)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid glob pattern '%s': %w", glob, err)
|
|
}
|
|
if matched {
|
|
labelSet[area] = struct{}{}
|
|
break // No need to check more files for this pattern
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var labels []string
|
|
for label := range labelSet {
|
|
labels = append(labels, label)
|
|
}
|
|
|
|
return labels, nil
|
|
}
|
|
|
|
// ValidateLabelerConfig validates the labeler configuration.
|
|
func ValidateLabelerConfig(config LabelerConfig) error {
|
|
for area, rules := range config {
|
|
if area == "" {
|
|
return fmt.Errorf("labeler config contains an empty area")
|
|
}
|
|
for _, rule := range rules {
|
|
for _, cf := range rule.ChangedFiles {
|
|
if cf.AnyGlobToAnyFile == "" {
|
|
return fmt.Errorf("labeler config for area '%s' contains an empty file pattern", area)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateLabelsConfig validates the labels configuration.
|
|
func ValidateLabelsConfig(labels []ForgejoLabel) error {
|
|
labelNames := make(map[string]struct{})
|
|
for _, label := range labels {
|
|
if label.Name == "" {
|
|
return fmt.Errorf("labels config contains a label with an empty name")
|
|
}
|
|
if label.Color == "" {
|
|
return fmt.Errorf("labels config contains a label with an empty color")
|
|
}
|
|
// Optionally, validate color format
|
|
if !isValidHexColor(label.Color) {
|
|
return fmt.Errorf("label '%s' has an invalid color format: %s", label.Name, label.Color)
|
|
}
|
|
// Check for duplicate labels
|
|
if _, exists := labelNames[label.Name]; exists {
|
|
return fmt.Errorf("duplicate label name found: %s", label.Name)
|
|
}
|
|
labelNames[label.Name] = struct{}{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func isValidHexColor(color string) bool {
|
|
match, _ := regexp.MatchString("^([A-Fa-f0-9]{6})$", color)
|
|
return match
|
|
}
|