Implement saving snapshots to a database

This commit is contained in:
2025-07-20 11:38:08 +02:00
parent 22f991e72e
commit b785d24a08
6 changed files with 60 additions and 8 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.exe *.exe
.qodo .qodo
*.sqlite

1
go.mod
View File

@@ -38,6 +38,7 @@ require (
) )
require ( require (
git.site.quack-lab.dev/dave/cyutils v1.0.0
github.com/go-git/go-git/v5 v5.14.0 github.com/go-git/go-git/v5 v5.14.0
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
golang.org/x/net v0.35.0 // indirect golang.org/x/net v0.35.0 // indirect

2
go.sum
View File

@@ -2,6 +2,8 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
git.site.quack-lab.dev/dave/cylogger v1.2.2 h1:4xUXASEBlG9NiGxh7f57xHh9imW4unHzakIEpQoKC5E= git.site.quack-lab.dev/dave/cylogger v1.2.2 h1:4xUXASEBlG9NiGxh7f57xHh9imW4unHzakIEpQoKC5E=
git.site.quack-lab.dev/dave/cylogger v1.2.2/go.mod h1:VS9MI4Y/cwjCBZgel7dSfCQlwtAgHmfvixOoBgBhtKg= git.site.quack-lab.dev/dave/cylogger v1.2.2/go.mod h1:VS9MI4Y/cwjCBZgel7dSfCQlwtAgHmfvixOoBgBhtKg=
git.site.quack-lab.dev/dave/cyutils v1.0.0 h1:yp/jkM2M7UZ+UIQuy+vPI7yDvTUdpbEdFL8h0lzUTvA=
git.site.quack-lab.dev/dave/cyutils v1.0.0/go.mod h1:luGNFimplFhkpRLebhkVTNjG2wYfPAs+pu+UIMhBYbE=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=

22
main.go
View File

@@ -34,8 +34,6 @@ func main() {
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <pattern> <lua_expression> <...files_or_globs>\n", os.Args[0]) fmt.Fprintf(os.Stderr, "Usage: %s [options] <pattern> <lua_expression> <...files_or_globs>\n", os.Args[0])
fmt.Fprintf(os.Stderr, "\nOptions:\n") fmt.Fprintf(os.Stderr, "\nOptions:\n")
fmt.Fprintf(os.Stderr, " -git\n")
fmt.Fprintf(os.Stderr, " Use git to manage files\n")
fmt.Fprintf(os.Stderr, " -reset\n") fmt.Fprintf(os.Stderr, " -reset\n")
fmt.Fprintf(os.Stderr, " Reset files to their original state\n") fmt.Fprintf(os.Stderr, " Reset files to their original state\n")
fmt.Fprintf(os.Stderr, " -loglevel string\n") fmt.Fprintf(os.Stderr, " -loglevel string\n")
@@ -83,7 +81,6 @@ func main() {
logger.Trace("Regex: %s", command.Regex) logger.Trace("Regex: %s", command.Regex)
logger.Trace("Files: %v", command.Files) logger.Trace("Files: %v", command.Files)
logger.Trace("Lua: %s", command.Lua) logger.Trace("Lua: %s", command.Lua)
logger.Trace("Git: %t", command.Git)
logger.Trace("Reset: %t", command.Reset) logger.Trace("Reset: %t", command.Reset)
logger.Trace("Isolate: %t", command.Isolate) logger.Trace("Isolate: %t", command.Isolate)
logger.Trace("LogLevel: %s", command.LogLevel) logger.Trace("LogLevel: %s", command.LogLevel)
@@ -140,16 +137,22 @@ func main() {
logger.Debug("Created logger for command %q with log level %s", cmdName, cmdLogLevel.String()) logger.Debug("Created logger for command %q with log level %s", cmdName, cmdLogLevel.String())
} }
db, err := utils.GetDB()
if err != nil {
logger.Error("Failed to get database: %v", err)
return
}
for file, association := range associations { for file, association := range associations {
workers <- struct{}{} workers <- struct{}{}
wg.Add(1) wg.Add(1)
logger.SafeGoWithArgs(func(args ...interface{}) { logger.SafeGoWithArgs(func(args ...interface{}) {
defer func() { <-workers }() defer func() { <-workers }()
defer wg.Done() defer wg.Done()
// Track per-file processing time // Track per-file processing time
fileStartTime := time.Now() fileStartTime := time.Now()
logger.Debug("Reading file %q", file)
fileData, err := os.ReadFile(file) fileData, err := os.ReadFile(file)
if err != nil { if err != nil {
logger.Error("Failed to read file %q: %v", file, err) logger.Error("Failed to read file %q: %v", file, err)
@@ -157,18 +160,28 @@ func main() {
} }
fileDataStr := string(fileData) fileDataStr := string(fileData)
logger.Debug("Saving file %q to database", file)
err = db.SaveFile(file, fileData)
if err != nil {
logger.Error("Failed to save file %q to database: %v", file, err)
return
}
logger.Debug("Running isolate commands for file %q", file)
fileDataStr, err = RunIsolateCommands(association, file, fileDataStr, &fileMutex) fileDataStr, err = RunIsolateCommands(association, file, fileDataStr, &fileMutex)
if err != nil { if err != nil {
logger.Error("Failed to run isolate commands for file %q: %v", file, err) logger.Error("Failed to run isolate commands for file %q: %v", file, err)
return return
} }
logger.Debug("Running other commands for file %q", file)
fileDataStr, err = RunOtherCommands(file, fileDataStr, association, &fileMutex, commandLoggers) fileDataStr, err = RunOtherCommands(file, fileDataStr, association, &fileMutex, commandLoggers)
if err != nil { if err != nil {
logger.Error("Failed to run other commands for file %q: %v", file, err) logger.Error("Failed to run other commands for file %q: %v", file, err)
return return
} }
logger.Debug("Writing file %q", file)
err = os.WriteFile(file, []byte(fileDataStr), 0644) err = os.WriteFile(file, []byte(fileDataStr), 0644)
if err != nil { if err != nil {
logger.Error("Failed to write file %q: %v", file, err) logger.Error("Failed to write file %q: %v", file, err)
@@ -263,7 +276,6 @@ func CreateExampleConfig() {
Regex: "price=\"(\\d+)\"", Regex: "price=\"(\\d+)\"",
Lua: "if num(v1) < 100 then return v1 * 1.5 else return v1 end", Lua: "if num(v1) < 100 then return v1 * 1.5 else return v1 end",
Files: []string{"items/*.xml", "shop/*.xml"}, Files: []string{"items/*.xml", "shop/*.xml"},
Git: true,
LogLevel: "DEBUG", LogLevel: "DEBUG",
}, },
{ {

View File

@@ -1,8 +1,11 @@
package utils package utils
import ( import (
"fmt"
"path/filepath" "path/filepath"
"time"
"git.site.quack-lab.dev/dave/cylogger"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
) )
@@ -10,6 +13,13 @@ import (
type DB interface { type DB interface {
DB() *gorm.DB DB() *gorm.DB
Raw(sql string, args ...any) *gorm.DB Raw(sql string, args ...any) *gorm.DB
SaveFile(filePath string, fileData []byte) error
}
type FileSnapshot struct {
Date time.Time `gorm:"primaryKey"`
FilePath string `gorm:"primaryKey"`
FileData []byte `gorm:"type:blob"`
} }
type DBWrapper struct { type DBWrapper struct {
@@ -30,11 +40,11 @@ func GetDB() (DB, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
db.AutoMigrate(&FileSnapshot{})
return &DBWrapper{db: db}, nil return &DBWrapper{db: db}, nil
} }
// Just a wrapper // Just a wrapper
func (db *DBWrapper) Raw(sql string, args ...any) *gorm.DB { func (db *DBWrapper) Raw(sql string, args ...any) *gorm.DB {
return db.db.Raw(sql, args...) return db.db.Raw(sql, args...)
@@ -43,3 +53,30 @@ func (db *DBWrapper) Raw(sql string, args ...any) *gorm.DB {
func (db *DBWrapper) DB() *gorm.DB { func (db *DBWrapper) DB() *gorm.DB {
return db.db return db.db
} }
func (db *DBWrapper) FileExists(filePath string) (bool, error) {
var count int64
err := db.db.Model(&FileSnapshot{}).Where("file_path = ?", filePath).Count(&count).Error
return count > 0, err
}
func (db *DBWrapper) SaveFile(filePath string, fileData []byte) error {
log := cylogger.Default.WithPrefix(fmt.Sprintf("SaveFile: %q", filePath))
exists, err := db.FileExists(filePath)
if err != nil {
log.Error("Error checking if file exists: %v", err)
return err
}
log.Debug("File exists: %t", exists)
// Nothing to do, file already exists
if exists {
log.Debug("File already exists, skipping save")
return nil
}
log.Debug("Saving file to database")
return db.db.Create(&FileSnapshot{
Date: time.Now(),
FilePath: filePath,
FileData: fileData,
}).Error
}

View File

@@ -16,7 +16,6 @@ type ModifyCommand struct {
Regex string `yaml:"regex"` Regex string `yaml:"regex"`
Lua string `yaml:"lua"` Lua string `yaml:"lua"`
Files []string `yaml:"files"` Files []string `yaml:"files"`
Git bool `yaml:"git"`
Reset bool `yaml:"reset"` Reset bool `yaml:"reset"`
LogLevel string `yaml:"loglevel"` LogLevel string `yaml:"loglevel"`
Isolate bool `yaml:"isolate"` Isolate bool `yaml:"isolate"`