Fix up the filesystem summary logger

This commit is contained in:
2025-11-20 15:37:58 +01:00
parent 610ba20276
commit 88ef93e9af
3 changed files with 48 additions and 27 deletions

View File

@@ -15,6 +15,7 @@ type FileSystem interface {
MkdirAll(path string, perm os.FileMode) error MkdirAll(path string, perm os.FileMode) error
Symlink(source, target string) error Symlink(source, target string) error
Link(source, target string) error Link(source, target string) error
RecordLinkAttempt(kind string, source, target string, err error, dryRun bool)
SummaryLines() []string SummaryLines() []string
IsDryRun() bool IsDryRun() bool
} }
@@ -52,7 +53,10 @@ func (r operationRecord) summaryLine() (string, bool) {
status = "DRY-RUN" status = "DRY-RUN"
} }
return fmt.Sprintf("%s %s -> %s (%s)", kindLabel, r.source, r.target, status), true source := FormatSourcePath(r.source)
target := FormatTargetPath(r.target)
return fmt.Sprintf("%s %s → %s (%s)", kindLabel, source, target, status), true
} }
type baseFileSystem struct { type baseFileSystem struct {
@@ -119,16 +123,20 @@ func (fs *realFileSystem) MkdirAll(path string, perm os.FileMode) error {
func (fs *realFileSystem) Symlink(source, target string) error { func (fs *realFileSystem) Symlink(source, target string) error {
err := os.Symlink(source, target) err := os.Symlink(source, target)
fs.addOperation(opSymlink, source, target, err, false) fs.RecordLinkAttempt(opSymlink, source, target, err, false)
return err return err
} }
func (fs *realFileSystem) Link(source, target string) error { func (fs *realFileSystem) Link(source, target string) error {
err := os.Link(source, target) err := os.Link(source, target)
fs.addOperation(opHardlink, source, target, err, false) fs.RecordLinkAttempt(opHardlink, source, target, err, false)
return err return err
} }
func (fs *realFileSystem) RecordLinkAttempt(kind string, source, target string, err error, dryRun bool) {
fs.addOperation(kind, source, target, err, dryRun)
}
func (fs *realFileSystem) SummaryLines() []string { func (fs *realFileSystem) SummaryLines() []string {
return summarizeOperations(fs.snapshot()) return summarizeOperations(fs.snapshot())
} }
@@ -162,15 +170,19 @@ func (fs *dryRunFileSystem) MkdirAll(path string, perm os.FileMode) error {
} }
func (fs *dryRunFileSystem) Symlink(source, target string) error { func (fs *dryRunFileSystem) Symlink(source, target string) error {
fs.addOperation(opSymlink, source, target, nil, true) fs.RecordLinkAttempt(opSymlink, source, target, nil, true)
return nil return nil
} }
func (fs *dryRunFileSystem) Link(source, target string) error { func (fs *dryRunFileSystem) Link(source, target string) error {
fs.addOperation(opHardlink, source, target, nil, true) fs.RecordLinkAttempt(opHardlink, source, target, nil, true)
return nil return nil
} }
func (fs *dryRunFileSystem) RecordLinkAttempt(kind string, source, target string, err error, dryRun bool) {
fs.addOperation(kind, source, target, err, dryRun)
}
func (fs *dryRunFileSystem) SummaryLines() []string { func (fs *dryRunFileSystem) SummaryLines() []string {
return summarizeOperations(fs.snapshot()) return summarizeOperations(fs.snapshot())
} }

View File

@@ -177,19 +177,28 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) {
return return
} }
linkKind := opSymlink
if instruction.Hard {
linkKind = opHardlink
}
reportErr := func(err error) {
filesystem.RecordLinkAttempt(linkKind, instruction.Source, instruction.Target, err, filesystem.IsDryRun())
status <- err
}
if !FileExists(instruction.Source) { if !FileExists(instruction.Source) {
status <- fmt.Errorf("instruction source %s does not exist", FormatSourcePath(instruction.Source)) reportErr(fmt.Errorf("instruction source %s does not exist", FormatSourcePath(instruction.Source)))
return return
} }
if instruction.Files { if instruction.Files {
info, err := os.Stat(instruction.Source) info, err := os.Stat(instruction.Source)
if err != nil { if err != nil {
status <- fmt.Errorf("could not stat source %s; err: %v", FormatSourcePath(instruction.Source), err) reportErr(fmt.Errorf("could not stat source %s; err: %v", FormatSourcePath(instruction.Source), err))
return return
} }
if info.IsDir() { if info.IsDir() {
status <- fmt.Errorf("source %s is a directory but files=true", FormatSourcePath(instruction.Source)) reportErr(fmt.Errorf("source %s is a directory but files=true", FormatSourcePath(instruction.Source)))
return return
} }
} }
@@ -207,31 +216,31 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) {
if FileExists(instruction.Target) { if FileExists(instruction.Target) {
if instruction.Files { if instruction.Files {
if info, err := os.Stat(instruction.Target); err == nil && info.IsDir() { if info, err := os.Stat(instruction.Target); err == nil && info.IsDir() {
status <- fmt.Errorf("target %s is a directory but files=true", FormatTargetPath(instruction.Target)) reportErr(fmt.Errorf("target %s is a directory but files=true", FormatTargetPath(instruction.Target)))
return return
} }
} }
if instruction.Force { if instruction.Force {
isSymlink, err := IsSymlink(instruction.Target) isSymlink, err := IsSymlink(instruction.Target)
if err != nil { if err != nil {
status <- fmt.Errorf("could not determine whether %s is a sym link or not, stopping; err: %v", reportErr(fmt.Errorf("could not determine whether %s is a sym link or not, stopping; err: %v",
FormatTargetPath(instruction.Target), err) FormatTargetPath(instruction.Target), err))
return return
} }
if instruction.Hard { if instruction.Hard {
info, err := os.Stat(instruction.Target) info, err := os.Stat(instruction.Target)
if err != nil { if err != nil {
status <- fmt.Errorf("could not stat %s, stopping; err: %v", reportErr(fmt.Errorf("could not stat %s, stopping; err: %v",
FormatTargetPath(instruction.Target), err) FormatTargetPath(instruction.Target), err))
return return
} }
if info.Mode().IsRegular() && info.Name() == filepath.Base(instruction.Source) { if info.Mode().IsRegular() && info.Name() == filepath.Base(instruction.Source) {
LogTarget("Overwriting existing file %s", instruction.Target) LogTarget("Overwriting existing file %s", instruction.Target)
err := filesystem.Remove(instruction.Target) err := filesystem.Remove(instruction.Target)
if err != nil { if err != nil {
status <- fmt.Errorf("could not remove existing file %s; err: %v", reportErr(fmt.Errorf("could not remove existing file %s; err: %v",
FormatTargetPath(instruction.Target), err) FormatTargetPath(instruction.Target), err))
return return
} }
} }
@@ -241,27 +250,27 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) {
LogTarget("Removing symlink at %s", instruction.Target) LogTarget("Removing symlink at %s", instruction.Target)
err = filesystem.Remove(instruction.Target) err = filesystem.Remove(instruction.Target)
if err != nil { if err != nil {
status <- fmt.Errorf("failed deleting %s due to %v", reportErr(fmt.Errorf("failed deleting %s due to %v",
FormatTargetPath(instruction.Target), err) FormatTargetPath(instruction.Target), err))
return return
} }
} else { } else {
if !instruction.Delete { if !instruction.Delete {
status <- fmt.Errorf("refusing to delte actual (non symlink) file %s", reportErr(fmt.Errorf("refusing to delte actual (non symlink) file %s",
FormatTargetPath(instruction.Target)) FormatTargetPath(instruction.Target)))
return return
} }
LogImportant("Deleting (!!!) %s", instruction.Target) LogImportant("Deleting (!!!) %s", instruction.Target)
err = filesystem.RemoveAll(instruction.Target) err = filesystem.RemoveAll(instruction.Target)
if err != nil { if err != nil {
status <- fmt.Errorf("failed deleting %s due to %v", reportErr(fmt.Errorf("failed deleting %s due to %v",
FormatTargetPath(instruction.Target), err) FormatTargetPath(instruction.Target), err))
return return
} }
} }
} else { } else {
status <- fmt.Errorf("target %s exists - handle manually or set the 'forced' flag (3rd field)", reportErr(fmt.Errorf("target %s exists - handle manually or set the 'forced' flag (3rd field)",
FormatTargetPath(instruction.Target)) FormatTargetPath(instruction.Target)))
return return
} }
} }
@@ -270,8 +279,8 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) {
if _, err := os.Stat(targetDir); os.IsNotExist(err) { if _, err := os.Stat(targetDir); os.IsNotExist(err) {
err = filesystem.MkdirAll(targetDir, 0755) err = filesystem.MkdirAll(targetDir, 0755)
if err != nil { if err != nil {
status <- fmt.Errorf("failed creating directory %s due to %v", reportErr(fmt.Errorf("failed creating directory %s due to %v",
FormatTargetPath(targetDir), err) FormatTargetPath(targetDir), err))
return return
} }
} }

View File

@@ -420,8 +420,8 @@ func TestLinkInstruction_RunAsync(t *testing.T) {
summary := filesystem.SummaryLines() summary := filesystem.SummaryLines()
assert.Equal(t, 1, len(summary)) assert.Equal(t, 1, len(summary))
assert.Contains(t, summary[0], "DRY-RUN") assert.Contains(t, summary[0], "DRY-RUN")
assert.Contains(t, summary[0], srcFile) assert.Contains(t, summary[0], FormatSourcePath(srcFile))
assert.Contains(t, summary[0], target) assert.Contains(t, summary[0], FormatTargetPath(target))
}) })
} }