summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md14
-rw-r--r--cmd/ponzu/add.go194
-rw-r--r--cmd/ponzu/main.go19
-rw-r--r--cmd/ponzu/options.go2
-rw-r--r--cmd/ponzu/usage.go13
5 files changed, 240 insertions, 2 deletions
diff --git a/README.md b/README.md
index 74d0ba3..0b9f2f2 100644
--- a/README.md
+++ b/README.md
@@ -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{})