<commit_before>package main

import (
	"errors"
	"path"
	"time"

	"github.com/libgit2/git2go"
)

type RepoConfig struct {
	Name  string
	Email string
}

func isGitRepo(checkpath string) (bool, error) {
	ceiling := []string{checkpath}

	repopath, err := git.Discover(checkpath, false, ceiling)
	nonRepoErr := errors.New("Could not find repository from '" + checkpath + "'")
	if err != nil && err.Error() != nonRepoErr.Error() {
		return false, err
	}
	if err.Error() == nonRepoErr.Error() {
		return false, nil
	}
	// the path is the parent of the repo, which appends '.git'
	// to the path
	dirpath := path.Dir(path.Clean(repopath))
	if dirpath == checkpath {
		return true, nil
	}
	return false, nil
}

func getRepoConfig(repo *git.Repository) (RepoConfig, error) {
	config, err := repo.Config()
	if err != nil {
		return RepoConfig{}, err
	}
	name, err := config.LookupString("user.name")
	if err != nil {
		return RepoConfig{}, err
	}
	email, err := config.LookupString("user.email")
	if err != nil {
		return RepoConfig{}, err
	}
	repoconf := RepoConfig{
		Name:  name,
		Email: email,
	}
	return repoconf, nil
}

func gitAddCommitFile(repopath, filename, message string) (commitId string, err error) {
	repo, err := git.OpenRepository(repopath)
	if err != nil {
		return "", err
	}

	config, err := getRepoConfig(repo)
	if err != nil {
		return "", err
	}

	index, err := repo.Index()
	if err != nil {
		return "", err
	}

	err = index.AddByPath(filename)
	if err != nil {
		return "", err
	}

	err = index.Write()
	if err != nil {
		return "", err
	}

	treeId, err := index.WriteTree()
	if err != nil {
		return "", err
	}

	// new file is now staged, so we have to create a commit
	sig := &git.Signature{
		Name:  config.Name,
		Email: config.Email,
		When:  time.Now(),
	}

	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return "", err
	}

	var commit *git.Oid
	haslog, err := repo.HasLog("HEAD")
	if err != nil {
		return "", err
	}
	if !haslog {
		// In this case, the repo has been initialized, but nothing has ever been committed
		commit, err = repo.CreateCommit("HEAD", sig, sig, message, tree)
		if err != nil {
			return "", err
		}
	} else {
		// In this case, the repo has commits
		currentBranch, err := repo.Head()
		if err != nil {
			return "", err
		}
		currentTip, err := repo.LookupCommit(currentBranch.Target())
		if err != nil {
			return "", err
		}
		commit, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip)
		if err != nil {
			return "", err
		}
	}

	return commit.String(), nil
}
<commit_msg>Do not break if a dir IS a repo<commit_after>package main

import (
	"errors"
	"path"
	"time"

	"github.com/libgit2/git2go"
)

type RepoConfig struct {
	Name  string
	Email string
}

func isGitRepo(checkpath string) (bool, error) {
	ceiling := []string{checkpath}

	repopath, err := git.Discover(checkpath, false, ceiling)
	nonRepoErr := errors.New("Could not find repository from '" + checkpath + "'")
	if err != nil && err.Error() != nonRepoErr.Error() {
		return false, err
	}
	if err != nil && err.Error() == nonRepoErr.Error() {
		return false, nil
	}
	// the path is the parent of the repo, which appends '.git'
	// to the path
	dirpath := path.Dir(path.Clean(repopath))
	if dirpath == checkpath {
		return true, nil
	}
	return false, nil
}

func getRepoConfig(repo *git.Repository) (RepoConfig, error) {
	config, err := repo.Config()
	if err != nil {
		return RepoConfig{}, err
	}
	name, err := config.LookupString("user.name")
	if err != nil {
		return RepoConfig{}, err
	}
	email, err := config.LookupString("user.email")
	if err != nil {
		return RepoConfig{}, err
	}
	repoconf := RepoConfig{
		Name:  name,
		Email: email,
	}
	return repoconf, nil
}

func gitAddCommitFile(repopath, filename, message string) (commitId string, err error) {
	repo, err := git.OpenRepository(repopath)
	if err != nil {
		return "", err
	}

	config, err := getRepoConfig(repo)
	if err != nil {
		return "", err
	}

	index, err := repo.Index()
	if err != nil {
		return "", err
	}

	err = index.AddByPath(filename)
	if err != nil {
		return "", err
	}

	err = index.Write()
	if err != nil {
		return "", err
	}

	treeId, err := index.WriteTree()
	if err != nil {
		return "", err
	}

	// new file is now staged, so we have to create a commit
	sig := &git.Signature{
		Name:  config.Name,
		Email: config.Email,
		When:  time.Now(),
	}

	tree, err := repo.LookupTree(treeId)
	if err != nil {
		return "", err
	}

	var commit *git.Oid
	haslog, err := repo.HasLog("HEAD")
	if err != nil {
		return "", err
	}
	if !haslog {
		// In this case, the repo has been initialized, but nothing has ever been committed
		commit, err = repo.CreateCommit("HEAD", sig, sig, message, tree)
		if err != nil {
			return "", err
		}
	} else {
		// In this case, the repo has commits
		currentBranch, err := repo.Head()
		if err != nil {
			return "", err
		}
		currentTip, err := repo.LookupCommit(currentBranch.Target())
		if err != nil {
			return "", err
		}
		commit, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip)
		if err != nil {
			return "", err
		}
	}

	return commit.String(), nil
}
<|endoftext|>


<commit_before>package runtimes

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/aisk/chrysanthemum"
	"github.com/facebookgo/parseignore"
	"github.com/facebookgo/symwalk"
	"github.com/fsnotify/fsnotify"
	"github.com/leancloud/lean-cli/utils"
)

// ErrInvalidRuntime means the project's structure is not a valid LeanEngine project
var ErrInvalidRuntime = errors.New("invalid runtime")

type filesPattern struct {
	Includes []string
	Excludes []string
}

// Runtime stands for a language runtime
type Runtime struct {
	command     *exec.Cmd
	ProjectPath string
	Name        string
	Exec        string
	Args        []string
	WatchFiles  []string
	Envs        []string
	Port        string
	// DeployFiles is the patterns for source code to deploy to the remote server
	DeployFiles filesPattern
	// Errors is the channel that receives the command's error result
	Errors chan error
}

// Run the project, and watch file changes
func (runtime *Runtime) Run() {
	go func() {
		for {
			runtime.command = exec.Command(runtime.Exec, runtime.Args...)
			runtime.command.Env = os.Environ()
			runtime.command.Stdout = os.Stdout
			runtime.command.Stderr = os.Stderr
			runtime.command.Env = os.Environ()

			for _, env := range runtime.Envs {
				runtime.command.Env = append(runtime.command.Env, env)
			}

			chrysanthemum.Printf("项目已启动，请使用浏览器访问：http://localhost:%s\r\n", runtime.Port)
			err := runtime.command.Run()
			// TODO: this maybe not portable
			if err.Error() == "signal: killed" {
				continue
			} else {
				runtime.Errors <- err
				break
			}
		}
	}()
}

