コード例 #1
0
ファイル: dev.go プロジェクト: rainycape/gondola
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
}
コード例 #2
0
ファイル: dev.go プロジェクト: rainycape/gondola
func (p *Project) startLocked() error {
	p.port = randomFreePort()
	cmd := p.ProjectCmd()
	log.Infof("Starting %s (%s)", p.Name(), cmdString(cmd))
	p.cmd = cmd
	p.out.Reset()
	p.runError = nil
	p.exitCode = 0
	err := cmd.Start()
	go func() {
		werr := cmd.Wait()
		if cmd == p.cmd {
			// Othewise the process was intentionally killed
			if s := cmd.ProcessState; s != nil {
				exitCode := exitStatus(s)
				p.Lock()
				defer p.Unlock()
				p.runError = werr
				p.exitCode = exitCode
				log.Warningf("%s exited with code %d", p.Name(), exitCode)
			}
		}
	}()
	time.AfterFunc(100*time.Millisecond, p.projectStarted)
	return err
}
コード例 #3
0
ファイル: rm.go プロジェクト: rainycape/gondola
func rmGenCommand(args *command.Args) error {
	dir := "."
	if len(args.Args()) > 0 {
		dir = args.Args()[0]
	}
	re := regexp.MustCompile("(?i).+\\.gen\\..+")
	return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if info != nil && !info.IsDir() && re.MatchString(path) {
			log.Infof("Removing %s", path)
			if err := os.Remove(path); err != nil {
				return err
			}
			dir := filepath.Dir(path)
			if infos, err := ioutil.ReadDir(dir); err == nil && len(infos) == 0 {
				log.Infof("Removing empty dir %s", dir)
				if err := os.Remove(dir); err != nil {
					return err
				}
			}
		}
		return nil
	})
}
コード例 #4
0
ファイル: dev.go プロジェクト: rainycape/gondola
// Build builds the project. If the project was already building, the build
// is restarted.
func (p *Project) Build() {
	builder := &Builder{
		Dir:     p.dir,
		GoFlags: p.goFlags,
		Tags:    p.tags,
	}
	var restarted bool
	p.Lock()
	if p.builder != nil {
		p.builder.Cancel()
		restarted = true
	}
	p.builder = builder
	p.StopMonitoring()
	p.Unlock()
	if err := p.Stop(); err != nil {
		log.Panic(err)
	}
	p.errors = nil
	if !restarted {
		log.Infof("Building %s (%s)", p.Name(), builder.BuildCommandString())
	}
	var err error
	p.errors, err = builder.Build()
	p.Lock()
	defer p.Unlock()
	if p.builder != builder {
		// Canceled by another build
		return
	}
	p.builder = nil
	p.built = time.Now().UTC()
	if err != nil {
		log.Errorf("%d errors building %s", len(p.errors), p.Name())
		p.reloadClients()
	} else {
		if err := p.startLocked(); err != nil {
			log.Panic(err)
		}
	}
	if err := p.StartMonitoring(); err != nil {
		log.Errorf("Error monitoring files for project %s: %s. Development server must be manually restarted.", p.Name(), err)
	}
	// Build dependencies, to speed up future builds
	go func() {
		builder.GoInstallDeps()
	}()
}
コード例 #5
0
ファイル: dev.go プロジェクト: rainycape/gondola
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
}