feat: add .gitignore, upgrade to Go 1.23 with deps, improve logging,
config loading, and glob-based deletion, and push image in deploy script
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.log
|
@@ -1 +1,2 @@
|
|||||||
docker build -t directory-forbidder .
|
docker build -t docker.site.quack-lab.dev/directory-forbidder .
|
||||||
|
docker push docker.site.quack-lab.dev/directory-forbidder
|
18
go.mod
18
go.mod
@@ -1,3 +1,19 @@
|
|||||||
module main
|
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
|
||||||
|
)
|
||||||
|
30
go.sum
Normal file
30
go.sum
Normal file
@@ -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=
|
162
main.go
162
main.go
@@ -2,79 +2,133 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
logger "git.site.quack-lab.dev/dave/cylogger"
|
||||||
|
"github.com/bmatcuk/doublestar/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
type Config struct {
|
||||||
Black = "\033[30m"
|
WorkDir string
|
||||||
Red = "\033[31m"
|
ScanSeconds int
|
||||||
Green = "\033[32m"
|
Patterns []string
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func getenv(key, def string) string {
|
||||||
wd := flag.String("wd", "", "working directory")
|
if v, ok := os.LookupEnv(key); ok {
|
||||||
scanTimer := flag.Int("scan", 60, "scan interval in seconds")
|
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()
|
flag.Parse()
|
||||||
|
|
||||||
workdir := path.Clean(*wd)
|
workdir := filepath.Clean(*flagWDir)
|
||||||
forbidden := flag.Args()
|
if pfx := strings.TrimSpace(getenv("PATH_PREFIX", "")); pfx != "" {
|
||||||
envForbidden, ok := os.LookupEnv("FORBIDDEN")
|
workdir = filepath.Join(workdir, pfx)
|
||||||
if ok {
|
|
||||||
forbidden = append(forbidden, strings.Split(envForbidden, ",")...)
|
|
||||||
}
|
}
|
||||||
pathPrefix, ok := os.LookupEnv("PATH_PREFIX")
|
workdir = filepath.ToSlash(workdir)
|
||||||
if ok {
|
|
||||||
workdir = path.Join(workdir, pathPrefix)
|
// 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)
|
// Normalize patterns to slash style; patterns are relative to workdir
|
||||||
for {
|
for i := range patterns {
|
||||||
log.Printf("Running at %s", time.Now().Format(time.RFC3339))
|
patterns[i] = filepath.ToSlash(strings.TrimSpace(patterns[i]))
|
||||||
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)
|
|
||||||
|
|
||||||
_, 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 {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Removing file %s%s%s", PathColor, fullPath, Reset)
|
if info.IsDir() {
|
||||||
err = os.RemoveAll(fullPath)
|
log.Info("Removing directory %s", full)
|
||||||
if err != nil {
|
} else {
|
||||||
log.Printf("Error removing file %s%s%s: %s%v%s", PathColor, fullPath, Reset, ErrorColor, err, Reset)
|
log.Info("Removing file %s", full)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(full); err != nil {
|
||||||
|
log.Error("remove %s: %v", full, err)
|
||||||
continue
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user