package ci import ( "fmt" "strings" gitdomain "github.com/forgeo/forgebucket/internal/domain/git" "gopkg.in/yaml.v3" ) const workflowDir = ".forgebucket/workflows" // ListWorkflows returns the file paths of all workflow YAML files in a repo at a // given ref. Returns nil (no error) when the workflows directory doesn't exist. func ListWorkflows(repoPath, ref string) ([]string, error) { entries, err := gitdomain.TreeLS(repoPath, ref, workflowDir) if err != nil { // Directory does not exist at this ref — no workflows, not an error. return nil, nil } var paths []string for _, e := range entries { if e.Type == "blob" && (strings.HasSuffix(e.Name, ".yml") || strings.HasSuffix(e.Name, ".yaml")) { paths = append(paths, workflowDir+"/"+e.Name) } } return paths, nil } // ParseWorkflow reads and parses a single workflow YAML file from the repo at ref. func ParseWorkflow(repoPath, ref, filePath string) (*WorkflowFile, error) { data, err := gitdomain.BlobCat(repoPath, ref, filePath) if err != nil { return nil, fmt.Errorf("read %s: %w", filePath, err) } var wf WorkflowFile if err := yaml.Unmarshal(data, &wf); err != nil { return nil, fmt.Errorf("parse %s: %w", filePath, err) } return &wf, nil } // MatchesPushTrigger reports whether a workflow should run for a push to ref. // ref is the full ref name, e.g. "refs/heads/main". func MatchesPushTrigger(wf *WorkflowFile, ref string) bool { if wf.On.Push == nil { return false } trigger := wf.On.Push // No branch filter means "all branches". if len(trigger.Branches) == 0 && len(trigger.Tags) == 0 { return true } branch := strings.TrimPrefix(ref, "refs/heads/") for _, pattern := range trigger.Branches { if matchGlob(pattern, branch) { return true } } tag := strings.TrimPrefix(ref, "refs/tags/") for _, pattern := range trigger.Tags { if matchGlob(pattern, tag) { return true } } return false } // matchGlob supports simple "*" wildcards (not full glob). func matchGlob(pattern, s string) bool { if pattern == "*" { return true } if !strings.Contains(pattern, "*") { return pattern == s } parts := strings.SplitN(pattern, "*", 2) return strings.HasPrefix(s, parts[0]) && strings.HasSuffix(s, parts[1]) }