// Watch file changes
func (runtime *Runtime) Watch(interval time.Duration) error {

	// watch file changes
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	lastFiredTime := time.Now()

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				_ = event
				now := time.Now()
				if now.Sub(lastFiredTime) > interval {
					err = runtime.command.Process.Kill()
					if err != nil {
						runtime.Errors <- err
					}
					lastFiredTime = now
				}
			case err = <-watcher.Errors:
				runtime.Errors <- err
			}
		}
	}()
	for _, file := range runtime.WatchFiles {
		err = watcher.Add(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (runtime *Runtime) ArchiveUploadFiles(archiveFile string, ignoreFilePath string) error {
	return runtime.defaultArchive(archiveFile, ignoreFilePath)
}

func (runtime *Runtime) defaultArchive(archiveFile string, ignoreFilePath string) error {
	matcher, err := runtime.readIgnore(ignoreFilePath)
	if os.IsNotExist(err) {
		return fmt.Errorf("指定的 ignore 文件 '%s' 不存在", ignoreFilePath)
	} else if err != nil {
		return err
	}

	files := []struct{ Name, Path string }{}
	err = symwalk.Walk(".", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// convert DOS's '\' path seprater to UNIX style
		path = filepath.ToSlash(path)
		decision, err := matcher.Match(path, info)
		if err != nil {
			return err
		}

		if info.IsDir() {
			if decision == parseignore.Exclude {
				return filepath.SkipDir
			}
			return nil
		}

		if decision != parseignore.Exclude {
			files = append(files, struct{ Name, Path string }{
				Name: path,
				Path: path,
			})
		}
		return nil
	})

	if err != nil {
		return err
	}
	spinner := chrysanthemum.New("压缩项目文件").Start()

	err = utils.ArchiveFiles(archiveFile, files)
	if err != nil {
		spinner.Failed()
	}
	spinner.Successed()
	return nil
}

// DetectRuntime returns the project's runtime
func DetectRuntime(projectPath string) (*Runtime, error) {
	bar := chrysanthemum.New("正在检测运行时").Start()
	// order is important
	if utils.IsFileExists(filepath.Join(projectPath, "cloud", "main.js")) {
		chrysanthemum.Printf("检测到 cloudcode 运行时\r\n")
		bar.Successed()
		return &Runtime{
			Name: "cloudcode",
		}, nil
	}
	packageFilePath := filepath.Join(projectPath, "package.json")
	if utils.IsFileExists(filepath.Join(projectPath, "server.js")) && utils.IsFileExists(packageFilePath) {
		bar.Successed()
		chrysanthemum.Printf("检测到 node.js 运行时\r\n")
		return newNodeRuntime(projectPath)
	}
	if utils.IsFileExists(packageFilePath) {
		data, err := ioutil.ReadFile(packageFilePath)
		if err == nil {
			data = utils.StripUTF8BOM(data)
			var result struct {
				Scripts struct {
					Start string `json:"start"`
				} `json:"scripts"`
			}
			if err = json.Unmarshal(data, &result); err == nil {
				if result.Scripts.Start != "" {
					bar.Successed()
					chrysanthemum.Printf("检测到 node.js 运行时\r\n")
					return newNodeRuntime(projectPath)
				}
			}
		}
	}
	if utils.IsFileExists(filepath.Join(projectPath, "requirements.txt")) && utils.IsFileExists(filepath.Join(projectPath, "wsgi.py")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 Python 运行时\r\n")
		return newPythonRuntime(projectPath)
	}
	if utils.IsFileExists(filepath.Join(projectPath, "composer.json")) && utils.IsFileExists(filepath.Join("public", "index.php")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 PHP 运行时\r\n")
		return newPhpRuntime(projectPath)
	}
	if utils.IsFileExists(filepath.Join(projectPath, "pom.xml")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 Java 运行时\r\n")
		return newJavaRuntime(projectPath)
	}
	bar.Failed()
	return nil, ErrInvalidRuntime
}

func lookupBin(fallbacks []string) (string, error) {
	for i, bin := range fallbacks {
		binPath, err := exec.LookPath(bin)
		if err == nil { // found
			if i == 0 {
				chrysanthemum.Printf("找到运行文件 `%s`\r\n", binPath)
			} else {
				chrysanthemum.Printf("没有找到命令 `%s`，使用 `%s` 代替 \r\n", fallbacks[i-1], fallbacks[i])
			}
			return bin, nil
		}
	}

	return "", fmt.Errorf("`%s` not found", fallbacks[0])
}

func newPythonRuntime(projectPath string) (*Runtime, error) {
	execName := "python2.7"

	content, err := ioutil.ReadFile(filepath.Join(projectPath, "runtime.txt"))
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		// the default content
		content = []byte("python-2.7")
	}
	if strings.HasPrefix(string(content), "python-2.7") {
		execName, err = lookupBin([]string{"python2.7", "python2", "python"})
		if err != nil {
			return nil, err
		}
	} else if strings.HasPrefix(string(content), "python-3.5") {
		execName, err = lookupBin([]string{"python3.5", "python3", "python"})
		if err != nil {
			return nil, err
		}
	} else {
		return nil, errors.New("invalid python runtime.txt format, only `python-2.7` and `python-3.5` were allowed")
	}

	return &Runtime{
		ProjectPath: projectPath,
		Name:        "python",
		Exec:        execName,
		Args:        []string{"wsgi.py"},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newNodeRuntime(projectPath string) (*Runtime, error) {
	execName := "node"
	args := []string{"server.js"}
	pkgFile := filepath.Join(projectPath, "package.json")
	if content, err := ioutil.ReadFile(pkgFile); err == nil {
		content = utils.StripUTF8BOM(content)
		pkg := new(struct {
			Scripts struct {
				Start string `json:"start"`
				Dev   string `json:"dev"`
			} `json:"scripts"`
			Dependencies map[string]string `json:"dependencies"`
		})
		err = json.Unmarshal(content, pkg)
		if err != nil {
			return nil, err
		}

		if pkg.Scripts.Dev != "" {
			execName = "npm"
			args = []string{"run", "dev"}
		} else if pkg.Scripts.Start != "" {
			execName = "npm"
			args = []string{"start"}
		}

		if sdkVersion, ok := pkg.Dependencies["leanengine"]; ok {
			if strings.HasPrefix(sdkVersion, "0.") ||
				strings.HasPrefix(sdkVersion, "~0.") ||
				strings.HasPrefix(sdkVersion, "^0.") {
				s := "当前使用 leanengine SDK 版本过低，本地云函数调试功能将会不能正常启用。建议参考 http://url.leanapp.cn/Og1cVia 尽快升级。"
				fmt.Fprintf(os.Stderr, " %s [WARNING] %s\r\n", chrysanthemum.Fail, s)
			}
		}

	}

	return &Runtime{
		ProjectPath: projectPath,
		Name:        "node.js",
		Exec:        execName,
		Args:        args,
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newJavaRuntime(projectPath string) (*Runtime, error) {
	return &Runtime{
		ProjectPath: projectPath,
		Name:        "java",
		Exec:        "mvn",
		Args:        []string{"jetty:run"},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newPhpRuntime(projectPath string) (*Runtime, error) {
	entryScript, err := getPHPEntryScriptPath()
	if err != nil {
		return nil, err
	}
	return &Runtime{
		ProjectPath: projectPath,
		Name:        "php",
		Exec:        "php",
		Args:        []string{"-S", "127.0.0.1:3000", "-t", "public", entryScript},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}
<commit_msg>:sparkles: support pyenv<commit_after>package runtimes

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	"github.com/aisk/chrysanthemum"
	"github.com/facebookgo/parseignore"
	"github.com/facebookgo/symwalk"
	"github.com/fsnotify/fsnotify"
	"github.com/leancloud/lean-cli/utils"
)

// ErrInvalidRuntime means the project's structure is not a valid LeanEngine project
var ErrInvalidRuntime = errors.New("invalid runtime")

type filesPattern struct {
	Includes []string
	Excludes []string
}

// Runtime stands for a language runtime
type Runtime struct {
	command     *exec.Cmd
	ProjectPath string
	Name        string
	Exec        string
	Args        []string
	WatchFiles  []string
	Envs        []string
	Port        string
	// DeployFiles is the patterns for source code to deploy to the remote server
	DeployFiles filesPattern
	// Errors is the channel that receives the command's error result
	Errors chan error
}

// Run the project, and watch file changes
func (runtime *Runtime) Run() {
	go func() {
		for {
			runtime.command = exec.Command(runtime.Exec, runtime.Args...)
			runtime.command.Env = os.Environ()
			runtime.command.Stdout = os.Stdout
			runtime.command.Stderr = os.Stderr
			runtime.command.Env = os.Environ()

			for _, env := range runtime.Envs {
				runtime.command.Env = append(runtime.command.Env, env)
			}

			chrysanthemum.Printf("项目已启动，请使用浏览器访问：http://localhost:%s\r\n", runtime.Port)
			err := runtime.command.Run()
			// TODO: this maybe not portable
			if err.Error() == "signal: killed" {
				continue
			} else {
				runtime.Errors <- err
				break
			}
		}
	}()
}

// Watch file changes
func (runtime *Runtime) Watch(interval time.Duration) error {

	// watch file changes
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	lastFiredTime := time.Now()

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				_ = event
				now := time.Now()
				if now.Sub(lastFiredTime) > interval {
					err = runtime.command.Process.Kill()
					if err != nil {
						runtime.Errors <- err
					}
					lastFiredTime = now
				}
			case err = <-watcher.Errors:
				runtime.Errors <- err
			}
		}
	}()
	for _, file := range runtime.WatchFiles {
		err = watcher.Add(file)
		if err != nil {
			return err
		}
	}

	return nil
}

func (runtime *Runtime) ArchiveUploadFiles(archiveFile string, ignoreFilePath string) error {
	return runtime.defaultArchive(archiveFile, ignoreFilePath)
}

func (runtime *Runtime) defaultArchive(archiveFile string, ignoreFilePath string) error {
	matcher, err := runtime.readIgnore(ignoreFilePath)
	if os.IsNotExist(err) {
		return fmt.Errorf("指定的 ignore 文件 '%s' 不存在", ignoreFilePath)
	} else if err != nil {
		return err
	}

	files := []struct{ Name, Path string }{}
	err = symwalk.Walk(".", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// convert DOS's '\' path seprater to UNIX style
		path = filepath.ToSlash(path)
		decision, err := matcher.Match(path, info)
		if err != nil {
			return err
		}

		if info.IsDir() {
			if decision == parseignore.Exclude {
				return filepath.SkipDir
			}
			return nil
		}

		if decision != parseignore.Exclude {
			files = append(files, struct{ Name, Path string }{
				Name: path,
				Path: path,
			})
		}
		return nil
	})

	if err != nil {
		return err
	}
	spinner := chrysanthemum.New("压缩项目文件").Start()

	err = utils.ArchiveFiles(archiveFile, files)
	if err != nil {
		spinner.Failed()
	}
	spinner.Successed()
	return nil
}

// DetectRuntime returns the project's runtime
func DetectRuntime(projectPath string) (*Runtime, error) {
	bar := chrysanthemum.New("正在检测运行时").Start()
	// order is important
	if utils.IsFileExists(filepath.Join(projectPath, "cloud", "main.js")) {
		chrysanthemum.Printf("检测到 cloudcode 运行时\r\n")
		bar.Successed()
		return &Runtime{
			Name: "cloudcode",
		}, nil
	}
	packageFilePath := filepath.Join(projectPath, "package.json")
	if utils.IsFileExists(filepath.Join(projectPath, "server.js")) && utils.IsFileExists(packageFilePath) {
		bar.Successed()
		chrysanthemum.Printf("检测到 node.js 运行时\r\n")
		return newNodeRuntime(projectPath)
	}
	if utils.IsFileExists(packageFilePath) {
		data, err := ioutil.ReadFile(packageFilePath)
		if err == nil {
			data = utils.StripUTF8BOM(data)
			var result struct {
				Scripts struct {
					Start string `json:"start"`
				} `json:"scripts"`
			}
			if err = json.Unmarshal(data, &result); err == nil {
				if result.Scripts.Start != "" {
					bar.Successed()
					chrysanthemum.Printf("检测到 node.js 运行时\r\n")
					return newNodeRuntime(projectPath)
				}
			}
		}
	}
	if utils.IsFileExists(filepath.Join(projectPath, "requirements.txt")) && utils.IsFileExists(filepath.Join(projectPath, "wsgi.py")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 Python 运行时\r\n")
		return newPythonRuntime(projectPath)
	}
	if utils.IsFileExists(filepath.Join(projectPath, "composer.json")) && utils.IsFileExists(filepath.Join("public", "index.php")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 PHP 运行时\r\n")
		return newPhpRuntime(projectPath)
	}
	if utils.IsFileExists(filepath.Join(projectPath, "pom.xml")) {
		bar.Successed()
		chrysanthemum.Printf("检测到 Java 运行时\r\n")
		return newJavaRuntime(projectPath)
	}
	bar.Failed()
	return nil, ErrInvalidRuntime
}

func lookupBin(fallbacks []string) (string, error) {
	for i, bin := range fallbacks {
		binPath, err := exec.LookPath(bin)
		if err == nil { // found
			if i == 0 {
				chrysanthemum.Printf("找到运行文件 `%s`\r\n", binPath)
			} else {
				chrysanthemum.Printf("没有找到命令 `%s`，使用 `%s` 代替 \r\n", fallbacks[i-1], fallbacks[i])
			}
			return bin, nil
		}
	}

	return "", fmt.Errorf("`%s` not found", fallbacks[0])
}

func newPythonRuntime(projectPath string) (*Runtime, error) {

	content, err := ioutil.ReadFile(filepath.Join(projectPath, ".python-version"))
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}

		execName := "python2.7"
		content, err = ioutil.ReadFile(filepath.Join(projectPath, "runtime.txt"))
		if err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
			// the default content
			content = []byte("python-2.7")
		}
		if strings.HasPrefix(string(content), "python-2.7") {
			execName, err = lookupBin([]string{"python2.7", "python2", "python"})
			if err != nil {
				return nil, err
			}
		} else if strings.HasPrefix(string(content), "python-3.5") {
			execName, err = lookupBin([]string{"python3.5", "python3", "python"})
			if err != nil {
				return nil, err
			}
		} else {
			return nil, errors.New("invalid python runtime.txt format, only `python-2.7` and `python-3.5` were allowed")
		}

		return &Runtime{
			ProjectPath: projectPath,
			Name:        "python",
			Exec:        execName,
			Args:        []string{"wsgi.py"},
			WatchFiles:  []string{"."},
			Envs:        os.Environ(),
			Errors:      make(chan error),
		}, nil
	}
	pythonVersion := string(content)
	if !(strings.HasPrefix(pythonVersion, "2.") || strings.HasPrefix(pythonVersion, "3.")) {
		return nil, errors.New("错误的 pyenv 版本，目前云引擎只支持 CPython，请检查 .python-version 文件确认")
	}
	chrysanthemum.Println("检测到项目使用 pyenv，请确保当前环境 pyenv 已正确设置")

	return &Runtime{
		ProjectPath: projectPath,
		Name:        "python",
		Exec:        "python",
		Args:        []string{"wsgi.py"},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newNodeRuntime(projectPath string) (*Runtime, error) {
	execName := "node"
	args := []string{"server.js"}
	pkgFile := filepath.Join(projectPath, "package.json")
	if content, err := ioutil.ReadFile(pkgFile); err == nil {
		content = utils.StripUTF8BOM(content)
		pkg := new(struct {
			Scripts struct {
				Start string `json:"start"`
				Dev   string `json:"dev"`
			} `json:"scripts"`
			Dependencies map[string]string `json:"dependencies"`
		})
		err = json.Unmarshal(content, pkg)
		if err != nil {
			return nil, err
		}

		if pkg.Scripts.Dev != "" {
			execName = "npm"
			args = []string{"run", "dev"}
		} else if pkg.Scripts.Start != "" {
			execName = "npm"
			args = []string{"start"}
		}

		if sdkVersion, ok := pkg.Dependencies["leanengine"]; ok {
			if strings.HasPrefix(sdkVersion, "0.") ||
				strings.HasPrefix(sdkVersion, "~0.") ||
				strings.HasPrefix(sdkVersion, "^0.") {
				s := "当前使用 leanengine SDK 版本过低，本地云函数调试功能将会不能正常启用。建议参考 http://url.leanapp.cn/Og1cVia 尽快升级。"
				fmt.Fprintf(os.Stderr, " %s [WARNING] %s\r\n", chrysanthemum.Fail, s)
			}
		}

	}

	return &Runtime{
		ProjectPath: projectPath,
		Name:        "node.js",
		Exec:        execName,
		Args:        args,
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newJavaRuntime(projectPath string) (*Runtime, error) {
	return &Runtime{
		ProjectPath: projectPath,
		Name:        "java",
		Exec:        "mvn",
		Args:        []string{"jetty:run"},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}

func newPhpRuntime(projectPath string) (*Runtime, error) {
	entryScript, err := getPHPEntryScriptPath()
	if err != nil {
		return nil, err
	}
	return &Runtime{
		ProjectPath: projectPath,
		Name:        "php",
		Exec:        "php",
		Args:        []string{"-S", "127.0.0.1:3000", "-t", "public", entryScript},
		WatchFiles:  []string{"."},
		Envs:        os.Environ(),
		Errors:      make(chan error),
	}, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package collider

import (
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

var serverAddr string
var once sync.Once
var registerTimeout = time.Second
var cl *Collider

var port = flag.Int("port", 8089, "The port that Collider listens to")

func startCollider() {
	serverAddr = "localhost:" + strconv.Itoa(*port)

	cl = &Collider{
		roomTable: newRoomTable(registerTimeout, "http://"+serverAddr),
		dash:      newDashboard(),
	}

	go cl.Run(*port, false)
	fmt.Println("Test WebSocket server listening on ", serverAddr)
}

func newConfig(t *testing.T, path string) *websocket.Config {
	wsaddr := fmt.Sprintf("ws://%s%s", serverAddr, path)
	lh := "http://localhost"
	c, err := websocket.NewConfig(wsaddr, lh)
	if err != nil {
		t.Fatalf("NewConfig(%q, %q) got error: %s, want nil", wsaddr, lh, err.Error())
	}
	return c
}

func setup() {
	once.Do(startCollider)
	cl.roomTable = newRoomTable(registerTimeout, "http://"+serverAddr)
}

func addWsClient(t *testing.T, roomID string, clientID string) *websocket.Conn {
	c, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatalf("net.Dial(tcp, %q) got error: %s, want nil", serverAddr, err.Error())
	}
	config := newConfig(t, "/ws")
	conn, err := websocket.NewClient(config, c)
	if err != nil {
		t.Fatalf("websocket.NewClient(%v, %v) got error: %s, want nil", config, c, err.Error())
	}

	// Registers the client.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: clientID,
		RoomID:   roomID,
	}
	write(t, conn, m)

	return conn
}

func read(t *testing.T, conn *websocket.Conn) string {
	var data = make([]byte, 512)
	n, err := conn.Read(data)
	if err != nil {
		t.Errorf("conn.Read(%v) got error: %v, want nil", data, err)
	}
	return string(data[0:n])
}

func write(t *testing.T, conn *websocket.Conn, data interface{}) {
	enc := json.NewEncoder(conn)
	err := enc.Encode(data)
	if err != nil {
		t.Errorf("json.NewEncoder(%v).Encode(%v) got error: %v, want nil", conn, data, err)
	}
}

func postSend(t *testing.T, roomID string, clientID string, msg string) {
	urlstr := "http://" + serverAddr + "/" + roomID + "/" + clientID
	r := strings.NewReader(msg)
	_, err := http.Post(urlstr, "application/octet-stream", r)
	if err != nil {
		t.Errorf("http.Post(%q, %q) got error: %q, want nil", urlstr, msg, err)
	}
}

func postDel(t *testing.T, roomID string, clientID string) {
	var c http.Client
	urlstr := "http://" + serverAddr + "/" + roomID + "/" + clientID
	req, err := http.NewRequest("DELETE", urlstr, nil)
	if err != nil {
		t.Errorf("http.NewRequest(DELETE, %q, nil) got error: %v, want nil", urlstr, err)
	}
	_, err = c.Do(req)
	if err != nil {
		t.Errorf("http.Client.Do(%v) got error: %v", req, err)
	}
}

func expectConnectionClose(t *testing.T, conn *websocket.Conn) {
	var m string
	err := websocket.Message.Receive(conn, &m)
	if err == nil || err.Error() != "EOF" {
		t.Errorf("websocket.Message.Receive(%v) = %v, want EOF", conn, err)
	}
}

func expectReceiveMessage(t *testing.T, conn *websocket.Conn, msg string) {
	var m wsClientMsg
	err := json.Unmarshal([]byte(read(t, conn)), &m)

	if err != nil {
		t.Errorf("json.Unmarshal([]byte(read(t, conn))) got error: %v, want nil", err)
	}
	if m.Msg != msg {
		t.Errorf("After json.Unmarshal([]byte(read(t, conn)), &m), m.Msg = %s, want %s", m.Msg, msg)
	}
}

func expectReceiveError(t *testing.T, conn *websocket.Conn) {
	var m wsServerMsg
	if err := json.Unmarshal([]byte(read(t, conn)), &m); err != nil {
		t.Errorf("json.Unmarshal([]byte(read(t, conn)), &m) got error: %v, want nil", err)
	}
	if m.Error == "" {
		t.Errorf("After json.Unmarshal([]byte(read(t, conn)), &m), m.Error = %v, want non-empty", m.Error)
	}
}

func waitForCondition(f func() bool) bool {
	for i := 0; i < 10 && !f(); i++ {
		time.Sleep(1000)
	}
	return f()
}

func TestWsForwardServer(t *testing.T) {
	setup()
	c1 := addWsClient(t, "abc", "123")
	c2 := addWsClient(t, "abc", "456")

	// Sends a message from conn1 to conn2.
	m := wsClientMsg{
		Cmd: "send",
		Msg: "hello",
	}
	write(t, c1, m)
	expectReceiveMessage(t, c2, m.Msg)
	c1.Close()
	c2.Close()
}

// Tests that an error is returned if the same client id is registered twice.
func TestWsForwardServerDuplicatedID(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Registers the same client again.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: "123",
		RoomID:   "abc",
	}
	write(t, c, m)
	expectReceiveError(t, c)
	expectConnectionClose(t, c)
}

// Tests that an error is returned if the same client tries to register a second time.
func TestWsForwardServerConnectTwice(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Registers again.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: "123",
		RoomID:   "abc",
	}
	write(t, c, m)
	expectReceiveError(t, c)
	expectConnectionClose(t, c)
}

// Tests that message sent through POST is received.
func TestHttpHandlerSend(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Sends a POST request and expects to receive the message on the websocket connection.
	m := "hello!"
	postSend(t, "abc", "456", m)
	expectReceiveMessage(t, c, m)
	c.Close()
}

// Tests that message cached through POST is delivered.
func TestHttpHandlerSendCached(t *testing.T) {
	setup()

	// Sends a POST request and expects to receive the message on the websocket connection.
	m := "hello!"
	rid, src, dest := "abc", "456", "123"
	postSend(t, rid, src, m)
	if !waitForCondition(func() bool { return cl.roomTable.rooms[rid] != nil }) {
		t.Errorf("After a POST request to the room %q, cl.roomTable.rooms[%q] = nil, want non-nil", rid, rid)
	}

	c := addWsClient(t, rid, dest)
	expectReceiveMessage(t, c, m)
	if !waitForCondition(func() bool { return len(cl.roomTable.rooms[rid].clients[src].msgs) == 0 }) {
		t.Errorf("After a POST request from the room %q from client %q and registering client %q, cl.roomTable.rooms[%q].clients[%q].msgs = %v, want emtpy", rid, src, dest, rid, src, cl.roomTable.rooms[rid].clients[src].msgs)
	}

	c.Close()
}

// Tests that deleting the client through DELETE works.
func TestHttpHandlerDeleteConnection(t *testing.T) {
	setup()
	rid, cid := "abc", "1"
	c := addWsClient(t, rid, cid)

	// Waits until the server has registered the client.
	if !waitForCondition(func() bool { return cl.roomTable.rooms[rid] != nil }) {
		t.Errorf("After registering client %q in room %q, cl.roomTable.rooms[%q] = nil, want non-nil", cid, rid, rid)
	}

	// Deletes the client.
	postDel(t, rid, cid)
	expectConnectionClose(t, c)
	if !waitForCondition(func() bool { return len(cl.roomTable.rooms) == 0 }) {
		t.Errorf("After deleting client %q from room %q, cl.roomTable.rooms = %v, want empty", cid, rid, cl.roomTable.rooms)
	}
}

func TestRoomCleanedUpAfterTimeout(t *testing.T) {
	setup()

	// Sends a POST request to create a new and unregistered client.
	r, c := "abc", "1"
	postSend(t, r, c, "hi")
	if !waitForCondition(func() bool { return cl.roomTable.rooms[r] != nil }) {
		t.Errorf("After a POST request to the room %q, cl.roomTable.rooms[%q] = nil, want non-nil", r, r)
	}
	time.Sleep(registerTimeout + time.Second)

	if l := len(cl.roomTable.rooms); l != 0 {
		t.Errorf("After timeout without registering the new client, len(cl.roomTable.rooms) = %d, want 0", l)
	}
}

func TestDeregisteredClientNotRemovedUntilTimeout(t *testing.T) {
	setup()

	rid, cid := "abc", "1"
	conn := addWsClient(t, rid, cid)
	c, _ := cl.roomTable.room(rid).client(cid)

	conn.Close()

	// Waits for the client to deregister.
	if !waitForCondition(func() bool { return !c.registered() }) {
		t.Errorf("After websockt.Connection.Close(), client.registered() = true, want false")
	}

	// Checks that the client is still in the room.
	if actual, _ := cl.roomTable.room(rid).client(cid); actual != c {
		t.Errorf("After websockt.Connection.Close(), cl.roomTable.room[rid].client[cid] = %v, want %v", actual, c)
	}

	// Checks that the client and room are removed after the timeout.
	time.Sleep(registerTimeout + time.Second)
	if l := len(cl.roomTable.rooms); l != 0 {
		t.Errorf("After timeout without re-registering the new client, len(cl.roomTable.rooms) = %d, want 0", l)
	}
}

func TestReregisterClientBeforeTimeout(t *testing.T) {
	setup()

	rid, cid := "abc", "1"
	conn := addWsClient(t, rid, cid)
	c, _ := cl.roomTable.room(rid).client(cid)

	conn.Close()

	// Waits for the client to deregister.
	if !waitForCondition(func() bool { return !c.registered() }) {
		t.Errorf("After websockt.Connection.Close(), client.registered() = true, want false")
	}

	// Checks that the client is still in the room.
	if actual, _ := cl.roomTable.room(rid).client(cid); actual != c {
		t.Errorf("After websockt.Connection.Close(), cl.roomTable.room[rid].client[cid] = %v, want %v", actual, c)
	}

	// Reregister the client.
	conn = addWsClient(t, rid, cid)

	// Waits for the client to be registered.
	if !waitForCondition(func() bool { return c.registered() }) {
		t.Errorf("After addWsClient(...) again, client.registered() = false, want true")
	}

	// Checks that the timer has been stopped.
	if c.timer != nil {
		t.Errorf("After addWsClient() again, client.timer = %v, want nil", c.timer)
	}
}
<commit_msg>Update collider_test.go websocket dependency.<commit_after>// Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package collider

import (
	"golang.org/x/net/websocket"
	"encoding/json"
	"flag"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

var serverAddr string
var once sync.Once
var registerTimeout = time.Second
var cl *Collider

var port = flag.Int("port", 8089, "The port that Collider listens to")

func startCollider() {
	serverAddr = "localhost:" + strconv.Itoa(*port)

	cl = &Collider{
		roomTable: newRoomTable(registerTimeout, "http://"+serverAddr),
		dash:      newDashboard(),
	}

	go cl.Run(*port, false)
	fmt.Println("Test WebSocket server listening on ", serverAddr)
}

func newConfig(t *testing.T, path string) *websocket.Config {
	wsaddr := fmt.Sprintf("ws://%s%s", serverAddr, path)
	lh := "http://localhost"
	c, err := websocket.NewConfig(wsaddr, lh)
	if err != nil {
		t.Fatalf("NewConfig(%q, %q) got error: %s, want nil", wsaddr, lh, err.Error())
	}
	return c
}

func setup() {
	once.Do(startCollider)
	cl.roomTable = newRoomTable(registerTimeout, "http://"+serverAddr)
}

func addWsClient(t *testing.T, roomID string, clientID string) *websocket.Conn {
	c, err := net.Dial("tcp", serverAddr)
	if err != nil {
		t.Fatalf("net.Dial(tcp, %q) got error: %s, want nil", serverAddr, err.Error())
	}
	config := newConfig(t, "/ws")
	conn, err := websocket.NewClient(config, c)
	if err != nil {
		t.Fatalf("websocket.NewClient(%v, %v) got error: %s, want nil", config, c, err.Error())
	}

	// Registers the client.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: clientID,
		RoomID:   roomID,
	}
	write(t, conn, m)

	return conn
}

func read(t *testing.T, conn *websocket.Conn) string {
	var data = make([]byte, 512)
	n, err := conn.Read(data)
	if err != nil {
		t.Errorf("conn.Read(%v) got error: %v, want nil", data, err)
	}
	return string(data[0:n])
}

func write(t *testing.T, conn *websocket.Conn, data interface{}) {
	enc := json.NewEncoder(conn)
	err := enc.Encode(data)
	if err != nil {
		t.Errorf("json.NewEncoder(%v).Encode(%v) got error: %v, want nil", conn, data, err)
	}
}

func postSend(t *testing.T, roomID string, clientID string, msg string) {
	urlstr := "http://" + serverAddr + "/" + roomID + "/" + clientID
	r := strings.NewReader(msg)
	_, err := http.Post(urlstr, "application/octet-stream", r)
	if err != nil {
		t.Errorf("http.Post(%q, %q) got error: %q, want nil", urlstr, msg, err)
	}
}

func postDel(t *testing.T, roomID string, clientID string) {
	var c http.Client
	urlstr := "http://" + serverAddr + "/" + roomID + "/" + clientID
	req, err := http.NewRequest("DELETE", urlstr, nil)
	if err != nil {
		t.Errorf("http.NewRequest(DELETE, %q, nil) got error: %v, want nil", urlstr, err)
	}
	_, err = c.Do(req)
	if err != nil {
		t.Errorf("http.Client.Do(%v) got error: %v", req, err)
	}
}

func expectConnectionClose(t *testing.T, conn *websocket.Conn) {
	var m string
	err := websocket.Message.Receive(conn, &m)
	if err == nil || err.Error() != "EOF" {
		t.Errorf("websocket.Message.Receive(%v) = %v, want EOF", conn, err)
	}
}

func expectReceiveMessage(t *testing.T, conn *websocket.Conn, msg string) {
	var m wsClientMsg
	err := json.Unmarshal([]byte(read(t, conn)), &m)

	if err != nil {
		t.Errorf("json.Unmarshal([]byte(read(t, conn))) got error: %v, want nil", err)
	}
	if m.Msg != msg {
		t.Errorf("After json.Unmarshal([]byte(read(t, conn)), &m), m.Msg = %s, want %s", m.Msg, msg)
	}
}

func expectReceiveError(t *testing.T, conn *websocket.Conn) {
	var m wsServerMsg
	if err := json.Unmarshal([]byte(read(t, conn)), &m); err != nil {
		t.Errorf("json.Unmarshal([]byte(read(t, conn)), &m) got error: %v, want nil", err)
	}
	if m.Error == "" {
		t.Errorf("After json.Unmarshal([]byte(read(t, conn)), &m), m.Error = %v, want non-empty", m.Error)
	}
}

func waitForCondition(f func() bool) bool {
	for i := 0; i < 10 && !f(); i++ {
		time.Sleep(1000)
	}
	return f()
}

func TestWsForwardServer(t *testing.T) {
	setup()
	c1 := addWsClient(t, "abc", "123")
	c2 := addWsClient(t, "abc", "456")

	// Sends a message from conn1 to conn2.
	m := wsClientMsg{
		Cmd: "send",
		Msg: "hello",
	}
	write(t, c1, m)
	expectReceiveMessage(t, c2, m.Msg)
	c1.Close()
	c2.Close()
}

// Tests that an error is returned if the same client id is registered twice.
func TestWsForwardServerDuplicatedID(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Registers the same client again.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: "123",
		RoomID:   "abc",
	}
	write(t, c, m)
	expectReceiveError(t, c)
	expectConnectionClose(t, c)
}

// Tests that an error is returned if the same client tries to register a second time.
func TestWsForwardServerConnectTwice(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Registers again.
	m := wsClientMsg{
		Cmd:      "register",
		ClientID: "123",
		RoomID:   "abc",
	}
	write(t, c, m)
	expectReceiveError(t, c)
	expectConnectionClose(t, c)
}

// Tests that message sent through POST is received.
func TestHttpHandlerSend(t *testing.T) {
	setup()
	c := addWsClient(t, "abc", "123")

	// Sends a POST request and expects to receive the message on the websocket connection.
	m := "hello!"
	postSend(t, "abc", "456", m)
	expectReceiveMessage(t, c, m)
	c.Close()
}

// Tests that message cached through POST is delivered.
func TestHttpHandlerSendCached(t *testing.T) {
	setup()

	// Sends a POST request and expects to receive the message on the websocket connection.
	m := "hello!"
	rid, src, dest := "abc", "456", "123"
	postSend(t, rid, src, m)
	if !waitForCondition(func() bool { return cl.roomTable.rooms[rid] != nil }) {
		t.Errorf("After a POST request to the room %q, cl.roomTable.rooms[%q] = nil, want non-nil", rid, rid)
	}

	c := addWsClient(t, rid, dest)
	expectReceiveMessage(t, c, m)
	if !waitForCondition(func() bool { return len(cl.roomTable.rooms[rid].clients[src].msgs) == 0 }) {
		t.Errorf("After a POST request from the room %q from client %q and registering client %q, cl.roomTable.rooms[%q].clients[%q].msgs = %v, want emtpy", rid, src, dest, rid, src, cl.roomTable.rooms[rid].clients[src].msgs)
	}

	c.Close()
}

// Tests that deleting the client through DELETE works.
func TestHttpHandlerDeleteConnection(t *testing.T) {
	setup()
	rid, cid := "abc", "1"
	c := addWsClient(t, rid, cid)

	// Waits until the server has registered the client.
	if !waitForCondition(func() bool { return cl.roomTable.rooms[rid] != nil }) {
		t.Errorf("After registering client %q in room %q, cl.roomTable.rooms[%q] = nil, want non-nil", cid, rid, rid)
	}

	// Deletes the client.
	postDel(t, rid, cid)
	expectConnectionClose(t, c)
	if !waitForCondition(func() bool { return len(cl.roomTable.rooms) == 0 }) {
		t.Errorf("After deleting client %q from room %q, cl.roomTable.rooms = %v, want empty", cid, rid, cl.roomTable.rooms)
	}
}

func TestRoomCleanedUpAfterTimeout(t *testing.T) {
	setup()

	// Sends a POST request to create a new and unregistered client.
	r, c := "abc", "1"
	postSend(t, r, c, "hi")
	if !waitForCondition(func() bool { return cl.roomTable.rooms[r] != nil }) {
		t.Errorf("After a POST request to the room %q, cl.roomTable.rooms[%q] = nil, want non-nil", r, r)
	}
	time.Sleep(registerTimeout + time.Second)

	if l := len(cl.roomTable.rooms); l != 0 {
		t.Errorf("After timeout without registering the new client, len(cl.roomTable.rooms) = %d, want 0", l)
	}
}

func TestDeregisteredClientNotRemovedUntilTimeout(t *testing.T) {
	setup()

	rid, cid := "abc", "1"
	conn := addWsClient(t, rid, cid)
	c, _ := cl.roomTable.room(rid).client(cid)

	conn.Close()

	// Waits for the client to deregister.
	if !waitForCondition(func() bool { return !c.registered() }) {
		t.Errorf("After websockt.Connection.Close(), client.registered() = true, want false")
	}

	// Checks that the client is still in the room.
	if actual, _ := cl.roomTable.room(rid).client(cid); actual != c {
		t.Errorf("After websockt.Connection.Close(), cl.roomTable.room[rid].client[cid] = %v, want %v", actual, c)
	}

	// Checks that the client and room are removed after the timeout.
	time.Sleep(registerTimeout + time.Second)
	if l := len(cl.roomTable.rooms); l != 0 {
		t.Errorf("After timeout without re-registering the new client, len(cl.roomTable.rooms) = %d, want 0", l)
	}
}

func TestReregisterClientBeforeTimeout(t *testing.T) {
	setup()

	rid, cid := "abc", "1"
	conn := addWsClient(t, rid, cid)
	c, _ := cl.roomTable.room(rid).client(cid)

	conn.Close()

	// Waits for the client to deregister.
	if !waitForCondition(func() bool { return !c.registered() }) {
		t.Errorf("After websockt.Connection.Close(), client.registered() = true, want false")
	}

	// Checks that the client is still in the room.
	if actual, _ := cl.roomTable.room(rid).client(cid); actual != c {
		t.Errorf("After websockt.Connection.Close(), cl.roomTable.room[rid].client[cid] = %v, want %v", actual, c)
	}

	// Reregister the client.
	conn = addWsClient(t, rid, cid)

	// Waits for the client to be registered.
	if !waitForCondition(func() bool { return c.registered() }) {
		t.Errorf("After addWsClient(...) again, client.registered() = false, want true")
	}

	// Checks that the timer has been stopped.
	if c.timer != nil {
		t.Errorf("After addWsClient() again, client.timer = %v, want nil", c.timer)
	}
}
<|endoftext|>


<commit_before>package gol

import (
	"github.com/ianremmler/chipmunk"
	"github.com/ianremmler/gordian"

	"fmt"
	"math"
	"math/rand"
	"sync"
	"time"
)

const (
	simTime       = time.Second / 60
	updateTime    = time.Second / 24
	pauseTime     = time.Second
	headStartTime = time.Second
	maxScore      = 10
	fieldWidth    = 1000
	fieldHeight   = 500
	edgeRadius    = 8
	goalSize      = 200
	playerRadius  = 10
	playerMass    = 1
	ballRadius    = 10
	ballMass      = 0.1
)

const (
	normLayer = 1 << iota
	goalLayer
)

type player struct {
	id          gordian.ClientId
	team        int
	body        chipmunk.Body
	shape       chipmunk.Shape
	cursorBody  chipmunk.Body
	cursorJoint chipmunk.Constraint
}

func (p *player) place() {
	hfw, hfh := 0.5*fieldWidth-playerRadius, 0.5*fieldHeight-playerRadius
	pos := chipmunk.Vect{rand.Float64() * hfw, rand.Float64()*(2*hfh) - hfh}
	if p.team == 0 {
		pos.X = -pos.X
	}
	minDist := 0.25*fieldHeight + playerRadius
	len := pos.Length()
	if len < minDist {
		pos = pos.Div(len).Mul(minDist)
	}
	p.body.SetPosition(pos)
}

type ball struct {
	body  chipmunk.Body
	shape chipmunk.Shape
}

type Player struct {
	Pos  chipmunk.Vect
	Team int
}

type Ball struct {
	Pos chipmunk.Vect
}

type configMsg struct {
	Id           string
	FieldWidth   float64
	FieldHeight  float64
	GoalSize     float64
	PlayerRadius float64
	BallRadius   float64
}

type stateMsg struct {
	Players map[string]Player
	Ball    Ball
	Score   []int
}

type Gol struct {
	players     map[gordian.ClientId]*player
	ball        ball
	score       []int
	pauseTicks  []int
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	mu          sync.Mutex
	*gordian.Gordian
}

func New() *Gol {
	g := &Gol{
		players:     map[gordian.ClientId]*player{},
		score:       []int{0, 0},
		pauseTicks:  []int{0, 0},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(24),
	}
	g.setup()
	return g
}

func (g *Gol) setup() {
	g.space = chipmunk.SpaceNew()
	g.space.SetDamping(0.1)
	hfw, hfh, hgs := 0.5*fieldWidth, 0.5*fieldHeight, 0.5*goalSize
	sidePts := []chipmunk.Vect{{-hfw, hgs}, {-hfw, hfh}, {hfw, hfh}, {hfw, hgs}}
	numSideSegs := len(sidePts) - 1
	for i := 0; i < 2; i++ {
		sign := 2*float64(i) - 1
		for j := 0; j < numSideSegs; j++ {
			p0, p1 := sidePts[j], sidePts[j+1]
			p0.Y *= sign
			p1.Y *= sign
			fieldSeg := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, edgeRadius)
			fieldSeg.SetLayers(normLayer)
			fieldSeg.SetElasticity(1.0)
			fieldSeg.SetFriction(1.0)
			g.space.AddShape(fieldSeg)
		}
		p0, p1 := chipmunk.Vect{sign * hfw, -hgs}, chipmunk.Vect{sign * hfw, hgs}
		goal := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, edgeRadius)
		goal.SetLayers(goalLayer)
		goal.SetElasticity(1.0)
		goal.SetFriction(1.0)
		g.space.AddShape(goal)
	}
	moment := chipmunk.MomentForCircle(ballMass, 0, ballRadius, chipmunk.Origin())
	g.ball.body = chipmunk.BodyNew(ballMass, moment)
	g.space.AddBody(g.ball.body)
	g.ball.shape = chipmunk.CircleShapeNew(g.ball.body, ballRadius, chipmunk.Origin())
	g.ball.shape.SetLayers(normLayer)
	g.ball.shape.SetElasticity(0.9)
	g.ball.shape.SetFriction(0.1)
	g.space.AddShape(g.ball.shape)
}

