diff --git a/main.go b/main.go index c78fdb5..cb25f46 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,10 @@ package main import ( - "flag" "os" "path/filepath" + "regexp" + "strconv" "strings" "time" @@ -12,11 +13,17 @@ import ( ) type Config struct { - WorkDir string - ScanSeconds int - Patterns []string + WorkDir string + Patterns []string + ScanInterval time.Duration } +var ( + timeUnits = map[string]int64{"ms": 1, "s": 1000, "m": 60_000, "h": 3_600_000, "d": 86_400_000, "M": 2_592_000_000, "y": 31_536_000_000} + valueRegex = regexp.MustCompile(`\d+`) + unitRegex = regexp.MustCompile(`[a-zA-Z]+`) +) + func getenv(key, def string) string { if v, ok := os.LookupEnv(key); ok { return v @@ -24,51 +31,77 @@ func getenv(key, def string) string { return def } -func loadConfig() Config { - flagWDir := flag.String("wd", "", "working directory") - flagScan := flag.Int("scan", 60, "scan interval in seconds") - logger.InitFlag() - flag.Parse() - - workdir := filepath.Clean(*flagWDir) - if pfx := strings.TrimSpace(getenv("PATH_PREFIX", "")); pfx != "" { - workdir = filepath.Join(workdir, pfx) +func parseDurationMS(expr string) int64 { + expr = strings.TrimSpace(expr) + if expr == "" { + return 0 } - workdir = filepath.ToSlash(workdir) + var total int64 + for _, p := range strings.Split(expr, "_") { + p = strings.TrimSpace(p) + if p == "" { + continue + } + v := valueRegex.FindString(p) + u := unitRegex.FindString(p) + if v == "" || u == "" { + logger.Warning("Invalid duration part: %q", p) + continue + } + unit, ok := timeUnits[u] + if !ok { + logger.Warning("Invalid duration unit: %q", u) + continue + } + n, err := strconv.ParseInt(v, 10, 64) + if err != nil { + logger.Warning("Invalid duration value: %q: %v", v, err) + continue + } + total += n * unit + } + return total +} - // Gather patterns: args + FORBIDDEN env (comma-separated) - patterns := []string{} - patterns = append(patterns, flag.Args()...) +func loadConfig() Config { + // logger.InitFlag() is optional if you don't use CLI flags to set log level, + // but it is safe to call to honor standard logger flags if provided. + logger.InitFlag() + + workdir := filepath.ToSlash(strings.TrimSpace(getenv("WORKDIR", "/tmp"))) + if pfx := strings.TrimSpace(getenv("PATH_PREFIX", "")); pfx != "" { + workdir = filepath.ToSlash(filepath.Join(workdir, pfx)) + } + + // Patterns from FORBIDDEN (comma-separated). Relative to WorkDir. + var patterns []string if env := strings.TrimSpace(getenv("FORBIDDEN", "")); env != "" { for _, p := range strings.Split(env, ",") { p = strings.TrimSpace(p) if p != "" { - patterns = append(patterns, p) + patterns = append(patterns, filepath.ToSlash(p)) } } } - // Normalize patterns to slash style; patterns are relative to workdir - for i := range patterns { - patterns[i] = filepath.ToSlash(strings.TrimSpace(patterns[i])) - } + interval := time.Duration(parseDurationMS(getenv("SCAN_INTERVAL", "60s"))) * time.Millisecond logger.Info("Config:") - logger.Info(" WorkDir: %s", workdir) - logger.Info(" ScanSeconds: %d", *flagScan) - logger.Info(" Patterns: %v", patterns) + logger.Info(" WORKDIR: %s", workdir) + logger.Info(" PATH_PREFIX: %s", getenv("PATH_PREFIX", "")) + logger.Info(" FORBIDDEN: %v", patterns) + logger.Info(" SCAN_INTERVAL(ms): %d", interval.Milliseconds()) return Config{ - WorkDir: workdir, - ScanSeconds: *flagScan, - Patterns: patterns, + WorkDir: workdir, + Patterns: patterns, + ScanInterval: interval, } } func deleteMatches(cfg Config) { log := logger.Default.WithPrefix("deleteMatches") - // Ensure workdir exists if cfg.WorkDir == "" { log.Error("WorkDir is empty") return @@ -82,7 +115,6 @@ func deleteMatches(cfg Config) { if pat == "" { continue } - // Use doublestar.Glob against the workdir FS; it returns relative paths matches, err := doublestar.Glob(os.DirFS(cfg.WorkDir), pat) if err != nil { log.Error("glob %q: %v", pat, err) @@ -94,9 +126,7 @@ func deleteMatches(cfg Config) { } for _, rel := range matches { - full := filepath.Join(cfg.WorkDir, rel) - full = filepath.Clean(full) - + full := filepath.Clean(filepath.Join(cfg.WorkDir, rel)) info, err := os.Stat(full) if err != nil { log.Warning("stat %s: %v", full, err) @@ -117,18 +147,23 @@ func deleteMatches(cfg Config) { } } +func doRun(cfg Config) { + deleteMatches(cfg) +} + func main() { cfg := loadConfig() logger.Info("Starting forbidden cleaner") + doRun(cfg) - // Run immediately, then on interval - deleteMatches(cfg) + t := time.NewTicker(cfg.ScanInterval) + defer t.Stop() - ticker := time.NewTicker(time.Duration(cfg.ScanSeconds) * time.Second) - defer ticker.Stop() for { - ts := <-ticker.C - logger.Info("Tick %d", ts.UnixMilli()) - deleteMatches(cfg) + select { + case ts := <-t.C: + logger.Info("Tick %d", ts.UnixMilli()) + doRun(cfg) + } } }