esync

Directory watching and remote syncing
Log | Files | Refs | README | LICENSE

commit f1b312c573720555f8aaa74f82cb84215837a66b
parent a4c9505fc628315db5c7c543bf1d9e51f31c1fbc
Author: Erik Loualiche <eloualic@umn.edu>
Date:   Sun, 22 Mar 2026 10:30:59 -0500

feat: esync check reports broken symlinks with their dangling targets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
Mcmd/check.go | 20++++++++++++++++++++
1 file changed, 20 insertions(+), 0 deletions(-)

diff --git a/cmd/check.go b/cmd/check.go @@ -19,6 +19,7 @@ import ( var ( greenHeader = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("10")) yellowHeader = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("11")) + redHeader = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("9")) dimText = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) ) @@ -87,6 +88,7 @@ func printPreview(cfg *config.Config) error { var included []fileEntry var excluded []fileEntry + var brokenLinks []fileEntry var includedSize int64 err := filepath.Walk(localDir, func(path string, info os.FileInfo, err error) error { @@ -104,6 +106,15 @@ func printPreview(cfg *config.Config) error { return nil } + // Detect broken symlinks (Walk uses Lstat, so symlinks show up with err==nil) + if info.Mode()&os.ModeSymlink != 0 { + if _, statErr := os.Stat(path); statErr != nil { + target, _ := os.Readlink(path) + brokenLinks = append(brokenLinks, fileEntry{path: rel, rule: target}) + } + return nil // skip all symlinks from included/excluded lists + } + // Check against ignore patterns for _, pattern := range patterns { if matchesIgnorePattern(rel, info, pattern) { @@ -168,6 +179,15 @@ func printPreview(cfg *config.Config) error { } fmt.Println() + // --- Broken symlinks --- + if len(brokenLinks) > 0 { + fmt.Println(redHeader.Render(" Broken symlinks:")) + for _, f := range brokenLinks { + fmt.Printf(" %-40s %s\n", f.path, dimText.Render("-> "+f.rule)) + } + fmt.Println() + } + // --- Totals --- totals := fmt.Sprintf(" %d files included (%s) | %d excluded", len(included), formatSize(includedSize), len(excluded))