func (g *Gol) Run() {
	go g.run()
	go g.sim()
	g.Gordian.Run()
}

func (g *Gol) run() {
	for {
		select {
		case client := <-g.Control:
			g.clientCtrl(client)
		case msg := <-g.InBox:
			g.handleMessage(&msg)
		case <-g.updateTimer:
			g.update()
		}
	}
}

func (g *Gol) sim() {
	for {
		<-g.simTimer

		g.mu.Lock()

		g.space.Step(float64(simTime) / float64(time.Second))
		g.handlePauses()
		g.handleGoals()

		g.mu.Unlock()
	}
}

func (g *Gol) handlePauses() {
	// enable control if pause is ending
	for _, player := range g.players {
		if g.pauseTicks[player.team] == 1 && player.cursorJoint.Space() != g.space {
			g.space.AddConstraint(player.cursorJoint)
		}
	}
	// update pause countdown
	for i := range g.pauseTicks {
		if g.pauseTicks[i] > 0 {
			g.pauseTicks[i]--
		}
	}
}

func (g *Gol) handleGoals() {
	ballX := g.ball.body.Position().X
	if math.Abs(ballX) > fieldWidth/2 { // GOL!
		team := 0
		if ballX < 0 {
			team = 1
		}
		g.score[team]++
		if g.score[0] >= maxScore || g.score[1] >= maxScore {
			g.score[0], g.score[1] = 0, 0
		}
		g.kickoff(team)
	}
}

