func devCommand(args *command.Args, opts *devOptions) error { if !opts.Verbose { log.SetLevel(log.LInfo) } dir := opts.Dir if dir == "" { dir = "." } path, err := filepath.Abs(dir) if err != nil { return err } configPath := findConfig(dir, opts.Config) if configPath == "" { name := opts.Config if name == "" { name = fmt.Sprintf("(tried %s)", strings.Join(autoConfigNames(), ", ")) } log.Panicf("can't find configuration file %s in %s", name, dir) } log.Infof("Using config file %s", configPath) p := NewProject(path, configPath) p.port = opts.Port p.tags = opts.Tags p.goFlags = opts.GoFlags p.noDebug = opts.NoDebug p.noCache = opts.NoCache p.profile = opts.Profile go p.Build() log.Infof("Starting Gondola development server on port %d (press Control+C to exit)", p.port) if !opts.NoBrowser { time.AfterFunc(time.Second, func() { host := "localhost" if sshConn := os.Getenv("SSH_CONNECTION"); sshConn != "" { parts := strings.Split(sshConn, " ") // e.g. SSH_CONNECTION="10.211.55.2 56989 10.211.55.8 22" if len(parts) == 4 { if net.ParseIP(parts[2]) != nil { host = parts[2] } } } url := fmt.Sprintf("http://%s:%d", host, p.App.Config().Port) if err := browser.Open(url); err != nil { log.Errorf("error opening browser: open %s manually (error was %s)", url, err) } }) } p.Listen() return nil }
// MustListenAndServe works like ListenAndServe, but panics if // there's an error func (app *App) MustListenAndServe() { err := app.ListenAndServe() if err != nil { log.Panicf("error listening on port %d: %s", app.cfg.Port, err) } }
func (p *Project) StartMonitoring() error { watcher, err := newFSWatcher() if err != nil { return err } var files []string pkgs, err := p.Packages() if err != nil && len(pkgs) == 0 { // Monitor just the files in the project directory infos, err2 := ioutil.ReadDir(p.dir) if err2 != nil { // Return the original error, since it will show // why the the packages failed to import return err } for _, entry := range infos { if !entry.IsDir() { files = append(files, filepath.Join(p.dir, entry.Name())) } } } watcher.IsValidFile = func(path string) bool { return path == p.configPath || isSource(path) } var timer *time.Timer var mu sync.Mutex onChanged := func(path string) { if path == p.configPath { log.Infof("Config file %s changed, restarting...", p.configPath) if err := p.Stop(); err != nil { log.Errorf("Error stopping %s: %s", p.Name(), err) } if err := p.Start(); err != nil { log.Panicf("Error starting %s: %s", p.Name(), err) } } else { // Merge multiple events arriving in // a small time window mu.Lock() if timer == nil { timer = time.AfterFunc(10*time.Millisecond, func() { mu.Lock() timer = nil p.Build() mu.Unlock() }) } mu.Unlock() } } watcher.Added = onChanged watcher.Removed = onChanged watcher.Changed = onChanged if len(files) > 0 { // Packages could not be imported and we're // using files as a fallback. for _, f := range files { if err := watcher.Add(f); err != nil { return err } } } else { if err := watcher.AddPackages(pkgs); err != nil { return err } } if err := watcher.Add(p.configPath); err != nil { return err } p.watcher = watcher return nil }