コード例 #1
0
ファイル: main.go プロジェクト: mattn/modd
func main() {
	kingpin.Version(watch.Version)
	kingpin.Parse()

	log := termlog.NewLog()
	if *debug {
		log.Enable("debug")
		watch.Logger = log
	}

	ret, err := ioutil.ReadFile(*file)
	if err != nil {
		kingpin.Fatalf("%s", err)
	}
	cnf, err := conf.Parse(*file, string(ret))
	if err != nil {
		kingpin.Fatalf("%s", err)
	}
	watchfile := *file
	if *noconf {
		watchfile = ""
	}

	for {
		cnf = run(log, cnf, watchfile)
		if cnf == nil {
			break
		}
	}
}
コード例 #2
0
ファイル: modd_test.go プロジェクト: shaunstanislaus/modd
func _testWatch(t *testing.T, modfunc func(), trigger string, expected []string) {
	defer utils.WithTempDir(t)()

	err := os.MkdirAll("a", 0777)
	if err != nil {
		t.Fatal(err)
	}
	err = os.MkdirAll("b", 0777)
	if err != nil {
		t.Fatal(err)
	}
	confTxt := `
        ** {
            prep: echo ":all:" @mods
        }
        a/** {
            prep: echo ":a:" @mods
        }
        b/** {
            prep: echo ":b:" @mods
        }
    `
	cnf, err := conf.Parse("test", confTxt)
	if err != nil {
		t.Fatal(err)
	}

	lt := termlog.NewLogTest()

	modchan := make(chan *watch.Mod, 1024)
	cback := func() {
		// There's some race condition in rjeczalik/notify. If we don't wait a
		// bit here, we sometimes don't receive notifications for our changes.
		time.Sleep(200 * time.Millisecond)
		start := time.Now()
		modfunc()
		for {
			if strings.Contains(lt.String(), trigger) {
				break
			}
			if time.Now().Sub(start) > timeout {
				fmt.Println("Timeout!")
				break
			}
			time.Sleep(50 * time.Millisecond)
		}
		modchan <- nil
	}
	_, err = runOnChan(modchan, cback, lt.Log, cnf, "", nil)
	if err != nil {
		t.Fatalf("runOnChan: %s", err)
	}
	ret := events(lt.String())

	if !reflect.DeepEqual(ret, expected) {
		t.Errorf("Expected\n%#v\nGot\n%#v", expected, ret)
	}
}
コード例 #3
0
ファイル: modd.go プロジェクト: cortesi/modd
// ReadConfig parses the configuration file in ConfPath
func (mr *ModRunner) ReadConfig() error {
	ret, err := ioutil.ReadFile(mr.ConfPath)
	if err != nil {
		return fmt.Errorf("Error reading config file %s: %s", mr.ConfPath, err)
	}
	newcnf, err := conf.Parse(mr.ConfPath, string(ret))
	if err != nil {
		return fmt.Errorf("Error reading config file %s: %s", mr.ConfPath, err)
	}

	shellMethod := newcnf.GetVariables()[shellVarName]
	if !shell.Has(shellMethod) {
		return fmt.Errorf("No shell interface %q", shellMethod)
	}

	newcnf.CommonExcludes(CommonExcludes)
	mr.Config = newcnf
	return nil
}
コード例 #4
0
ファイル: main.go プロジェクト: shaunstanislaus/modd
func main() {
	kingpin.Version(modd.Version)
	kingpin.Parse()

	if *ignores {
		for _, patt := range modd.CommonExcludes {
			fmt.Println(patt)
		}
		os.Exit(0)
	}

	log := termlog.NewLog()
	if *debug {
		log.Enable("debug")
		watch.Logger = log
	}

	ret, err := ioutil.ReadFile(*file)
	if err != nil {
		kingpin.Fatalf("%s", err)
	}
	cnf, err := conf.Parse(*file, string(ret))
	if err != nil {
		kingpin.Fatalf("%s", err)
	}
	watchfile := *file
	if *noconf {
		watchfile = ""
	}

	notifiers := []notify.Notifier{}
	if *doNotify {
		n := notify.PlatformNotifier()
		if n == nil {
			log.Shout("Could not find a desktop notifier")
		} else {
			notifiers = append(notifiers, n)
		}
	}
	if *beep {
		notifiers = append(notifiers, &notify.BeepNotifier{})
	}

	if *prep {
		err := modd.PrepOnly(log, cnf, notifiers)
		if err != nil {
			log.Shout("%s", err)
		}
	} else {
		for {
			cnf.CommonExcludes(modd.CommonExcludes)
			cnf, err = modd.Run(log, cnf, watchfile, notifiers)
			if err != nil {
				log.Shout("%s", err)
				break
			}
			if cnf == nil {
				break
			}
		}
	}
}
コード例 #5
0
ファイル: modd.go プロジェクト: shaunstanislaus/modd
// Gives control of chan to caller
func runOnChan(modchan chan *watch.Mod, readyCallback func(), log termlog.TermLog, cnf *conf.Config, watchconf string, notifiers []notify.Notifier) (*conf.Config, error) {
	err := PrepOnly(log, cnf, notifiers)
	if err != nil {
		return nil, err
	}

	dworld, err := NewDaemonWorld(cnf, log)
	if err != nil {
		return nil, err
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	defer signal.Reset(os.Interrupt, os.Kill)
	defer dworld.Shutdown(os.Kill)
	go func() {
		dworld.Shutdown(<-c)
		os.Exit(0)
	}()

	dworld.Start()
	watchpaths := cnf.WatchPatterns()
	if watchconf != "" {
		watchpaths = append(watchpaths, filepath.Dir(watchconf))
	}

	// FIXME: This takes a long time. We could start it in parallel with the
	// first process run in a goroutine
	watcher, err := watch.Watch(watchpaths, lullTime, modchan)
	if err != nil {
		return nil, fmt.Errorf("Error watching: %s", err)
	}
	defer watcher.Stop()
	go readyCallback()

	for mod := range modchan {
		if mod == nil {
			break
		}
		if watchconf != "" && mod.Has(watchconf) {
			ret, err := ioutil.ReadFile(watchconf)
			if err != nil {
				log.Warn("Reloading config - error reading %s: %s", watchconf, err)
				continue
			}
			newcnf, err := conf.Parse(watchconf, string(ret))
			if err != nil {
				log.Warn("Reloading config - error reading %s: %s", watchconf, err)
				continue
			}
			log.Notice("Reloading config %s", watchconf)
			return newcnf, nil
		}
		log.SayAs("debug", "Delta: \n%s", mod.String())
		for i, b := range cnf.Blocks {
			lmod, err := mod.Filter(b.Include, b.Exclude)
			if err != nil {
				log.Shout("Error filtering events: %s", err)
				continue
			}
			if lmod.Empty() {
				continue
			}
			err = RunPreps(b, cnf.GetVariables(), lmod, log, notifiers)
			if err != nil {
				if _, ok := err.(ProcError); ok {
					continue
				} else {
					return nil, err
				}
			}
			dworld.DaemonPens[i].Restart()
		}
	}
	return nil, nil
}
コード例 #6
0
ファイル: main.go プロジェクト: mattn/modd
func run(log termlog.TermLog, cnf *conf.Config, watchconf string) *conf.Config {
	modchan := make(chan *watch.Mod, 1024)
	if *ignores {
		for _, patt := range watch.CommonExcludes {
			fmt.Println(patt)
		}
		os.Exit(0)
	}

	daemonPens := make([]*modd.DaemonPen, len(cnf.Blocks))
	for i, b := range cnf.Blocks {
		if !b.NoCommonFilter {
			b.Exclude = append(b.Exclude, watch.CommonExcludes...)
		}
		cnf.Blocks[i] = b

		_, err := prepsAndNotify(b, cnf.GetVariables(), nil, log)
		if err != nil {
			log.Shout("%s", err)
			return nil
		}

		d := modd.DaemonPen{}
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt, os.Kill)
		go func() {
			d.Shutdown(<-c)
			os.Exit(0)
		}()
		if !*prep {
			d.Start(b.Daemons, cnf.GetVariables(), log)
		}
		daemonPens[i] = &d
	}
	if *prep {
		os.Exit(0)
	}

	watchpaths := cnf.WatchPaths()
	if watchconf != "" {
		watchpaths = append(watchpaths, watchconf)
	}

	// FIXME: This takes a long time. We could start it in parallel with the
	// first process run in a goroutine
	watcher, err := watch.Watch(watchpaths, lullTime, modchan)
	defer watcher.Stop()
	if err != nil {
		kingpin.Fatalf("Fatal error: %s", err)
	}

	for mod := range modchan {
		if watchconf != "" && mod.Has(watchconf) {
			ret, err := ioutil.ReadFile(watchconf)
			if err != nil {
				log.Warn("Reloading config - error reading %s: %s", watchconf, err)
				continue
			}
			newcnf, err := conf.Parse(*file, string(ret))
			if err != nil {
				log.Warn("Reloading config - error reading %s: %s", watchconf, err)
				continue
			}
			log.Notice("Reloading config %s", watchconf)
			return newcnf
		}
		if mod == nil {
			break
		}
		log.SayAs("debug", "Delta: \n%s", mod.String())
		for i, b := range cnf.Blocks {
			lmod, err := mod.Filter(b.Include, b.Exclude)
			if err != nil {
				log.Shout("Error filtering events: %s", err)
				continue
			}
			if lmod.Empty() {
				continue
			}

			proceed, err := prepsAndNotify(b, cnf.GetVariables(), lmod, log)
			if err != nil {
				log.Shout("%s", err)
				return nil
			}
			if !proceed {
				continue
			}
			daemonPens[i].Restart()
		}
	}
	return nil
}
コード例 #7
0
ファイル: modd_test.go プロジェクト: cortesi/modd
func _testWatch(t *testing.T, modfunc func(), trigger string, expected []string) {
	defer utils.WithTempDir(t)()

	err := os.MkdirAll("a", 0777)
	if err != nil {
		t.Fatal(err)
	}

	err = os.MkdirAll("b", 0777)
	if err != nil {
		t.Fatal(err)
	}

	touch(t, "a/initial")
	// There's some race condition in rjeczalik/notify. If we don't wait a bit
	// here, we sometimes receive notifications for the change above even
	// though we haven't started the watcher.
	time.Sleep(200 * time.Millisecond)

	confTxt := `
        ** {
            prep +onchange: echo ":skipit:" @mods
            prep: echo ":all:" @mods
        }
        a/** {
            prep: echo ":a:" @mods
        }
        b/** {
            prep: echo ":b:" @mods
        }
    `
	cnf, err := conf.Parse("test", confTxt)
	if err != nil {
		t.Fatal(err)
	}

	lt := termlog.NewLogTest()

	modchan := make(chan *moddwatch.Mod, 1024)
	cback := func() {
		start := time.Now()
		modfunc()
		for {
			if strings.Contains(lt.String(), trigger) {
				break
			}
			if time.Now().Sub(start) > timeout {
				fmt.Println("Timeout!")
				break
			}
			time.Sleep(50 * time.Millisecond)
		}
		modchan <- nil
	}

	mr := ModRunner{
		Log:    lt.Log,
		Config: cnf,
	}

	err = mr.runOnChan(modchan, cback)
	if err != nil {
		t.Fatalf("runOnChan: %s", err)
	}

	ret := events(lt.String())

	if !reflect.DeepEqual(ret, expected) {
		t.Errorf("Expected\n%#v\nGot\n%#v", expected, ret)
	}
}