func (g *Gol) kickoff(team int) {
	otherTeam := 1 - team

	g.ball.body.SetPosition(chipmunk.Vect{})
	g.ball.body.SetVelocity(chipmunk.Vect{})
	for _, player := range g.players {
		player.place()
		player.body.SetVelocity(chipmunk.Vect{})
		if g.pauseTicks[player.team] == 0 && player.cursorJoint.Space() == g.space {
			// disable control for a bit
			g.space.RemoveConstraint(player.cursorJoint)
		}
	}
	// give the team that was scored on a little head start for "kickoff"
	g.pauseTicks[team] = int((pauseTime + headStartTime) / simTime)
	g.pauseTicks[otherTeam] = int(pauseTime / simTime)
}

func (g *Gol) clientCtrl(client *gordian.Client) {
	switch client.Ctrl {
	case gordian.Connect:
		g.connect(client)
	case gordian.Close:
		g.close(client)
	}
}

func (g *Gol) nextTeam() int {
	teamSize := []int{0, 0}
	for _, player := range g.players {
		teamSize[player.team]++
	}
	switch {
	case teamSize[0] < teamSize[1]:
		return 0
	case teamSize[0] > teamSize[1]:
		return 1
	default:
		return rand.Intn(2)
	}
}

func (g *Gol) addPlayer(id gordian.ClientId) *player {
	player := &player{id: id, team: g.nextTeam()}

	moment := chipmunk.MomentForCircle(playerMass, 0, playerRadius, chipmunk.Origin())
	player.body = chipmunk.BodyNew(playerMass, moment)
	g.space.AddBody(player.body)

	player.shape = chipmunk.CircleShapeNew(player.body, playerRadius, chipmunk.Origin())
	player.shape.SetLayers(normLayer | goalLayer)
	player.shape.SetElasticity(0.9)
	player.shape.SetFriction(0.1)
	g.space.AddShape(player.shape)

	player.cursorBody = chipmunk.BodyNew(math.Inf(0), math.Inf(0))
	player.cursorJoint = chipmunk.PivotJointNew2(player.cursorBody, player.body,
		chipmunk.Vect{}, chipmunk.Vect{})
	player.cursorJoint.SetMaxForce(1000.0)
	g.space.AddConstraint(player.cursorJoint)

	g.players[player.id] = player

	return player
}

