diff options
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | cmd/ponzu/add.go | 194 | ||||
-rw-r--r-- | cmd/ponzu/main.go | 19 | ||||
-rw-r--r-- | cmd/ponzu/options.go | 2 | ||||
-rw-r--r-- | cmd/ponzu/usage.go | 13 |
5 files changed, 240 insertions, 2 deletions
@@ -184,6 +184,20 @@ $ ponzu upgrade --- +### add, a + +Downloads an addon to GOPATH/src and copys it to the Ponzu project's ./addons directory. +Must be called from within a Ponzu project directory. + +Example: +```bash +$ ponzu add github.com/bosssauce/fbscheduler +``` + +Errors will be reported, but successful add commands return nothing. + +--- + ### version, v Prints the version of Ponzu your project is using. Must be called from within a diff --git a/cmd/ponzu/add.go b/cmd/ponzu/add.go new file mode 100644 index 0000000..abac59c --- /dev/null +++ b/cmd/ponzu/add.go @@ -0,0 +1,194 @@ +package main + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + "os/user" + "path/filepath" + "runtime" + "strings" +) + +// use `go get` to download addon and add to $GOPATH/src, useful +// for IDE auto-import and code completion, then copy entire directory +// tree to project's ./addons folder +func getAddon(args []string) error { + + var cmdOptions []string + var addonPath = args[1] + + // Go get + cmdOptions = append(cmdOptions, "get", addonPath) + get := exec.Command(gocmd, cmdOptions...) + get.Stderr = os.Stderr + get.Stdout = os.Stdout + + err := get.Start() + if err != nil { + return addError(err) + } + err = get.Wait() + if err != nil { + return addError(err) + } + + // copy to ./addons folder + // resolve GOPATH + gopath, err := getGOPATH() + if err != nil { + return addError(err) + } + + pwd, err := os.Getwd() + if err != nil { + return addError(err) + } + + src := filepath.Join(gopath, "src", addonPath) + + // Need to strip the addon name for copyAll? + last := filepath.Base(addonPath) + dest := filepath.Join(pwd, "addons", strings.Replace(addonPath, last, "", 1)) + + err = replicateAll(src, dest) + if err != nil { + return addError(err) + } + return nil +} + +// resolve GOPATH. In 1.8 can be default, or custom. A custom GOPATH can +// also contain multiple paths, in which case 'go get' uses the first +func getGOPATH() (string, error) { + var gopath string + gopath = os.Getenv("GOPATH") + if gopath == "" { + // not set, find the default + usr, err := user.Current() + if err != nil { + return gopath, err + } + gopath = filepath.Join(usr.HomeDir, "go") + } else { + // parse out in case of multiple, retain first + if runtime.GOOS == "windows" { + gopaths := strings.Split(gopath, ";") + gopath = gopaths[0] + } else { + gopaths := strings.Split(gopath, ":") + gopath = gopaths[0] + } + } + return gopath, nil +} + +// this is distinct from copyAll() in that files are copied, not moved, +// since we also need them to remain in $GOPATH/src +// thanks to @markc of stack overflow for the copyFile and copyFileContents functions +func replicateAll(src, dst string) error { + err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + sep := string(filepath.Separator) + + // base == the ponzu project dir + string(filepath.Separator) + parts := strings.Split(src, sep) + base := strings.Join(parts[:len(parts)-1], sep) + base += sep + + target := filepath.Join(dst, path[len(base):]) + + // if its a directory, make dir in dst + if info.IsDir() { + err := os.MkdirAll(target, os.ModeDir|os.ModePerm) + if err != nil { + return err + } + } else { + // if its a file, copy file to dir of dst + err = copyFile(path, target) + if err != nil { + return err + } + } + + return nil + }) + if err != nil { + return err + } + + return nil +} + +// copyFile copies a file from src to dst. if src and dst files exist, and are +// the same, then return success. Otherise, attempt to create a hard link +// between the two files. If that fail, copy the file contents from src to dst. +// thanks to Stack Overflow +func copyFile(src, dst string) (err error) { + sfi, err := os.Stat(src) + if err != nil { + return + } + if !sfi.Mode().IsRegular() { + // cannot copy non-regular files (e.g., directories, + // symlinks, devices, etc.) + return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String()) + } + dfi, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return + } + } else { + if !(dfi.Mode().IsRegular()) { + return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return + } + } + if err = os.Link(src, dst); err == nil { + return + } + err = copyFileContents(src, dst) + return +} + +// copyFileContents copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. +// Thanks for Stack Overflow +func copyFileContents(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + cerr := out.Close() + if err == nil { + err = cerr + } + }() + if _, err = io.Copy(out, in); err != nil { + return + } + err = out.Sync() + return +} + +// generic error return +func addError(err error) error { + return errors.New("Ponzu add failed. " + "\n" + err.Error()) +} diff --git a/cmd/ponzu/main.go b/cmd/ponzu/main.go index f44f626..b3dc708 100644 --- a/cmd/ponzu/main.go +++ b/cmd/ponzu/main.go @@ -21,7 +21,7 @@ import ( var ( usage = usageHeader + usageNew + usageGenerate + - usageBuild + usageRun + usageUpgrade + usageVersion + usageBuild + usageRun + usageUpgrade + usageAdd + usageVersion port int httpsport int https bool @@ -88,6 +88,10 @@ func main() { case "version", "v": fmt.Println(usageVersion) os.Exit(0) + + case "add", "a": + fmt.Println(usageAdd) + os.Exit(0) } case "new": @@ -265,6 +269,19 @@ func main() { fmt.Println("Input not recognized. No upgrade made. Answer as 'y' or 'n' only.") } + case "add", "a": + // expecting two args, add and the go gettable package uri + if len(args) < 2 { + fmt.Println(usageAdd) + os.Exit(0) + } + + err := getAddon(args) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + case "": fmt.Println(usage) fmt.Println(usageHelp) diff --git a/cmd/ponzu/options.go b/cmd/ponzu/options.go index 9023f84..c406975 100644 --- a/cmd/ponzu/options.go +++ b/cmd/ponzu/options.go @@ -305,7 +305,7 @@ func buildPonzuServer(args []string) error { buildOptions := []string{"build", "-o", buildOutputName()} cmdBuildFiles := []string{ "main.go", "options.go", "generate.go", - "usage.go", "paths.go", + "usage.go", "paths.go", "add.go", } var cmdBuildFilePaths []string for _, file := range cmdBuildFiles { diff --git a/cmd/ponzu/usage.go b/cmd/ponzu/usage.go index 3418cc1..fd289cf 100644 --- a/cmd/ponzu/usage.go +++ b/cmd/ponzu/usage.go @@ -162,6 +162,19 @@ var usageVersion = ` ` +var usageAdd = ` +add, a <import path> + + Downloads addon from specified import path to $GOPATH/src and copys it to the + current project's ./addons directory. Must be called from within a + Ponzu project directory. + + Example: + $ ponzu add github.com/bosssauce/fbscheduler + + +` + func ponzu(isCLI bool) (map[string]interface{}, error) { kv := make(map[string]interface{}) |