diff options
| author | Bernhard Guillon <Bernhard.Guillon@begu.org> | 2026-07-06 21:15:17 +0200 |
|---|---|---|
| committer | Bernhard Guillon <Bernhard.Guillon@begu.org> | 2026-07-06 21:15:17 +0200 |
| commit | b505ac2fe010fcc24b774c313d33dc099214e8ed (patch) | |
| tree | ff3f6472c84abd45e7918722e70ee681b859b780 /sync.go | |
| download | sourcewatch-b505ac2fe010fcc24b774c313d33dc099214e8ed.tar.gz sourcewatch-b505ac2fe010fcc24b774c313d33dc099214e8ed.zip | |
Initial project scaffolding
- main.go: CLI entry point with flag parsing
- gitignore.go: .gitignore loading and parsing
- sync.go: initial sync with manifest tracking
- watcher.go: fsnotify-based file watcher with debouncing
- Dockerfile: multi-stage build for Alpine runtime
Diffstat (limited to 'sync.go')
| -rw-r--r-- | sync.go | 106 |
1 files changed, 106 insertions, 0 deletions
@@ -0,0 +1,106 @@ +package main + +import ( + "io" + "log" + "os" + "path/filepath" + "strings" + + ignore "github.com/sabhiram/go-gitignore" +) + +type FileMeta struct { + Size int64 + Mode os.FileMode + Symlink string +} + +type Manifest struct { + Files map[string]FileMeta +} + +func initialSync(hostDir, dataDir string, rules *ignore.GitIgnore, dryRun bool) (*Manifest, error) { + manifest := &Manifest{Files: make(map[string]FileMeta)} + + err := filepath.Walk(hostDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + relPath, err := filepath.Rel(hostDir, path) + if err != nil { + return err + } + + if relPath == "." { + return nil + } + + if relPath == ".git" || strings.HasPrefix(relPath, ".git"+string(filepath.Separator)) { + return filepath.SkipDir + } + + if rules.MatchesPath(relPath) { + if info.IsDir() { + return filepath.SkipDir + } + return nil + } + + destPath := filepath.Join(dataDir, relPath) + + linkTarget := "" + if info.Mode()&os.ModeSymlink != 0 { + linkTarget, err = os.Readlink(path) + if err != nil { + return err + } + } + + meta := FileMeta{ + Size: info.Size(), + Mode: info.Mode(), + Symlink: linkTarget, + } + manifest.Files[relPath] = meta + + if dryRun { + log.Printf("[dry-run] would sync: %s", relPath) + return nil + } + + if info.IsDir() { + return os.MkdirAll(destPath, info.Mode()) + } + + if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { + return err + } + + if linkTarget != "" { + return os.Symlink(linkTarget, destPath) + } + + return copyFile(path, destPath, info.Mode()) + }) + + return manifest, err +} + +func copyFile(src, dst string, mode os.FileMode) error { + srcFile, err := os.Open(src) + if err != nil { + return err + } + defer srcFile.Close() + + dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) + if err != nil { + return err + } + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + return err +} |