func (g *Gol) removePlayer(id gordian.ClientId) {
	player, ok := g.players[id]
	if !ok {
		return
	}
	if player.cursorJoint.Space() == g.space {
		g.space.RemoveConstraint(player.cursorJoint)
	}
	player.cursorJoint.Free()
	g.space.RemoveBody(player.body)
	g.space.RemoveShape(player.shape)
	player.body.Free()
	player.shape.Free()
	player.cursorBody.Free()

	delete(g.players, id)
}

func (g *Gol) connect(client *gordian.Client) {
	g.curId++

	client.Id = g.curId
	client.Ctrl = gordian.Register
	g.Control <- client
	client = <-g.Control
	if client.Ctrl != gordian.Establish {
		return
	}

	g.mu.Lock()

	player := g.addPlayer(client.Id)
	player.place()

	g.mu.Unlock()

	data := configMsg{
		FieldWidth:   fieldWidth,
		FieldHeight:  fieldHeight,
		GoalSize:     goalSize,
		PlayerRadius: playerRadius,
		BallRadius:   ballRadius,
		Id:           fmt.Sprintf("%d", client.Id),
	}
	msg := gordian.Message{
		To:   client.Id,
		Type: "config",
		Data: data,
	}
	g.OutBox <- msg
}

func (g *Gol) close(client *gordian.Client) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.removePlayer(client.Id)
}

