Przeglądaj źródła

watch: add watch function that traverses up directory structure recursively (#1013)

Dan Miller 6 lat temu
rodzic
commit
0482f9276a
2 zmienionych plików z 46 dodań i 4 usunięć
  1. 24 0
      pkg/watch/notify_test.go
  2. 22 4
      pkg/watch/watcher_naive.go

+ 24 - 0
pkg/watch/notify_test.go

@@ -5,6 +5,7 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
 	"testing"
 	"time"
@@ -309,6 +310,29 @@ func TestWatchBothDirAndFile(t *testing.T) {
 	f.assertEvents(fileB)
 }
 
+func TestWatchNonexistentDirectory(t *testing.T) {
+	f := newNotifyFixture(t)
+	defer f.tearDown()
+
+	root := f.JoinPath("root")
+	err := os.Mkdir(root, 0777)
+	if err != nil {
+		t.Fatal(err)
+	}
+	parent := f.JoinPath("root", "parent")
+	file := f.JoinPath("root", "parent", "a")
+
+	f.watch(file)
+	f.fsync()
+	f.events = nil
+	f.WriteFile(file, "hello")
+	if runtime.GOOS == "darwin" {
+		f.assertEvents(file)
+	} else {
+		f.assertEvents(parent, file)
+	}
+}
+
 type notifyFixture struct {
 	*tempdir.TempDirFixture
 	notify  Notify

+ 22 - 4
pkg/watch/watcher_naive.go

@@ -3,6 +3,7 @@
 package watch
 
 import (
+	"fmt"
 	"log"
 	"os"
 	"path/filepath"
@@ -31,12 +32,11 @@ func (d *naiveNotify) Add(name string) error {
 
 	// if it's a file that doesn't exist, watch its parent
 	if os.IsNotExist(err) {
-		parent := filepath.Join(name, "..")
-		err = d.watcher.Add(parent)
+		err, fileWatched := d.watchUpRecursively(name)
 		if err != nil {
-			return errors.Wrapf(err, "notify.Add(%q)", name)
+			return errors.Wrapf(err, "watchUpRecursively(%q)", name)
 		}
-		d.watchList[parent] = true
+		d.watchList[fileWatched] = true
 	} else if fi.IsDir() {
 		err = d.watchRecursively(name)
 		if err != nil {
@@ -71,6 +71,24 @@ func (d *naiveNotify) watchRecursively(dir string) error {
 	})
 }
 
+func (d *naiveNotify) watchUpRecursively(path string) (error, string) {
+	if path == string(filepath.Separator) {
+		return fmt.Errorf("cannot watch root directory"), ""
+	}
+
+	_, err := os.Stat(path)
+	if err != nil && !os.IsNotExist(err) {
+		return errors.Wrapf(err, "os.Stat(%q)", path), ""
+	}
+
+	if os.IsNotExist(err) {
+		parent := filepath.Dir(path)
+		return d.watchUpRecursively(parent)
+	}
+
+	return d.watcher.Add(path), path
+}
+
 func (d *naiveNotify) Close() error {
 	return d.watcher.Close()
 }