From 51903397f055a0de9763246663a9ff4ab82e10cb Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Thu, 7 Aug 2025 11:18:08 +0200 Subject: [PATCH] feat: add .gitignore, upgrade to Go 1.23 with deps, improve logging, config loading, and glob-based deletion, and push image in deploy script --- .gitignore | 1 + deploy.sh | 3 +- go.mod | 18 +++++- go.sum | 30 ++++++++++ main.go | 162 +++++++++++++++++++++++++++++++++++------------------ main.log | 3 - 6 files changed, 158 insertions(+), 59 deletions(-) create mode 100644 .gitignore create mode 100644 go.sum delete mode 100644 main.log diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/deploy.sh b/deploy.sh index d0256a4..de2fef3 100644 --- a/deploy.sh +++ b/deploy.sh @@ -1 +1,2 @@ -docker build -t directory-forbidder . \ No newline at end of file +docker build -t docker.site.quack-lab.dev/directory-forbidder . +docker push docker.site.quack-lab.dev/directory-forbidder \ No newline at end of file diff --git a/go.mod b/go.mod index 9a9693e..653aa2f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,19 @@ module main -go 1.22.4 +go 1.23 + +toolchain go1.23.6 + +require ( + git.site.quack-lab.dev/dave/cylogger v1.3.0 + github.com/bmatcuk/doublestar/v4 v4.9.1 +) + +require ( + github.com/google/go-cmp v0.5.9 // indirect + github.com/hexops/valast v1.5.0 // indirect + golang.org/x/mod v0.7.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/tools v0.4.0 // indirect + mvdan.cc/gofumpt v0.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a456d06 --- /dev/null +++ b/go.sum @@ -0,0 +1,30 @@ +git.site.quack-lab.dev/dave/cylogger v1.3.0 h1:eTWPUD+ThVi8kGIsRcE0XDeoH3yFb5miFEODyKUdWJw= +git.site.quack-lab.dev/dave/cylogger v1.3.0/go.mod h1:wctgZplMvroA4X6p8f4B/LaCKtiBcT1Pp+L14kcS8jk= +github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= +github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hexops/autogold v0.8.1 h1:wvyd/bAJ+Dy+DcE09BoLk6r4Fa5R5W+O+GUzmR985WM= +github.com/hexops/autogold v0.8.1/go.mod h1:97HLDXyG23akzAoRYJh/2OBs3kd80eHyKPvZw0S5ZBY= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/hexops/valast v1.5.0 h1:FBTuvVi0wjTngtXJRZXMbkN/Dn6DgsUsBwch2DUJU8Y= +github.com/hexops/valast v1.5.0/go.mod h1:Jcy1pNH7LNraVaAZDLyv21hHg2WBv9Nf9FL6fGxU7o4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= +mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= diff --git a/main.go b/main.go index 8fbb604..c78fdb5 100644 --- a/main.go +++ b/main.go @@ -2,79 +2,133 @@ package main import ( "flag" - "io" - "log" "os" - "path" + "path/filepath" "strings" "time" + + logger "git.site.quack-lab.dev/dave/cylogger" + "github.com/bmatcuk/doublestar/v4" ) -const ( - Black = "\033[30m" - Red = "\033[31m" - Green = "\033[32m" - Yellow = "\033[33m" - Blue = "\033[34m" - Magenta = "\033[35m" - Cyan = "\033[36m" - White = "\033[37m" - Reset = "\033[0m" - PathColor = Magenta - ErrorColor = Red -) - -func init() { - log.SetFlags(log.Lmicroseconds | log.Lshortfile) - logFile, err := os.Create("main.log") - if err != nil { - log.Printf("Error creating log file: %v", err) - os.Exit(1) - } - logger := io.MultiWriter(os.Stdout, logFile) - log.SetOutput(logger) +type Config struct { + WorkDir string + ScanSeconds int + Patterns []string } -func main() { - wd := flag.String("wd", "", "working directory") - scanTimer := flag.Int("scan", 60, "scan interval in seconds") +func getenv(key, def string) string { + if v, ok := os.LookupEnv(key); ok { + return v + } + 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 := path.Clean(*wd) - forbidden := flag.Args() - envForbidden, ok := os.LookupEnv("FORBIDDEN") - if ok { - forbidden = append(forbidden, strings.Split(envForbidden, ",")...) + workdir := filepath.Clean(*flagWDir) + if pfx := strings.TrimSpace(getenv("PATH_PREFIX", "")); pfx != "" { + workdir = filepath.Join(workdir, pfx) } - pathPrefix, ok := os.LookupEnv("PATH_PREFIX") - if ok { - workdir = path.Join(workdir, pathPrefix) + workdir = filepath.ToSlash(workdir) + + // Gather patterns: args + FORBIDDEN env (comma-separated) + patterns := []string{} + patterns = append(patterns, flag.Args()...) + if env := strings.TrimSpace(getenv("FORBIDDEN", "")); env != "" { + for _, p := range strings.Split(env, ",") { + p = strings.TrimSpace(p) + if p != "" { + patterns = append(patterns, p) + } + } } - log.Printf("Working directory: %s%s%s", PathColor, workdir, Reset) - log.Printf("Forbidden files: %v", forbidden) - ticker := time.NewTicker(time.Duration(*scanTimer) * time.Second) - for { - log.Printf("Running at %s", time.Now().Format(time.RFC3339)) - for _, f := range forbidden { - log.Printf("Checking file %s%s%s", PathColor, f, Reset) - f = strings.Trim(f, " ") - fullPath := path.Join(workdir, f) - fullPath = path.Clean(fullPath) + // Normalize patterns to slash style; patterns are relative to workdir + for i := range patterns { + patterns[i] = filepath.ToSlash(strings.TrimSpace(patterns[i])) + } - _, err := os.Stat(fullPath) + logger.Info("Config:") + logger.Info(" WorkDir: %s", workdir) + logger.Info(" ScanSeconds: %d", *flagScan) + logger.Info(" Patterns: %v", patterns) + + return Config{ + WorkDir: workdir, + ScanSeconds: *flagScan, + Patterns: patterns, + } +} + +func deleteMatches(cfg Config) { + log := logger.Default.WithPrefix("deleteMatches") + + // Ensure workdir exists + if cfg.WorkDir == "" { + log.Error("WorkDir is empty") + return + } + if _, err := os.Stat(cfg.WorkDir); err != nil { + log.Error("WorkDir not accessible %s: %v", cfg.WorkDir, err) + return + } + + for _, pat := range cfg.Patterns { + 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) + continue + } + if len(matches) == 0 { + log.Debug("No matches for pattern %q", pat) + continue + } + + for _, rel := range matches { + full := filepath.Join(cfg.WorkDir, rel) + full = filepath.Clean(full) + + info, err := os.Stat(full) if err != nil { - log.Printf("Error stating file %s%s%s: %s%v%s", PathColor, fullPath, Reset, ErrorColor, err, Reset) + log.Warning("stat %s: %v", full, err) continue } - log.Printf("Removing file %s%s%s", PathColor, fullPath, Reset) - err = os.RemoveAll(fullPath) - if err != nil { - log.Printf("Error removing file %s%s%s: %s%v%s", PathColor, fullPath, Reset, ErrorColor, err, Reset) + if info.IsDir() { + log.Info("Removing directory %s", full) + } else { + log.Info("Removing file %s", full) + } + + if err := os.RemoveAll(full); err != nil { + log.Error("remove %s: %v", full, err) continue } } - <-ticker.C + } +} + +func main() { + cfg := loadConfig() + logger.Info("Starting forbidden cleaner") + + // Run immediately, then on interval + deleteMatches(cfg) + + ticker := time.NewTicker(time.Duration(cfg.ScanSeconds) * time.Second) + defer ticker.Stop() + for { + ts := <-ticker.C + logger.Info("Tick %d", ts.UnixMilli()) + deleteMatches(cfg) } } diff --git a/main.log b/main.log deleted file mode 100644 index ff5f1a0..0000000 --- a/main.log +++ /dev/null @@ -1,3 +0,0 @@ -10:28:29.544933 main.go:53: Working directory: . -10:28:29.545433 main.go:54: Forbidden files: [] -10:28:29.545433 main.go:58: Running at 2024-07-05T10:28:29+02:00