func (g *Gol) handleMessage(msg *gordian.Message) {
	g.mu.Lock()
	defer g.mu.Unlock()

	id := msg.From
	player, ok := g.players[id]
	if !ok {
		return
	}
	switch msg.Type {
	case "player":
		state := &Player{}
		err := msg.Unmarshal(state)
		if err != nil {
			return
		}
		player.cursorBody.SetPosition(state.Pos)
	}
}

func (g *Gol) update() {
	g.mu.Lock()

	state := stateMsg{
		Players: map[string]Player{},
		Ball:    Ball{g.ball.body.Position()},
		Score:   g.score,
	}
	for i, player := range g.players {
		state.Players[fmt.Sprintf("%d", i)] = Player{
			Pos:  player.body.Position(),
			Team: player.team,
		}
	}

	g.mu.Unlock()

	msg := gordian.Message{
		Type: "state",
		Data: state,
	}
	for id := range g.players {
		msg.To = id
		g.OutBox <- msg
	}
}
<commit_msg>Use built-in method to convert duration to float.<commit_after>package gol

import (
	"github.com/ianremmler/chipmunk"
	"github.com/ianremmler/gordian"

	"fmt"
	"math"
	"math/rand"
	"sync"
	"time"
)

const (
	simTime       = time.Second / 60
	updateTime    = time.Second / 24
	pauseTime     = time.Second
	headStartTime = time.Second
	maxScore      = 10
	fieldWidth    = 1000
	fieldHeight   = 500
	edgeRadius    = 8
	goalSize      = 200
	playerRadius  = 10
	playerMass    = 1
	ballRadius    = 10
	ballMass      = 0.1
)

const (
	normLayer = 1 << iota
	goalLayer
)

type player struct {
	id          gordian.ClientId
	team        int
	body        chipmunk.Body
	shape       chipmunk.Shape
	cursorBody  chipmunk.Body
	cursorJoint chipmunk.Constraint
}

func (p *player) place() {
	hfw, hfh := 0.5*fieldWidth-playerRadius, 0.5*fieldHeight-playerRadius
	pos := chipmunk.Vect{rand.Float64() * hfw, rand.Float64()*(2*hfh) - hfh}
	if p.team == 0 {
		pos.X = -pos.X
	}
	minDist := 0.25*fieldHeight + playerRadius
	len := pos.Length()
	if len < minDist {
		pos = pos.Div(len).Mul(minDist)
	}
	p.body.SetPosition(pos)
}

type ball struct {
	body  chipmunk.Body
	shape chipmunk.Shape
}

type Player struct {
	Pos  chipmunk.Vect
	Team int
}

type Ball struct {
	Pos chipmunk.Vect
}

type configMsg struct {
	Id           string
	FieldWidth   float64
	FieldHeight  float64
	GoalSize     float64
	PlayerRadius float64
	BallRadius   float64
}

type stateMsg struct {
	Players map[string]Player
	Ball    Ball
	Score   []int
}

type Gol struct {
	players     map[gordian.ClientId]*player
	ball        ball
	score       []int
	pauseTicks  []int
	simTimer    <-chan time.Time
	updateTimer <-chan time.Time
	curId       int
	space       *chipmunk.Space
	mu          sync.Mutex
	*gordian.Gordian
}

func New() *Gol {
	g := &Gol{
		players:     map[gordian.ClientId]*player{},
		score:       []int{0, 0},
		pauseTicks:  []int{0, 0},
		simTimer:    time.Tick(simTime),
		updateTimer: time.Tick(updateTime),
		Gordian:     gordian.New(24),
	}
	g.setup()
	return g
}

func (g *Gol) setup() {
	g.space = chipmunk.SpaceNew()
	g.space.SetDamping(0.1)
	hfw, hfh, hgs := 0.5*fieldWidth, 0.5*fieldHeight, 0.5*goalSize
	sidePts := []chipmunk.Vect{{-hfw, hgs}, {-hfw, hfh}, {hfw, hfh}, {hfw, hgs}}
	numSideSegs := len(sidePts) - 1
	for i := 0; i < 2; i++ {
		sign := 2*float64(i) - 1
		for j := 0; j < numSideSegs; j++ {
			p0, p1 := sidePts[j], sidePts[j+1]
			p0.Y *= sign
			p1.Y *= sign
			fieldSeg := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, edgeRadius)
			fieldSeg.SetLayers(normLayer)
			fieldSeg.SetElasticity(1.0)
			fieldSeg.SetFriction(1.0)
			g.space.AddShape(fieldSeg)
		}
		p0, p1 := chipmunk.Vect{sign * hfw, -hgs}, chipmunk.Vect{sign * hfw, hgs}
		goal := chipmunk.SegmentShapeNew(g.space.StaticBody(), p0, p1, edgeRadius)
		goal.SetLayers(goalLayer)
		goal.SetElasticity(1.0)
		goal.SetFriction(1.0)
		g.space.AddShape(goal)
	}
	moment := chipmunk.MomentForCircle(ballMass, 0, ballRadius, chipmunk.Origin())
	g.ball.body = chipmunk.BodyNew(ballMass, moment)
	g.space.AddBody(g.ball.body)
	g.ball.shape = chipmunk.CircleShapeNew(g.ball.body, ballRadius, chipmunk.Origin())
	g.ball.shape.SetLayers(normLayer)
	g.ball.shape.SetElasticity(0.9)
	g.ball.shape.SetFriction(0.1)
	g.space.AddShape(g.ball.shape)
}

func (g *Gol) Run() {
	go g.run()
	go g.sim()
	g.Gordian.Run()
}

func (g *Gol) run() {
	for {
		select {
		case client := <-g.Control:
			g.clientCtrl(client)
		case msg := <-g.InBox:
			g.handleMessage(&msg)
		case <-g.updateTimer:
			g.update()
		}
	}
}

func (g *Gol) sim() {
	for {
		<-g.simTimer

		g.mu.Lock()

		g.space.Step(simTime.Seconds())
		g.handlePauses()
		g.handleGoals()

		g.mu.Unlock()
	}
}

func (g *Gol) handlePauses() {
	// enable control if pause is ending
	for _, player := range g.players {
		if g.pauseTicks[player.team] == 1 && player.cursorJoint.Space() != g.space {
			g.space.AddConstraint(player.cursorJoint)
		}
	}
	// update pause countdown
	for i := range g.pauseTicks {
		if g.pauseTicks[i] > 0 {
			g.pauseTicks[i]--
		}
	}
}

func (g *Gol) handleGoals() {
	ballX := g.ball.body.Position().X
	if math.Abs(ballX) > fieldWidth/2 { // GOL!
		team := 0
		if ballX < 0 {
			team = 1
		}
		g.score[team]++
		if g.score[0] >= maxScore || g.score[1] >= maxScore {
			g.score[0], g.score[1] = 0, 0
		}
		g.kickoff(team)
	}
}

func (g *Gol) kickoff(team int) {
	otherTeam := 1 - team

	g.ball.body.SetPosition(chipmunk.Vect{})
	g.ball.body.SetVelocity(chipmunk.Vect{})
	for _, player := range g.players {
		player.place()
		player.body.SetVelocity(chipmunk.Vect{})
		if g.pauseTicks[player.team] == 0 && player.cursorJoint.Space() == g.space {
			// disable control for a bit
			g.space.RemoveConstraint(player.cursorJoint)
		}
	}
	// give the team that was scored on a little head start for "kickoff"
	g.pauseTicks[team] = int((pauseTime + headStartTime) / simTime)
	g.pauseTicks[otherTeam] = int(pauseTime / simTime)
}

func (g *Gol) clientCtrl(client *gordian.Client) {
	switch client.Ctrl {
	case gordian.Connect:
		g.connect(client)
	case gordian.Close:
		g.close(client)
	}
}

func (g *Gol) nextTeam() int {
	teamSize := []int{0, 0}
	for _, player := range g.players {
		teamSize[player.team]++
	}
	switch {
	case teamSize[0] < teamSize[1]:
		return 0
	case teamSize[0] > teamSize[1]:
		return 1
	default:
		return rand.Intn(2)
	}
}

func (g *Gol) addPlayer(id gordian.ClientId) *player {
	player := &player{id: id, team: g.nextTeam()}

	moment := chipmunk.MomentForCircle(playerMass, 0, playerRadius, chipmunk.Origin())
	player.body = chipmunk.BodyNew(playerMass, moment)
	g.space.AddBody(player.body)

	player.shape = chipmunk.CircleShapeNew(player.body, playerRadius, chipmunk.Origin())
	player.shape.SetLayers(normLayer | goalLayer)
	player.shape.SetElasticity(0.9)
	player.shape.SetFriction(0.1)
	g.space.AddShape(player.shape)

	player.cursorBody = chipmunk.BodyNew(math.Inf(0), math.Inf(0))
	player.cursorJoint = chipmunk.PivotJointNew2(player.cursorBody, player.body,
		chipmunk.Vect{}, chipmunk.Vect{})
	player.cursorJoint.SetMaxForce(1000.0)
	g.space.AddConstraint(player.cursorJoint)

	g.players[player.id] = player

	return player
}

func (g *Gol) removePlayer(id gordian.ClientId) {
	player, ok := g.players[id]
	if !ok {
		return
	}
	if player.cursorJoint.Space() == g.space {
		g.space.RemoveConstraint(player.cursorJoint)
	}
	player.cursorJoint.Free()
	g.space.RemoveBody(player.body)
	g.space.RemoveShape(player.shape)
	player.body.Free()
	player.shape.Free()
	player.cursorBody.Free()

	delete(g.players, id)
}

func (g *Gol) connect(client *gordian.Client) {
	g.curId++

	client.Id = g.curId
	client.Ctrl = gordian.Register
	g.Control <- client
	client = <-g.Control
	if client.Ctrl != gordian.Establish {
		return
	}

	g.mu.Lock()

	player := g.addPlayer(client.Id)
	player.place()

	g.mu.Unlock()

	data := configMsg{
		FieldWidth:   fieldWidth,
		FieldHeight:  fieldHeight,
		GoalSize:     goalSize,
		PlayerRadius: playerRadius,
		BallRadius:   ballRadius,
		Id:           fmt.Sprintf("%d", client.Id),
	}
	msg := gordian.Message{
		To:   client.Id,
		Type: "config",
		Data: data,
	}
	g.OutBox <- msg
}

func (g *Gol) close(client *gordian.Client) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.removePlayer(client.Id)
}

func (g *Gol) handleMessage(msg *gordian.Message) {
	g.mu.Lock()
	defer g.mu.Unlock()

	id := msg.From
	player, ok := g.players[id]
	if !ok {
		return
	}
	switch msg.Type {
	case "player":
		state := &Player{}
		err := msg.Unmarshal(state)
		if err != nil {
			return
		}
		player.cursorBody.SetPosition(state.Pos)
	}
}

