Files

93 lines
2.2 KiB
Go

package client
import (
"bytes"
"os"
"path"
"path/filepath"
"strings"
"lodge/internal/gitrepo"
"lodge/internal/protocol"
)
const (
RulesFile = ".lodge"
sharedKeyword = "shared"
branchKeyword = "branch"
excludeBegin = "# lodge begin"
excludeEnd = "# lodge end"
DefaultRules = "shared docs/agents\nshared CLAUDE.local.md\nbranch .agent\n"
)
type Rule struct {
Path string `json:"path"`
Shared bool `json:"shared"`
}
type Rules []Rule
func parseRules(text string) Rules {
rules := Rules{{Path: RulesFile, Shared: true}}
for _, line := range strings.Split(text, "\n") {
fields := strings.Fields(line)
if len(fields) != 2 || fields[0] != sharedKeyword && fields[0] != branchKeyword {
continue
}
rulePath := path.Clean(strings.Trim(filepath.ToSlash(fields[1]), "/"))
if protocol.SafePath(rulePath) {
rules = append(rules, Rule{Path: rulePath, Shared: fields[0] == sharedKeyword})
}
}
return rules
}
func LoadRules(root string) Rules {
data, err := os.ReadFile(filepath.Join(root, RulesFile))
if err != nil {
return parseRules(DefaultRules)
}
return parseRules(string(data))
}
func (rules Rules) paths() []string {
paths := make([]string, len(rules))
for i, rule := range rules {
paths[i] = rule.Path
}
return paths
}
func (rules Rules) covers(file string, shared bool) bool {
best := Rule{}
for _, rule := range rules {
if within(file, rule.Path) && len(rule.Path) > len(best.Path) {
best = rule
}
}
return best.Path != "" && best.Shared == shared
}
func within(path, dir string) bool {
return path == dir || strings.HasPrefix(path, dir+"/")
}
func writeExclude(repo *gitrepo.Repo, rules Rules) error {
file := filepath.Join(repo.CommonDir, "info", "exclude")
current, _ := os.ReadFile(file)
kept := current
if before, rest, found := bytes.Cut(current, []byte(excludeBegin)); found {
_, after, _ := bytes.Cut(rest, []byte(excludeEnd+"\n"))
kept = append(bytes.Clone(before), after...)
}
if len(kept) > 0 && !bytes.HasSuffix(kept, []byte("\n")) {
kept = append(kept, '\n')
}
block := excludeBegin + "\n/" + strings.Join(rules.paths(), "\n/") + "\n" + excludeEnd + "\n"
kept = append(kept, block...)
if bytes.Equal(kept, current) {
return nil
}
return writeFile(file, kept, 0o644)
}