diff options
author | Martin Treusch von Buttlar <martin.tvb@vitraum.de> | 2017-05-15 22:39:14 +0200 |
---|---|---|
committer | Martin Treusch von Buttlar <martin.tvb@vitraum.de> | 2017-05-15 22:39:14 +0200 |
commit | 4c54cc43537fd15cc459030cb792f8171bcd0fd7 (patch) | |
tree | 9e81500281ea4a33685bbf77f7c5ad2bbb3636e2 /cmd | |
parent | 0cf8aa550a3da63cb1509678bf5add0d73925546 (diff) |
add cobra commands
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/ponzu/add.go | 28 | ||||
-rw-r--r-- | cmd/ponzu/build.go | 91 | ||||
-rw-r--r-- | cmd/ponzu/generate.go | 38 | ||||
-rw-r--r-- | cmd/ponzu/main.go | 265 | ||||
-rw-r--r-- | cmd/ponzu/new.go | 176 | ||||
-rw-r--r-- | cmd/ponzu/options.go | 305 | ||||
-rw-r--r-- | cmd/ponzu/upgrade.go | 169 | ||||
-rw-r--r-- | cmd/ponzu/usage.go | 194 |
8 files changed, 615 insertions, 651 deletions
diff --git a/cmd/ponzu/add.go b/cmd/ponzu/add.go index 05d6f16..c1242db 100644 --- a/cmd/ponzu/add.go +++ b/cmd/ponzu/add.go @@ -8,15 +8,35 @@ import ( "os/exec" "path/filepath" "strings" + + "github.com/spf13/cobra" ) +var addCmd = &cobra.Command{ + Use: "add <repo>", + Short: "Downloads addon from specified import path", + Long: `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`, + RunE: func(cmd *cobra.Command, args []string) error { + // expecting two args, add and the go gettable package uri + if len(args) < 1 { + return errors.New("repo not given") + } + + return getAddon(args[0]) + }, +} + // 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 { +func getAddon(addonPath string) error { var cmdOptions []string - var addonPath = args[1] // Go get cmdOptions = append(cmdOptions, "get", addonPath) @@ -165,3 +185,7 @@ func copyFileContents(src, dst string) (err error) { func addError(err error) error { return errors.New("Ponzu add failed. " + "\n" + err.Error()) } + +func init() { + rootCmd.AddCommand(addCmd) +} diff --git a/cmd/ponzu/build.go b/cmd/ponzu/build.go new file mode 100644 index 0000000..3797e84 --- /dev/null +++ b/cmd/ponzu/build.go @@ -0,0 +1,91 @@ +package main + +import ( + "errors" + "os" + "os/exec" + "path/filepath" + + "github.com/spf13/cobra" +) + +func buildPonzuServer() error { + pwd, err := os.Getwd() + if err != nil { + return err + } + + // copy all ./content files to internal vendor directory + src := "content" + dst := filepath.Join("cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "content") + err = emptyDir(dst) + if err != nil { + return err + } + err = copyFilesWarnConflicts(src, dst, []string{"doc.go"}) + if err != nil { + return err + } + + // copy all ./addons files & dirs to internal vendor directory + src = "addons" + dst = filepath.Join("cmd", "ponzu", "vendor") + err = copyFilesWarnConflicts(src, dst, nil) + if err != nil { + return err + } + + // execute go build -o ponzu-cms cmd/ponzu/*.go + buildOptions := []string{"build", "-o", buildOutputName()} + cmdBuildFiles := []string{ + "main.go", "options.go", "generate.go", + "usage.go", "paths.go", "add.go", + } + var cmdBuildFilePaths []string + for _, file := range cmdBuildFiles { + p := filepath.Join(pwd, "cmd", "ponzu", file) + cmdBuildFilePaths = append(cmdBuildFilePaths, p) + } + + build := exec.Command(gocmd, append(buildOptions, cmdBuildFilePaths...)...) + build.Stderr = os.Stderr + build.Stdout = os.Stdout + + err = build.Start() + if err != nil { + return errors.New("Ponzu build step failed. Please try again. " + "\n" + err.Error()) + + } + err = build.Wait() + if err != nil { + return errors.New("Ponzu build step failed. Please try again. " + "\n" + err.Error()) + + } + + return nil +} + +var buildCmd = &cobra.Command{ + Use: "build", + Short: "build will build/compile the project to then be run.", + Long: `From within your Ponzu project directory, running build will copy and move +the necessary files from your workspace into the vendored directory, and +will build/compile the project to then be run. + +Example: +$ ponzu build +(or) +$ ponzu -gocmd=go1.8rc1 build + +By providing the 'gocmd' flag, you can specify which Go command to build the +project, if testing a different release of Go. + +Errors will be reported, but successful build commands return nothing.`, + RunE: func(cmd *cobra.Command, args []string) error { + return buildPonzuServer() + }, +} + +func init() { + rootCmd.AddCommand(buildCmd) +} diff --git a/cmd/ponzu/generate.go b/cmd/ponzu/generate.go index 5211df8..cc2b93a 100644 --- a/cmd/ponzu/generate.go +++ b/cmd/ponzu/generate.go @@ -8,6 +8,8 @@ import ( "path/filepath" "strings" "text/template" + + "github.com/spf13/cobra" ) type generateType struct { @@ -413,3 +415,39 @@ func generateContentType(args []string) error { return nil } + +var generateCmd = &cobra.Command{ + Use: "generate <generator type (,...fields)>", + Short: "generate boilerplate code for various Ponzu components", + Long: `Generate boilerplate code for various Ponzu components, such as 'content'. + +Example: +$ ponzu gen content review title:"string" body:"string" rating:"int" tags:"[]string" + +The command above will generate a file 'content/review.go' with boilerplate +methods, as well as struct definition, and corresponding field tags like: + +type Review struct { + Title string ` + "`json:" + `"title"` + "`" + ` + Body string ` + "`json:" + `"body"` + "`" + ` + Rating int ` + "`json:" + `"rating"` + "`" + ` + Tags []string ` + "`json:" + `"tags"` + "`" + ` +} + +The generate command will intelligently parse more sophisticated field names +such as 'field_name' and convert it to 'FieldName' and vice versa, only where +appropriate as per common Go idioms. Errors will be reported, but successful +generate commands return nothing.`, +} + +var contentCmd = &cobra.Command{ + Use: "content <namespace> <field> <field>...", + RunE: func(cmd *cobra.Command, args []string) error { + return generateContentType(args) + }, +} + +func init() { + generateCmd.AddCommand(contentCmd) + rootCmd.AddCommand(generateCmd) +} diff --git a/cmd/ponzu/main.go b/cmd/ponzu/main.go index d57e776..6b801f6 100644 --- a/cmd/ponzu/main.go +++ b/cmd/ponzu/main.go @@ -5,7 +5,7 @@ package main import ( - "flag" + "errors" "fmt" "log" "net/http" @@ -13,19 +13,20 @@ import ( "os/exec" "path/filepath" "strings" + "time" "github.com/ponzu-cms/ponzu/system/admin" "github.com/ponzu-cms/ponzu/system/api" "github.com/ponzu-cms/ponzu/system/api/analytics" "github.com/ponzu-cms/ponzu/system/db" "github.com/ponzu-cms/ponzu/system/tls" + "github.com/spf13/cobra" + "github.com/spf13/viper" _ "github.com/ponzu-cms/ponzu/content" ) var ( - usage = usageHeader + usageNew + usageGenerate + - usageBuild + usageRun + usageUpgrade + usageAdd + usageVersion port int httpsport int https bool @@ -36,107 +37,46 @@ var ( dev bool fork string gocmd string + year = fmt.Sprintf("%d", time.Now().Year()) ) -func main() { - flag.Usage = func() { - fmt.Println(usage) - } - - flag.IntVar(&port, "port", 8080, "port for ponzu to bind its HTTP listener") - flag.IntVar(&httpsport, "httpsport", 443, "port for ponzu to bind its HTTPS listener") - flag.BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management") - flag.BoolVar(&devhttps, "devhttps", false, "[dev environment] enable automatic TLS/SSL certificate management") - flag.BoolVar(&dev, "dev", false, "modify environment for Ponzu core development") - flag.BoolVar(&cli, "cli", false, "specify that information should be returned about the CLI, not project") - flag.StringVar(&fork, "fork", "", "modify repo source for Ponzu core development") - flag.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go") - flag.Parse() - - args := flag.Args() - - if len(args) < 1 { - fmt.Println(usage) - os.Exit(0) - } - - switch args[0] { - case "help", "h": - if len(args) < 2 { - fmt.Println(usageHelp) - fmt.Println(usage) - os.Exit(0) - } - - switch args[1] { - case "new": - fmt.Println(usageNew) - os.Exit(0) - - case "generate", "gen", "g": - fmt.Println(usageGenerate) - os.Exit(0) - - case "build": - fmt.Println(usageBuild) - os.Exit(0) - - case "run": - fmt.Println(usageRun) - os.Exit(0) - - case "upgrade": - fmt.Println(usageUpgrade) - os.Exit(0) - - case "version", "v": - fmt.Println(usageVersion) - os.Exit(0) - - case "add", "a": - fmt.Println(usageAdd) - os.Exit(0) - } +var rootCmd = &cobra.Command{ + Use: "ponzu", + Long: `Ponzu is a powerful and efficient open-source HTTP server framework and CMS. It +provides automatic, free, and secure HTTP/2 over TLS (certificates obtained via +[Let's Encrypt](https://letsencrypt.org)), a useful CMS and scaffolding to +generate set-up code, and a fast HTTP API on which to build modern applications. - case "new": - if len(args) < 2 { - fmt.Println(usageNew) - os.Exit(0) - } - - err := newProjectInDir(args[1]) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - case "generate", "gen", "g": - if len(args) < 3 { - fmt.Println(usageGenerate) - os.Exit(0) - } - - // check what we are asked to generate - switch args[1] { - case "content", "c": - err := generateContentType(args[2:]) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - default: - msg := fmt.Sprintf("Generator '%s' is not implemented.", args[1]) - fmt.Println(msg) - } - - case "build": - err := buildPonzuServer(args) - if err != nil { - fmt.Println(err) - os.Exit(1) - } +Ponzu is released under the BSD-3-Clause license (see LICENSE). +(c) 2016 - ` + year + ` Boss Sauce Creative, LLC`, +} - case "run": +var runCmd = &cobra.Command{ + Use: "run <service(,service)>", + Short: "starts the 'ponzu' HTTP server for the JSON API and or Admin System.", + Long: `Starts the 'ponzu' HTTP server for the JSON API, Admin System, or both. +The segments, separated by a comma, describe which services to start, either +'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, +if the server should utilize TLS encryption - served over HTTPS, which is +automatically managed using Let's Encrypt (https://letsencrypt.org) + +Example: +$ ponzu run +(or) +$ ponzu -port=8080 --https run admin,api +(or) +$ ponzu run admin +(or) +$ ponzu -port=8888 run api + +Defaults to '-port=8080 run admin,api' (running Admin & API on port 8080, without TLS) + +Note: +Admin and API cannot run on separate processes unless you use a copy of the +database, since the first process to open it receives a lock. If you intend +to run the Admin and API on separate processes, you must call them with the +'ponzu' command independently.`, + RunE: func(cmd *cobra.Command, args []string) error { var addTLS string if https { addTLS = "--https" @@ -149,8 +89,8 @@ func main() { } var services string - if len(args) > 1 { - services = args[1] + if len(args) > 0 { + services = args[0] } else { services = "admin,api" } @@ -167,32 +107,37 @@ func main() { serve.Stderr = os.Stderr serve.Stdout = os.Stdout - err := serve.Run() - if err != nil { - fmt.Println(err) - os.Exit(1) + return serve.Run() + }, +} + +var ErrWrongOrMissingService = errors.New("To execute 'ponzu serve', " + + "you must specify which service to run.") + +var serveCmd = &cobra.Command{ + Use: "serve <service,service>", + Short: "actually run the server", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return ErrWrongOrMissingService } - case "serve", "s": db.Init() defer db.Close() analytics.Init() defer analytics.Close() - if len(args) > 1 { - services := strings.Split(args[1], ",") - - for _, service := range services { - if service == "api" { - api.Run() - } else if service == "admin" { - admin.Run() - } else { - fmt.Println("To execute 'ponzu serve', you must specify which service to run.") - fmt.Println("$ ponzu --help") - os.Exit(1) - } + services := strings.Split(args[0], ",") + + for _, service := range services { + if service == "api" { + api.Run() + } else if service == "admin" { + admin.Run() + } else { + return ErrWrongOrMissingService } } @@ -229,69 +174,31 @@ func main() { fmt.Printf("Server listening on :%d for HTTP requests...\n", port) fmt.Println("\nvisit `/admin` to get started.") log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", port), nil)) + return nil + }, +} - case "version", "v": - // read ponzu.json value to Stdout - - p, err := ponzu(cli) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - fmt.Fprintf(os.Stdout, "Ponzu v%s\n", p["version"]) - - case "upgrade": - // confirm since upgrade will replace Ponzu core files - path, err := os.Getwd() - if err != nil { - fmt.Println("Failed to find current directory.", err) - os.Exit(1) - } - - fmt.Println("Only files you added to this directory, 'addons' and 'content' will be preserved.") - fmt.Println("Upgrade this project? (y/N):") - - answer, err := getAnswer() - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - switch answer { - case "n", "no", "\r\n", "\n", "": - fmt.Println("") - - case "y", "yes": - err := upgradePonzuProjectDir(path) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - default: - fmt.Println("Input not recognized. No upgrade made. Answer as 'y' or 'n' only.") - } +func init() { + for _, cmd := range []*cobra.Command{runCmd, serveCmd} { + cmd.Flags().IntVar(&port, "port", 8080, "port for ponzu to bind its HTTP listener") + cmd.Flags().IntVar(&httpsport, "httpsport", 443, "port for ponzu to bind its HTTPS listener") + cmd.Flags().BoolVar(&https, "https", false, "enable automatic TLS/SSL certificate management") + cmd.Flags().BoolVar(&devhttps, "devhttps", false, "[dev environment] enable automatic TLS/SSL certificate management") + } - case "add", "a": - // expecting two args, add and the go gettable package uri - if len(args) < 2 { - fmt.Println(usageAdd) - os.Exit(0) - } + rootCmd.AddCommand(runCmd, serveCmd) - err := getAddon(args) - if err != nil { - fmt.Println(err) - os.Exit(1) - } + pflags := rootCmd.PersistentFlags() + pflags.StringVar(&gocmd, "gocmd", "go", "custom go command if using beta or new release of Go") - case "": - fmt.Println(usage) - fmt.Println(usageHelp) + viper.BindPFlag("dev", pflags.Lookup("dev")) + viper.BindPFlag("cli", pflags.Lookup("cli")) + viper.BindPFlag("gocmd", pflags.Lookup("gocmd")) +} - default: - fmt.Println(usage) - fmt.Println(usageHelp) +func main() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) } } diff --git a/cmd/ponzu/new.go b/cmd/ponzu/new.go new file mode 100644 index 0000000..3c18a21 --- /dev/null +++ b/cmd/ponzu/new.go @@ -0,0 +1,176 @@ +package main + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/spf13/cobra" +) + +var newCmd = &cobra.Command{ + Use: "new <projectName>", + Short: "creates a 'ponzu' directory by the name supplied as a parameter", + Long: `Creates a 'ponzu' directory by the name supplied as a parameter +immediately following the 'new' option in the $GOPATH/src directory. Note: +'new' depends on the program 'git' and possibly a network connection. If +there is no local repository to clone from at the local machine's $GOPATH, +'new' will attempt to clone the 'github.com/ponzu-cms/ponzu' package from +over the network. + +Example: +$ ponzu new myProject +> New ponzu project created at $GOPATH/src/myProject + +Errors will be reported, but successful commands return nothing.`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("project name not supplied") + } + + return newProjectInDir(args[0]) + }, +} + +func newProjectInDir(path string) error { + // set path to be nested inside $GOPATH/src + gopath, err := getGOPATH() + if err != nil { + return err + } + path = filepath.Join(gopath, "src", path) + + // check if anything exists at the path, ask if it should be overwritten + if _, err = os.Stat(path); !os.IsNotExist(err) { + fmt.Println("Path exists, overwrite contents? (y/N):") + + answer, err := getAnswer() + if err != nil { + return err + } + + switch answer { + case "n", "no", "\r\n", "\n", "": + fmt.Println("") + + case "y", "yes": + err := os.RemoveAll(path) + if err != nil { + return fmt.Errorf("Failed to overwrite %s. \n%s", path, err) + } + + return createProjectInDir(path) + + default: + fmt.Println("Input not recognized. No files overwritten. Answer as 'y' or 'n' only.") + } + + return nil + } + + return createProjectInDir(path) +} + +func createProjectInDir(path string) error { + gopath, err := getGOPATH() + if err != nil { + return err + } + repo := ponzuRepo + local := filepath.Join(gopath, "src", filepath.Join(repo...)) + network := "https://" + strings.Join(repo, "/") + ".git" + if !strings.HasPrefix(path, gopath) { + path = filepath.Join(gopath, path) + } + + // create the directory or overwrite it + err = os.MkdirAll(path, os.ModeDir|os.ModePerm) + if err != nil { + return err + } + + if dev { + if fork != "" { + local = filepath.Join(gopath, "src", fork) + } + + devClone := exec.Command("git", "clone", local, "--branch", "ponzu-dev", "--single-branch", path) + devClone.Stdout = os.Stdout + devClone.Stderr = os.Stderr + + err = devClone.Start() + if err != nil { + return err + } + + err = devClone.Wait() + if err != nil { + return err + } + + err = vendorCorePackages(path) + if err != nil { + return err + } + + fmt.Println("Dev build cloned from " + local + ":ponzu-dev") + return nil + } + + // try to git clone the repository from the local machine's $GOPATH + localClone := exec.Command("git", "clone", local, path) + localClone.Stdout = os.Stdout + localClone.Stderr = os.Stderr + + err = localClone.Start() + if err != nil { + return err + } + err = localClone.Wait() + if err != nil { + fmt.Println("Couldn't clone from", local, "- trying network...") + + // try to git clone the repository over the network + networkClone := exec.Command("git", "clone", network, path) + networkClone.Stdout = os.Stdout + networkClone.Stderr = os.Stderr + + err = networkClone.Start() + if err != nil { + fmt.Println("Network clone failed to start. Try again and make sure you have a network connection.") + return err + } + err = networkClone.Wait() + if err != nil { + fmt.Println("Network clone failure.") + // failed + return fmt.Errorf("Failed to clone files from local machine [%s] and over the network [%s].\n%s", local, network, err) + } + } + + // create an internal vendor directory in ./cmd/ponzu and move content, + // management and system packages into it + err = vendorCorePackages(path) + if err != nil { + return err + } + + gitDir := filepath.Join(path, ".git") + err = os.RemoveAll(gitDir) + if err != nil { + fmt.Println("Failed to remove .git directory from your project path. Consider removing it manually.") + } + + fmt.Println("New ponzu project created at", path) + return nil +} + +func init() { + newCmd.Flags().StringVar(&fork, "fork", "", "modify repo source for Ponzu core development") + newCmd.Flags().BoolVar(&dev, "dev", false, "modify environment for Ponzu core development") + + rootCmd.AddCommand(newCmd) +} diff --git a/cmd/ponzu/options.go b/cmd/ponzu/options.go index 77e842c..5cc05e4 100644 --- a/cmd/ponzu/options.go +++ b/cmd/ponzu/options.go @@ -4,53 +4,11 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" - "os/exec" "path/filepath" "strings" - "time" ) -func newProjectInDir(path string) error { - // set path to be nested inside $GOPATH/src - gopath, err := getGOPATH() - if err != nil { - return err - } - path = filepath.Join(gopath, "src", path) - - // check if anything exists at the path, ask if it should be overwritten - if _, err = os.Stat(path); !os.IsNotExist(err) { - fmt.Println("Path exists, overwrite contents? (y/N):") - - answer, err := getAnswer() - if err != nil { - return err - } - - switch answer { - case "n", "no", "\r\n", "\n", "": - fmt.Println("") - - case "y", "yes": - err := os.RemoveAll(path) - if err != nil { - return fmt.Errorf("Failed to overwrite %s. \n%s", path, err) - } - - return createProjectInDir(path) - - default: - fmt.Println("Input not recognized. No files overwritten. Answer as 'y' or 'n' only.") - } - - return nil - } - - return createProjectInDir(path) -} - var ponzuRepo = []string{"github.com", "ponzu-cms", "ponzu"} func getAnswer() (string, error) { @@ -69,100 +27,6 @@ func getAnswer() (string, error) { return answer, nil } -func createProjectInDir(path string) error { - gopath, err := getGOPATH() - if err != nil { - return err - } - repo := ponzuRepo - local := filepath.Join(gopath, "src", filepath.Join(repo...)) - network := "https://" + strings.Join(repo, "/") + ".git" - if !strings.HasPrefix(path, gopath) { - path = filepath.Join(gopath, path) - } - - // create the directory or overwrite it - err = os.MkdirAll(path, os.ModeDir|os.ModePerm) - if err != nil { - return err - } - - if dev { - if fork != "" { - local = filepath.Join(gopath, "src", fork) - } - - devClone := exec.Command("git", "clone", local, "--branch", "ponzu-dev", "--single-branch", path) - devClone.Stdout = os.Stdout - devClone.Stderr = os.Stderr - - err = devClone.Start() - if err != nil { - return err - } - - err = devClone.Wait() - if err != nil { - return err - } - - err = vendorCorePackages(path) - if err != nil { - return err - } - - fmt.Println("Dev build cloned from " + local + ":ponzu-dev") - return nil - } - - // try to git clone the repository from the local machine's $GOPATH - localClone := exec.Command("git", "clone", local, path) - localClone.Stdout = os.Stdout - localClone.Stderr = os.Stderr - - err = localClone.Start() - if err != nil { - return err - } - err = localClone.Wait() - if err != nil { - fmt.Println("Couldn't clone from", local, "- trying network...") - - // try to git clone the repository over the network - networkClone := exec.Command("git", "clone", network, path) - networkClone.Stdout = os.Stdout - networkClone.Stderr = os.Stderr - - err = networkClone.Start() - if err != nil { - fmt.Println("Network clone failed to start. Try again and make sure you have a network connection.") - return err - } - err = networkClone.Wait() - if err != nil { - fmt.Println("Network clone failure.") - // failed - return fmt.Errorf("Failed to clone files from local machine [%s] and over the network [%s].\n%s", local, network, err) - } - } - - // create an internal vendor directory in ./cmd/ponzu and move content, - // management and system packages into it - err = vendorCorePackages(path) - if err != nil { - return err - } - - gitDir := filepath.Join(path, ".git") - err = os.RemoveAll(gitDir) - if err != nil { - fmt.Println("Failed to remove .git directory from your project path. Consider removing it manually.") - } - - fmt.Println("New ponzu project created at", path) - return nil -} - func vendorCorePackages(path string) error { vendorPath := filepath.Join(path, "cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu") err := os.MkdirAll(vendorPath, os.ModeDir|os.ModePerm) @@ -281,62 +145,6 @@ func emptyDir(path string) error { return nil } -func buildPonzuServer(args []string) error { - pwd, err := os.Getwd() - if err != nil { - return err - } - - // copy all ./content files to internal vendor directory - src := "content" - dst := filepath.Join("cmd", "ponzu", "vendor", "github.com", "ponzu-cms", "ponzu", "content") - err = emptyDir(dst) - if err != nil { - return err - } - err = copyFilesWarnConflicts(src, dst, []string{"doc.go"}) - if err != nil { - return err - } - - // copy all ./addons files & dirs to internal vendor directory - src = "addons" - dst = filepath.Join("cmd", "ponzu", "vendor") - err = copyFilesWarnConflicts(src, dst, nil) - if err != nil { - return err - } - - // execute go build -o ponzu-cms cmd/ponzu/*.go - buildOptions := []string{"build", "-o", buildOutputName()} - cmdBuildFiles := []string{ - "main.go", "options.go", "generate.go", - "usage.go", "paths.go", "add.go", - } - var cmdBuildFilePaths []string - for _, file := range cmdBuildFiles { - p := filepath.Join(pwd, "cmd", "ponzu", file) - cmdBuildFilePaths = append(cmdBuildFilePaths, p) - } - - build := exec.Command(gocmd, append(buildOptions, cmdBuildFilePaths...)...) - build.Stderr = os.Stderr - build.Stdout = os.Stdout - - err = build.Start() - if err != nil { - return errors.New("Ponzu build step failed. Please try again. " + "\n" + err.Error()) - - } - err = build.Wait() - if err != nil { - return errors.New("Ponzu build step failed. Please try again. " + "\n" + err.Error()) - - } - - return nil -} - func copyAll(src, dst string) error { err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -374,116 +182,3 @@ func copyAll(src, dst string) error { return nil } - -func upgradePonzuProjectDir(path string) error { - core := []string{ - ".gitattributes", - "LICENSE", - "ponzu-banner.png", - "README.md", - "cmd", - "deployment", - "management", - "system", - } - - stamp := fmt.Sprintf("ponzu-%d.bak", time.Now().Unix()) - temp := filepath.Join(os.TempDir(), stamp) - err := os.Mkdir(temp, os.ModeDir|os.ModePerm) - if err != nil { - return err - } - - // track non-Ponzu core items (added by user) - var user []os.FileInfo - list, err := ioutil.ReadDir(path) - if err != nil { - return err - } - - for _, item := range list { - // check if in core - var isCore bool - for _, name := range core { - if item.Name() == name { - isCore = true - break - } - } - - if !isCore { - user = append(user, item) - } - } - - // move non-Ponzu files to temp location - fmt.Println("Preserving files to be restored after upgrade...") - for _, item := range user { - src := filepath.Join(path, item.Name()) - if item.IsDir() { - err := os.Mkdir(filepath.Join(temp, item.Name()), os.ModeDir|os.ModePerm) - if err != nil { - return err - } - } - - err := copyAll(src, temp) - if err != nil { - return err - } - - fmt.Println(" [-]", item.Name()) - - } - - // remove all files in path - for _, item := range list { - err := os.RemoveAll(filepath.Join(path, item.Name())) - if err != nil { - return fmt.Errorf("Failed to remove old Ponzu files.\n%s", err) - } - } - - err = createProjectInDir(path) - if err != nil { - fmt.Println("") - fmt.Println("Upgrade failed...") - fmt.Println("Your code is backed up at the following location:") - fmt.Println(temp) - fmt.Println("") - fmt.Println("Manually create a new Ponzu project here and copy those files within it to fully restore.") - fmt.Println("") - return err - } - - // move non-Ponzu files from temp location backed - restore, err := ioutil.ReadDir(temp) - if err != nil { - return err - } - - fmt.Println("Restoring files preserved before upgrade...") - for _, r := range restore { - p := filepath.Join(temp, r.Name()) - err = copyAll(p, path) - if err != nil { - fmt.Println("Couldn't merge your previous project files with upgraded one.") - fmt.Println("Manually copy your files from the following directory:") - fmt.Println(temp) - return err - } - - fmt.Println(" [+]", r.Name()) - } - - // clean-up - backups := []string{filepath.Join(path, stamp), temp} - for _, bak := range backups { - err := os.RemoveAll(bak) - if err != nil { - return err - } - } - - return nil -} diff --git a/cmd/ponzu/upgrade.go b/cmd/ponzu/upgrade.go new file mode 100644 index 0000000..252a4f8 --- /dev/null +++ b/cmd/ponzu/upgrade.go @@ -0,0 +1,169 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + "github.com/spf13/cobra" +) + +var upgradeCmd = &cobra.Command{ + Use: "upgrade", + Short: "upgrades your project to the current ponzu version", + Long: `Will backup your own custom project code (like content, addons, uploads, etc) so +if necessary. Before running '$ ponzu upgrade', you should update the 'ponzu' +package by running '$ go get -u github.com/ponzu-cms/ponzu/...' + +Example: +$ ponzu upgrade`, + RunE: func(cmd *cobra.Command, args []string) error { + // confirm since upgrade will replace Ponzu core files + path, err := os.Getwd() + if err != nil { + return fmt.Errorf("Failed to find current directory: %s", err) + } + + fmt.Println("Only files you added to this directory, 'addons' and 'content' will be preserved.") + fmt.Println("Upgrade this project? (y/N):") + + answer, err := getAnswer() + if err != nil { + return err + } + + switch answer { + case "n", "no", "\r\n", "\n", "": + fmt.Println("") + + case "y", "yes": + err := upgradePonzuProjectDir(path) + if err != nil { + return err + } + + default: + fmt.Println("Input not recognized. No upgrade made. Answer as 'y' or 'n' only.") + } + return nil + }, +} + +func upgradePonzuProjectDir(path string) error { + core := []string{ + ".gitattributes", + "LICENSE", + "ponzu-banner.png", + "README.md", + "cmd", + "deployment", + "management", + "system", + } + + stamp := fmt.Sprintf("ponzu-%d.bak", time.Now().Unix()) + temp := filepath.Join(os.TempDir(), stamp) + err := os.Mkdir(temp, os.ModeDir|os.ModePerm) + if err != nil { + return err + } + + // track non-Ponzu core items (added by user) + var user []os.FileInfo + list, err := ioutil.ReadDir(path) + if err != nil { + return err + } + + for _, item := range list { + // check if in core + var isCore bool + for _, name := range core { + if item.Name() == name { + isCore = true + break + } + } + + if !isCore { + user = append(user, item) + } + } + + // move non-Ponzu files to temp location + fmt.Println("Preserving files to be restored after upgrade...") + for _, item := range user { + src := filepath.Join(path, item.Name()) + if item.IsDir() { + err := os.Mkdir(filepath.Join(temp, item.Name()), os.ModeDir|os.ModePerm) + if err != nil { + return err + } + } + + err := copyAll(src, temp) + if err != nil { + return err + } + + fmt.Println(" [-]", item.Name()) + + } + + // remove all files in path + for _, item := range list { + err := os.RemoveAll(filepath.Join(path, item.Name())) + if err != nil { + return fmt.Errorf("Failed to remove old Ponzu files.\n%s", err) + } + } + + err = createProjectInDir(path) + if err != nil { + fmt.Println("") + fmt.Println("Upgrade failed...") + fmt.Println("Your code is backed up at the following location:") + fmt.Println(temp) + fmt.Println("") + fmt.Println("Manually create a new Ponzu project here and copy those files within it to fully restore.") + fmt.Println("") + return err + } + + // move non-Ponzu files from temp location backed + restore, err := ioutil.ReadDir(temp) + if err != nil { + return err + } + + fmt.Println("Restoring files preserved before upgrade...") + for _, r := range restore { + p := filepath.Join(temp, r.Name()) + err = copyAll(p, path) + if err != nil { + fmt.Println("Couldn't merge your previous project files with upgraded one.") + fmt.Println("Manually copy your files from the following directory:") + fmt.Println(temp) + return err + } + + fmt.Println(" [+]", r.Name()) + } + + // clean-up + backups := []string{filepath.Join(path, stamp), temp} + for _, bak := range backups { + err := os.RemoveAll(bak) + if err != nil { + return err + } + } + + return nil +} + +func init() { + rootCmd.AddCommand(upgradeCmd) +} diff --git a/cmd/ponzu/usage.go b/cmd/ponzu/usage.go index 1f1364d..1571ad0 100644 --- a/cmd/ponzu/usage.go +++ b/cmd/ponzu/usage.go @@ -4,175 +4,34 @@ import ( "encoding/json" "fmt" "io/ioutil" + "os" "path/filepath" - "time" -) - -var year = fmt.Sprintf("%d", time.Now().Year()) - -var usageHeader = ` -$ ponzu [flags] command <params> - -Ponzu is a powerful and efficient open-source HTTP server framework and CMS. It -provides automatic, free, and secure HTTP/2 over TLS (certificates obtained via -[Let's Encrypt](https://letsencrypt.org)), a useful CMS and scaffolding to -generate set-up code, and a fast HTTP API on which to build modern applications. - -Ponzu is released under the BSD-3-Clause license (see LICENSE). -(c) 2016 - ` + year + ` Boss Sauce Creative, LLC - -COMMANDS: - -` - -var usageHelp = ` -help, h (command) - - Help command will print the usage for Ponzu, or if a command is entered, it - will show only the usage for that specific command. - - Example: - $ ponzu help generate - - -` - -var usageNew = ` -new <directory> - - Creates a 'ponzu' directory, or one by the name supplied as a parameter - immediately following the 'new' option in the $GOPATH/src directory. Note: - 'new' depends on the program 'git' and possibly a network connection. If - there is no local repository to clone from at the local machine's $GOPATH, - 'new' will attempt to clone the 'github.com/ponzu-cms/ponzu' package from - over the network. - - Example: - $ ponzu new myProject - > New ponzu project created at $GOPATH/src/myProject - - Errors will be reported, but successful commands return nothing. - - -` - -var usageGenerate = ` -generate, gen, g <generator type (,...fields)> - - Generate boilerplate code for various Ponzu components, such as 'content'. - - Example: - $ ponzu gen content review title:"string" body:"string" rating:"int" tags:"[]string" - - The command above will generate a file 'content/review.go' with boilerplate - methods, as well as struct definition, and corresponding field tags like: - - type Review struct { - Title string ` + "`json:" + `"title"` + "`" + ` - Body string ` + "`json:" + `"body"` + "`" + ` - Rating int ` + "`json:" + `"rating"` + "`" + ` - Tags []string ` + "`json:" + `"tags"` + "`" + ` - } - - The generate command will intelligently parse more sophisticated field names - such as 'field_name' and convert it to 'FieldName' and vice versa, only where - appropriate as per common Go idioms. Errors will be reported, but successful - generate commands return nothing. - - -` - -var usageBuild = ` -[-gocmd=go] build - - From within your Ponzu project directory, running build will copy and move - the necessary files from your workspace into the vendored directory, and - will build/compile the project to then be run. - - Example: - $ ponzu build - (or) - $ ponzu -gocmd=go1.8rc1 build - - By providing the 'gocmd' flag, you can specify which Go command to build the - project, if testing a different release of Go. - Errors will be reported, but successful build commands return nothing. - - -` - -var usageRun = ` -[[-port=8080] [--https|--devhttps]] run <service(,service)> - - Starts the 'ponzu' HTTP server for the JSON API, Admin System, or both. - The segments, separated by a comma, describe which services to start, either - 'admin' (Admin System / CMS backend) or 'api' (JSON API), and, optionally, - if the server should utilize TLS encryption - served over HTTPS, which is - automatically managed using Let's Encrypt (https://letsencrypt.org) - - Example: - $ ponzu run - (or) - $ ponzu -port=8080 --https run admin,api - (or) - $ ponzu run admin - (or) - $ ponzu -port=8888 run api - - Defaults to '-port=8080 run admin,api' (running Admin & API on port 8080, without TLS) - - Note: - Admin and API cannot run on separate processes unless you use a copy of the - database, since the first process to open it receives a lock. If you intend - to run the Admin and API on separate processes, you must call them with the - 'ponzu' command independently. - - -` - -var usageUpgrade = ` -upgrade - - Will backup your own custom project code (like content, addons, uploads, etc) so - we can safely re-clone Ponzu from the latest version you have or from the network - if necessary. Before running '$ ponzu upgrade', you should update the 'ponzu' - package by running '$ go get -u github.com/ponzu-cms/ponzu/...' - - Example: - $ ponzu upgrade - - -` - -var usageVersion = ` -[--cli] version, v - - Prints the version of Ponzu your project is using. Must be called from - within a Ponzu project directory. - - Example: - $ ponzu version - > Ponzu v0.7.1 - (or) - $ ponzu --cli version - > Ponzu v0.7.2 - - -` - -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 + "github.com/spf13/cobra" +) +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Prints the version of Ponzu your project is using.", + Long: `Prints the version of Ponzu your project is using. Must be called from +within a Ponzu project directory. + +Example: +$ ponzu version +> Ponzu v0.7.1 +(or) +$ ponzu --cli version +> Ponzu v0.7.2`, + Run: func(cmd *cobra.Command, args []string) { + p, err := ponzu(cli) + if err != nil { + fmt.Println(err) + os.Exit(1) + } -` + fmt.Fprintf(os.Stdout, "Ponzu v%s\n", p["version"]) + }, +} func ponzu(isCLI bool) (map[string]interface{}, error) { kv := make(map[string]interface{}) @@ -199,3 +58,8 @@ func ponzu(isCLI bool) (map[string]interface{}, error) { return kv, nil } + +func init() { + versionCmd.Flags().BoolVar(&cli, "cli", false, "specify that information should be returned about the CLI, not project") + rootCmd.AddCommand(versionCmd) +} |