func (g *Gol) update() {
	g.mu.Lock()

	state := stateMsg{
		Players: map[string]Player{},
		Ball:    Ball{g.ball.body.Position()},
		Score:   g.score,
	}
	for i, player := range g.players {
		state.Players[fmt.Sprintf("%d", i)] = Player{
			Pos:  player.body.Position(),
			Team: player.team,
		}
	}

	g.mu.Unlock()

	msg := gordian.Message{
		Type: "state",
		Data: state,
	}
	for id := range g.players {
		msg.To = id
		g.OutBox <- msg
	}
}
<|endoftext|>


<commit_before>package gitcli

import (
	"regexp"
	"strings"
	"fmt"
)

var gitGpgRegex = regexp.MustCompile(`^(.+)\|(.+)$`)
// verify a given commit's GPG sig
// anything that refers to commit will work (like HEAD~3)
func (r *Repo) GetCommitSignature(commit string) (key  string, signed bool, err error) {
	stdout, stderr, err := r.cmd(`log`, `--format=%G?|%GK`,`-1`, commit, `--`)
	if (err != nil )  { return "", false, fmt.Errorf("Error while running git log: %s|%s|%s", stdout, stderr, err) }
	matches := gitGpgRegex.FindStringSubmatch(stdout)
	if (len(matches) < 3) {
		return "", false, err
	} else {
		// G - good, U - good, untrusted
		if (matches[1] == "G" || matches[1] == "U") {
			return matches[2], true, err
		} else {
			return matches[2], false, err
		}
	}
}

// SetTrustedSignatures sets a list of signatures considered valid for VerifyCommit()
// USE LONG FORMAT (full fingerprint), for now git log only passes 16 characters of fingerprint
// but if that ever changes any sig that will be shorter than that won't be matched
func (r *Repo) SetTrustedSignatures(sigs []string) {
	r.trustedSigs = make(map[string]bool)
	for _, sig := range sigs {
		cleanedSig := strings.ToUpper(strings.Replace(sig, " ", "", -1))
		r.trustedSigs[cleanedSig] = true
	}
}

// VerifyCommit checks if given commit is signed by one of sigs set in SetTrustedSignatures
// git log only passes 16 characters of fingerprint so it only checks for substring of that signature
func (r *Repo) VerifyCommit (commit string) (bool, error){
	sigID, correct, err := r.GetCommitSignature(commit)
	if err != nil {return false, err}
	if !correct {return false, err}
	// hopefully when git changes it to provide full fingerprint it can be changed to just hash lookup
	for testedSig := range r.trustedSigs {
		if strings.Contains(testedSig, sigID) {
			return true, err
		}
	}
	return false, err
}
<commit_msg>set error on false<commit_after>package gitcli

import (
	"regexp"
	"strings"
	"fmt"
)

var gitGpgRegex = regexp.MustCompile(`^(.+)\|(.+)$`)
// verify a given commit's GPG sig
// anything that refers to commit will work (like HEAD~3)
func (r *Repo) GetCommitSignature(commit string) (key  string, signed bool, err error) {
	stdout, stderr, err := r.cmd(`log`, `--format=%G?|%GK`,`-1`, commit, `--`)
	if (err != nil )  { return "", false, fmt.Errorf("Error while running git log: %s|%s|%s", stdout, stderr, err) }
	matches := gitGpgRegex.FindStringSubmatch(stdout)
	if (len(matches) < 3) {
		return "", false, err
	} else {
		// G - good, U - good, untrusted
		if (matches[1] == "G" || matches[1] == "U") {
			return matches[2], true, err
		} else {
			return matches[2], false, fmt.Errorf("git returned bad commit state: %s", matches[1])
		}
	}
}

// SetTrustedSignatures sets a list of signatures considered valid for VerifyCommit()
// USE LONG FORMAT (full fingerprint), for now git log only passes 16 characters of fingerprint
// but if that ever changes any sig that will be shorter than that won't be matched
func (r *Repo) SetTrustedSignatures(sigs []string) {
	r.trustedSigs = make(map[string]bool)
	for _, sig := range sigs {
		cleanedSig := strings.ToUpper(strings.Replace(sig, " ", "", -1))
		r.trustedSigs[cleanedSig] = true
	}
}

// VerifyCommit checks if given commit is signed by one of sigs set in SetTrustedSignatures
// git log only passes 16 characters of fingerprint so it only checks for substring of that signature
func (r *Repo) VerifyCommit (commit string) (bool, error){
	sigID, correct, err := r.GetCommitSignature(commit)
	if err != nil {return false, err}
	if !correct {return false, err}
	// hopefully when git changes it to provide full fingerprint it can be changed to just hash lookup
	for testedSig := range r.trustedSigs {
		if strings.Contains(testedSig, sigID) {
			return true, err
		}
	}
	return false, err
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "os"
  "github.com/codegangsta/cli"
  "github.com/MongoHQ/mongohq-cli"
  "github.com/MongoHQ/controllers"  // MongoHQ CLI functions
)

func requireArguments(command string, c *cli.Context, argumentsSlice []string, errorMessages []string) {
  err := false

  for _, argument := range argumentsSlice {
    if !c.IsSet(argument) {
      err = true
      fmt.Println("--" + argument + " is required")
    }
  }

  if err {
    fmt.Println("\nMissing arguments, for more information, run: mongohq " + command + " --help\n")
    for _, errorMessage := range errorMessages {
      fmt.Println(errorMessage)
    }
    os.Exit(1)
  }
}

func main() {
  app := cli.NewApp()
  app.Name = "mongohq"
  app.Usage = "Allow MongoHQ interaction from the commandline (enables awesomeness)"
  app.Before = controllers.RequireAuth
  app.Version = mongohq_cli.Version()
  app.Commands = []cli.Command{
    {
      Name:      "backups",
      Usage:     "List backups on a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "(optional) Name of database to list backups for"},
        cli.StringFlag { "deployment,dep", "<string>", "(optional) Id of deployment to list backups for"},
      },
      Action: func(c *cli.Context) {
        filter := map[string]string{}
        if c.IsSet("database") { filter["database"] = c.String("database") }
        if c.IsSet("deployment") { filter["deployment"] = c.String("deployment") }
        controllers.Backups(filter)
      },
    },
    {
      Name:      "backups:info",
      Usage:     "List backups on a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "backup,b", "<string>", "File name of backup"},
      },
      Action: func(c *cli.Context) {
        requireArguments("backups:info", c, []string{"backup"}, []string{})
        controllers.Backup(c.String("backup"))
      },
    },
    {
      Name:      "backups:restore",
      Usage:     "Restore backup to a database to a new deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "backup,b", "<string>", "File name of backup"},
        cli.StringFlag { "source-database,source", "<string>", "Original database name"},
        cli.StringFlag { "destination-database,destination", "<string>", "New database name"},
      },
      Action: func(c *cli.Context) {
        requireArguments("backups:restore", c, []string{"backup", "source-database", "destination-database"}, []string{})
        controllers.RestoreBackup(c.String("backup"), c.String("source-database"), c.String("destination-database"))
      },
    },
    {
      Name:      "databases",
      Usage:     "list databases",
      Action: func(c *cli.Context) {
        controllers.Databases()
      },
    },
    {
      Name:      "databases:create",
      Usage:     "create database on an existing deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<string>", "Deployment to create database on"},
        cli.StringFlag { "database,db", "<string>", "Name of new database to create"},
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:create", c, []string{"deployment", "database"}, []string{})
        controllers.CreateDatabase(c.String("deployment"), c.String("database"))
      },
    },
    {
      Name:      "databases:info",
      Usage:     "information on database",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "Database name for more information"},
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:info", c, []string{"database"}, []string{})
        controllers.Database(c.String("database"))
      },
    },
    {
      Name:      "databases:remove",
      Usage:     "remove database",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "Name of new database to remove"},
        cli.BoolFlag { "force,f", "Force delete without confirmation" },
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:remove", c, []string{"database"}, []string{})
        controllers.RemoveDatabase(c.String("database"), c.Bool("force"))
      },
    },
    {
      Name:      "deployments",
      Usage:     "list deployments",
      Action: func(c *cli.Context) {
        controllers.Deployments()
      },
    },
    {
      Name:      "deployments:create",
      Usage:     "create a new Elastic Deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "New database name to be created on your new deployment"},
        cli.StringFlag { "region,r", "<string>", "Region for deployment. For a list of regions, run 'mongohq regions'"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:create", c, []string{"database", "region"}, []string{})
        controllers.CreateDeployment(c.String("database"), c.String("region"))
      },
    },
    {
      Name:      "deployments:info",
      Usage:     "information on deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "The id for the deployment for more information"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:info", c, []string{"deployment"}, []string{})
        controllers.Deployment(c.String("deployment"))
      },
    },
    {
      Name:      "deployments:mongostat",
      Usage:     "realtime mongostat",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "The id for the deployment for tailing mongostats"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:mongostat", c, []string{"deployment"}, []string{})
        controllers.DeploymentMongoStat(c.String("deployment"))
      },
    },
    {
      Name:      "deployments:logs (pending)",
      Usage:     "tail logs",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "The id for the deployment for tailing logs"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:logs", c, []string{"deployment"}, []string{})
        fmt.Println("Pending")
      },
    },
    {
      Name:      "deployments:oplog",
      Usage:     "tail oplog",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "The id for the deployment to tail an oplog"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:oplog", c, []string{"deployment"}, []string{})
        controllers.DeploymentOplog(c.String("deployment"))
      },
    },
    {
      Name:      "regions",
      Usage:     "list available regions",
      Action: func(c *cli.Context) {
        controllers.Regions()
      },
    },
    {
      Name:      "users",
      Usage:     "list users on a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "The deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "The specific database to list users"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users", c, []string{"deployment", "database"}, []string{})
        controllers.DatabaseUsers(c.String("deployment"), c.String("database"))
      },
    },
    {
      Name:      "users:create",
      Usage:     "add user to a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "The deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "The database name to create the user on"},
        cli.StringFlag { "username,u", "<string>", "The new user to create"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users:create", c, []string{"deployment", "database", "username"}, []string{})
        controllers.DatabaseCreateUser(c.String("deployment"), c.String("database"), c.String("username"))
      },
    },
    {
      Name:      "users:remove",
      Usage:     "remove user from database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "The deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "The database name to remove the user from"},
        cli.StringFlag { "username,u", "<string>", "The user to remove from the deployment"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users:remove", c, []string{"deployment", "database", "username"}, []string{})
        controllers.DatabaseRemoveUser(c.String("deployment"), c.String("database"), c.String("username"))
      },
    },
    {
      Name:      "logout",
      Usage:     "remove stored auth",
      Action:    func(c *cli.Context) {
        controllers.Logout()
      },
    },
  }

  app.Run(os.Args)
}
<commit_msg>Lower case all instructions for consistency<commit_after>package main

import (
  "fmt"
  "os"
  "github.com/codegangsta/cli"
  "github.com/MongoHQ/mongohq-cli"
  "github.com/MongoHQ/controllers"  // MongoHQ CLI functions
)

func requireArguments(command string, c *cli.Context, argumentsSlice []string, errorMessages []string) {
  err := false

  for _, argument := range argumentsSlice {
    if !c.IsSet(argument) {
      err = true
      fmt.Println("--" + argument + " is required")
    }
  }

  if err {
    fmt.Println("\nMissing arguments, for more information, run: mongohq " + command + " --help\n")
    for _, errorMessage := range errorMessages {
      fmt.Println(errorMessage)
    }
    os.Exit(1)
  }
}

func main() {
  app := cli.NewApp()
  app.Name = "mongohq"
  app.Usage = "Allow MongoHQ interaction from the commandline (enables awesomeness)"
  app.Before = controllers.RequireAuth
  app.Version = mongohq_cli.Version()
  app.Commands = []cli.Command{
    {
      Name:      "backups",
      Usage:     "list backups with optional filters",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "(optional) database to list backups for"},
        cli.StringFlag { "deployment,dep", "<string>", "(optional) deployment to list backups for"},
      },
      Action: func(c *cli.Context) {
        filter := map[string]string{}
        if c.IsSet("database") { filter["database"] = c.String("database") }
        if c.IsSet("deployment") { filter["deployment"] = c.String("deployment") }
        controllers.Backups(filter)
      },
    },
    {
      Name:      "backups:info",
      Usage:     "information on backup",
      Flags:     []cli.Flag {
        cli.StringFlag { "backup,b", "<string>", "file name of backup"},
      },
      Action: func(c *cli.Context) {
        requireArguments("backups:info", c, []string{"backup"}, []string{})
        controllers.Backup(c.String("backup"))
      },
    },
    {
      Name:      "backups:restore",
      Usage:     "restore backup to a new database",
      Flags:     []cli.Flag {
        cli.StringFlag { "backup,b", "<string>", "file name of backup"},
        cli.StringFlag { "source-database,source", "<string>", "original database name"},
        cli.StringFlag { "destination-database,destination", "<string>", "new database name"},
      },
      Action: func(c *cli.Context) {
        requireArguments("backups:restore", c, []string{"backup", "source-database", "destination-database"}, []string{})
        controllers.RestoreBackup(c.String("backup"), c.String("source-database"), c.String("destination-database"))
      },
    },
    {
      Name:      "databases",
      Usage:     "list databases",
      Action: func(c *cli.Context) {
        controllers.Databases()
      },
    },
    {
      Name:      "databases:create",
      Usage:     "create database on an existing deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<string>", "deployment to create database on"},
        cli.StringFlag { "database,db", "<string>", "new database to create"},
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:create", c, []string{"deployment", "database"}, []string{})
        controllers.CreateDatabase(c.String("deployment"), c.String("database"))
      },
    },
    {
      Name:      "databases:info",
      Usage:     "information on database",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", " database for more information"},
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:info", c, []string{"database"}, []string{})
        controllers.Database(c.String("database"))
      },
    },
    {
      Name:      "databases:remove",
      Usage:     "remove database",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "database to remove"},
        cli.BoolFlag { "force,f", "delete without confirmation" },
      },
      Action: func(c *cli.Context) {
        requireArguments("databases:remove", c, []string{"database"}, []string{})
        controllers.RemoveDatabase(c.String("database"), c.Bool("force"))
      },
    },
    {
      Name:      "deployments",
      Usage:     "list deployments",
      Action: func(c *cli.Context) {
        controllers.Deployments()
      },
    },
    {
      Name:      "deployments:create",
      Usage:     "create a new Elastic Deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "database,db", "<string>", "new database name"},
        cli.StringFlag { "region,r", "<string>", "region of deployment (for list of regions, run 'mongohq regions')"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:create", c, []string{"database", "region"}, []string{})
        controllers.CreateDeployment(c.String("database"), c.String("region"))
      },
    },
    {
      Name:      "deployments:info",
      Usage:     "information on deployment",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "deployment for more information"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:info", c, []string{"deployment"}, []string{})
        controllers.Deployment(c.String("deployment"))
      },
    },
    {
      Name:      "deployments:mongostat",
      Usage:     "realtime mongostat",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "deployment for watching mongostats"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:mongostat", c, []string{"deployment"}, []string{})
        controllers.DeploymentMongoStat(c.String("deployment"))
      },
    },
    {
      Name:      "deployments:logs (pending)",
      Usage:     "tail logs",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "deployment for tailing database logs"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:logs", c, []string{"deployment"}, []string{})
        fmt.Println("Pending")
      },
    },
    {
      Name:      "deployments:oplog",
      Usage:     "tail oplog",
      Flags:     []cli.Flag {
        cli.StringFlag{"deployment,dep", "<bson_id>", "deployment to tail the oplog"},
      },
      Action: func(c *cli.Context) {
        requireArguments("deployments:oplog", c, []string{"deployment"}, []string{})
        controllers.DeploymentOplog(c.String("deployment"))
      },
    },
    {
      Name:      "regions",
      Usage:     "list available regions",
      Action: func(c *cli.Context) {
        controllers.Regions()
      },
    },
    {
      Name:      "users",
      Usage:     "list users on a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "database to list users"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users", c, []string{"deployment", "database"}, []string{})
        controllers.DatabaseUsers(c.String("deployment"), c.String("database"))
      },
    },
    {
      Name:      "users:create",
      Usage:     "add user to a database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "atabase name to create the user on"},
        cli.StringFlag { "username,u", "<string>", "user to create"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users:create", c, []string{"deployment", "database", "username"}, []string{})
        controllers.DatabaseCreateUser(c.String("deployment"), c.String("database"), c.String("username"))
      },
    },
    {
      Name:      "users:remove",
      Usage:     "remove user from database",
      Flags:     []cli.Flag {
        cli.StringFlag { "deployment,dep", "<bson_id>", "deployment id the database is on"},
        cli.StringFlag { "database,db", "<string>", "database name to remove the user from"},
        cli.StringFlag { "username,u", "<string>", "user to remove from the deployment"},
      },
      Action: func(c *cli.Context) {
        requireArguments("users:remove", c, []string{"deployment", "database", "username"}, []string{})
        controllers.DatabaseRemoveUser(c.String("deployment"), c.String("database"), c.String("username"))
      },
    },
    {
      Name:      "logout",
      Usage:     "remove stored auth",
      Action:    func(c *cli.Context) {
        controllers.Logout()
      },
    },
  }

  app.Run(os.Args)
}
<|endoftext|>


<commit_before>package tools_test

import (
	"bytes"
	"io"
	"testing"

	"github.com/github/git-lfs/errors"
	. "github.com/github/git-lfs/tools"
	"github.com/stretchr/testify/assert"
)

func TestRetriableReaderReturnsSuccessfulReads(t *testing.T) {
	r := NewRetriableReader(bytes.NewBuffer([]byte{0x1, 0x2, 0x3, 0x4}))

	var buf [4]byte
	n, err := r.Read(buf[:])

	assert.Nil(t, err)
	assert.Equal(t, 4, n)
	assert.Equal(t, []byte{0x1, 0x2, 0x3, 0x4}, buf[:])
}

func TestRetriableReaderReturnsEOFs(t *testing.T) {
	r := NewRetriableReader(bytes.NewBuffer([]byte{ /* empty */ }))

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, io.EOF, err)
	assert.Equal(t, 0, n)
}

func TestRetriableReaderMakesErrorsRetriable(t *testing.T) {
	expected := errors.New("example error")

	r := NewRetriableReader(&ErrReader{expected})

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, 0, n)
	assert.EqualError(t, err, "LFS: "+expected.Error())
	assert.True(t, errors.IsRetriableError(err))

}

func TestRetriableReaderDoesNotRewrap(t *testing.T) {
	// expected is already "retriable", as would be the case if the
	// underlying reader was a *RetriableReader itself.
	expected := errors.NewRetriableError(errors.New("example error"))

	r := NewRetriableReader(&ErrReader{expected})

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, 0, n)
	// errors.NewRetriableError wraps the given error with the prefix
	// message "LFS", so these two errors should be equal, indicating that
	// the RetriableReader did not re-wrap the error it received.
	assert.EqualError(t, err, expected.Error())
	assert.True(t, errors.IsRetriableError(err))

}

// ErrReader implements io.Reader and only returns errors.
type ErrReader struct {
	// err is the error that this reader will return.
	err error
}

// Read implements io.Reader#Read, and returns (0, e.err).
func (e *ErrReader) Read(p []byte) (n int, err error) {
	return 0, e.err
}
<commit_msg>tools/test: remove '.' imports, increase readability<commit_after>package tools_test

import (
	"bytes"
	"io"
	"testing"

	"github.com/github/git-lfs/errors"
	"github.com/github/git-lfs/tools"
	"github.com/stretchr/testify/assert"
)

func TestRetriableReaderReturnsSuccessfulReads(t *testing.T) {
	r := tools.NewRetriableReader(bytes.NewBuffer([]byte{0x1, 0x2, 0x3, 0x4}))

	var buf [4]byte
	n, err := r.Read(buf[:])

	assert.Nil(t, err)
	assert.Equal(t, 4, n)
	assert.Equal(t, []byte{0x1, 0x2, 0x3, 0x4}, buf[:])
}

func TestRetriableReaderReturnsEOFs(t *testing.T) {
	r := tools.NewRetriableReader(bytes.NewBuffer([]byte{ /* empty */ }))

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, io.EOF, err)
	assert.Equal(t, 0, n)
}

func TestRetriableReaderMakesErrorsRetriable(t *testing.T) {
	expected := errors.New("example error")

	r := tools.NewRetriableReader(&ErrReader{expected})

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, 0, n)
	assert.EqualError(t, err, "LFS: "+expected.Error())
	assert.True(t, errors.IsRetriableError(err))

}

func TestRetriableReaderDoesNotRewrap(t *testing.T) {
	// expected is already "retriable", as would be the case if the
	// underlying reader was a *RetriableReader itself.
	expected := errors.NewRetriableError(errors.New("example error"))

	r := tools.NewRetriableReader(&ErrReader{expected})

	var buf [1]byte
	n, err := r.Read(buf[:])

	assert.Equal(t, 0, n)
	// errors.NewRetriableError wraps the given error with the prefix
	// message "LFS", so these two errors should be equal, indicating that
	// the RetriableReader did not re-wrap the error it received.
	assert.EqualError(t, err, expected.Error())
	assert.True(t, errors.IsRetriableError(err))

}

// ErrReader implements io.Reader and only returns errors.
type ErrReader struct {
	// err is the error that this reader will return.
	err error
}

// Read implements io.Reader#Read, and returns (0, e.err).
func (e *ErrReader) Read(p []byte) (n int, err error) {
	return 0, e.err
}
<|endoftext|>