diff --git a/Makefile b/Makefile index 229edc8..557c9f6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ clean: @echo "$(GREEN)[OK]$(NOCOLOR) Project was cleaned!" test: - @go test -v -cover -race ./... + @go test -v -cover ./... @echo "$(GREEN)[OK]$(NOCOLOR) Project was tested!" install: generate diff --git a/README.md b/README.md index 8c6de74..7bd8e11 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
+
## Requirements @@ -121,7 +121,7 @@ If you want to say «thank you» or/and support active development `create-go-ap 2. Twit about project [on your Twitter](https://twitter.com/intent/tweet?text=Set%20up%20a%20new%20Go%20%28Golang%29%20full%20stack%20app%20by%20running%20one%20CLI%20command%21%26url%3Dhttps%3A%2F%2Fgithub.com%2Fcreate-go-app%2Fcli). 3. Donate some money to project author via PayPal: [@paypal.me/koddr](https://paypal.me/koddr?locale.x=en_EN). 4. Join DigitalOcean at our [referral link](https://shrts.website/do/server) (your profit is **$100** and we get $25). -5. Buy awesome [domain name with 5% discount](https://shrts.website/reg/domain) at REG.COM. +5. Buy awesome [domain name with **5%** discount](https://shrts.website/reg/domain) at REG.COM. Thanks for your support! 😘 Together, we make this project better every day. diff --git a/cmd/cgapp/main.go b/cmd/cgapp/main.go index 9a18945..08d7d65 100644 --- a/cmd/cgapp/main.go +++ b/cmd/cgapp/main.go @@ -1,30 +1,19 @@ package main -import "github.com/create-go-app/cli/internal/cgapp" +import ( + "log" + "os" -var ( - // cgapp CLI version - version string = "0.8.2" - - // Templates registry - registry = map[string]string{ - // Backend templates - "default": "create-go-app/net_http-go-template", - "echo": "create-go-app/echo-go-template", - "fiber": "create-go-app/fiber-go-template", - - // Frontend templates - "react-js": "create-go-app/react-js-template", - "react-ts": "create-go-app/react-ts-template", - "preact": "create-go-app/preact-js-template", - - // Docker containers - "nginx": "create-go-app/nginx-certbot-docker", - "postgres": "create-go-app/postgres-docker", - } + "github.com/create-go-app/cli/pkg/cgapp" ) func main() { // Start new CLI app - cgapp.New(version, registry) + cli, err := cgapp.New() + if err != nil { + log.Fatal(err) + } + + // Run new CLI + cli.Run(os.Args) } diff --git a/internal/cgapp/new.go b/internal/cgapp/new.go deleted file mode 100644 index 73e3190..0000000 --- a/internal/cgapp/new.go +++ /dev/null @@ -1,212 +0,0 @@ -package cgapp - -import ( - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/create-go-app/cli/cmd/box" - "github.com/urfave/cli/v2" -) - -var ( - // App options: - appPath string - appBackend string - appFrontend string - appWebServer string - appDatabase string - appSilentRunning string -) - -// New function for start new CLI -func New(version string, registry map[string]string) { - // Init - cgapp := &cli.App{} - - // Configure - cgapp.Name = "cgapp" - cgapp.Usage = "set up a new Go (Golang) full stack app by running one command." - cgapp.Version = version - cgapp.EnableBashCompletion = true - - // CLI commands - cgapp.Commands = []*cli.Command{ - { - Name: "create", - Usage: "create new Go app", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "path", - Aliases: []string{"p"}, - Value: ".", - Usage: "path to create app, ex. ~/projects/my-app (default: \".\")", - Required: false, - Destination: &appPath, - }, - &cli.StringFlag{ - Name: "backend", - Aliases: []string{"b"}, - Value: "net/http", - Usage: "backend for your app, ex. Fiber, Echo (default: \"net/http\")", - Required: false, - Destination: &appBackend, - }, - &cli.StringFlag{ - Name: "frontend", - Aliases: []string{"f"}, - Value: "none", - Usage: "frontend for your app, ex. Preact, React.js, React.ts (default: \"none\")", - Required: false, - Destination: &appFrontend, - }, - &cli.StringFlag{ - Name: "webserver", - Aliases: []string{"w"}, - Value: "nginx", - Usage: "web/proxy server for your app (default: \"nginx\")", - Required: false, - Destination: &appWebServer, - }, - &cli.StringFlag{ - Name: "database", - Aliases: []string{"d"}, - Value: "none", - Usage: "database for your app, ex. Postgres (default: \"none\")", - Required: false, - Destination: &appDatabase, - }, - &cli.StringFlag{ - Name: "silent", - Value: "none", - Usage: "silent running (default: \"none\")", - Required: false, - Destination: &appSilentRunning, - }, - }, - Action: func(c *cli.Context) error { - // START message - SendMessage("[*] Create Go App v"+version, "yellow") - SendMessage("\n[START] Creating a new app...", "green") - - // Create main folder for app - SendMessage("\n[PROCESS] App folder and config files", "cyan") - ErrChecker(os.Mkdir(appPath, 0750)) - SendMessage("[OK] App folder was created!", "") - - // Create config files for app - ErrChecker(File(".editorconfig", box.Get("/dotfiles/.editorconfig"))) - ErrChecker(File(".gitignore", box.Get("/dotfiles/.gitignore"))) - ErrChecker(File("Makefile", box.Get("/dotfiles/Makefile"))) - - // Create backend files - SendMessage("\n[PROCESS] App backend", "cyan") - ErrChecker( - Create(&Config{ - name: strings.ToLower(appBackend), - match: "^(net/http|fiber|echo)$", - view: "backend", - folder: appPath, - }, - registry, - ), - ) - - // Create frontend files - if appFrontend != "none" { - SendMessage("\n[PROCESS] App frontend", "cyan") - ErrChecker( - Create(&Config{ - name: strings.ToLower(appFrontend), - match: "^(preact|react-js|react-ts)$", - view: "frontend", - folder: appPath, - }, - registry, - ), - ) - - // Install dependencies - SendMessage("\n[PROCESS] Frontend dependencies", "cyan") - SendMessage("[WAIT] Installing frontend dependencies (may take some time)!", "yellow") - - // Go to ./frontend folder and run npm install - cmd := exec.Command("npm", "install") - cmd.Dir = filepath.Join(appPath, "frontend") - ErrChecker(cmd.Run()) - - SendMessage("[OK] Frontend dependencies was installed!", "green") - } - - // Docker containers - SendMessage("\n[START] Configuring Docker containers...", "green") - - // Check ./frontend folder - SendMessage("\n[PROCESS] File docker-compose.yml", "cyan") - _, err := os.Stat(filepath.Join(appPath, "frontend")) - if !os.IsNotExist(err) { - // If exists, create fullstack app docker-compose override file - ErrChecker( - File( - "docker-compose.yml", - box.Get("/docker/docker-compose.fullstack.yml"), - ), - ) - } else { - // Default docker-compose.yml - ErrChecker( - File( - "docker-compose.yml", - box.Get("/docker/docker-compose.backend.yml"), - ), - ) - } - - // Production settings docker-compose.prod.yml - ErrChecker( - File( - "docker-compose.prod.yml", - box.Get("/docker/docker-compose.fullstack.yml"), - ), - ) - - // Create container files - SendMessage("\n[PROCESS] Web/proxy server", "cyan") - ErrChecker( - Create(&Config{ - name: strings.ToLower(appWebServer), - match: "^(nginx)$", - view: "webserver", - folder: appPath, - }, - registry, - ), - ) - - // Create database files - if appDatabase != "none" { - SendMessage("\n[PROCESS] Database", "cyan") - ErrChecker( - Create(&Config{ - name: strings.ToLower(appDatabase), - match: "^(postgres)$", - view: "database", - folder: appPath, - }, - registry, - ), - ) - } - - // END message - SendMessage("\n[DONE] Run `make` from `"+appPath+"` folder!", "yellow") - - return nil - }, - }, - } - - // Run new CLI - ErrChecker(cgapp.Run(os.Args)) -} diff --git a/pkg/cgapp/actions.go b/pkg/cgapp/actions.go new file mode 100644 index 0000000..f4f399f --- /dev/null +++ b/pkg/cgapp/actions.go @@ -0,0 +1,132 @@ +package cgapp + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/create-go-app/cli/cmd/box" + "github.com/urfave/cli/v2" +) + +// CreateCLIAction actions for `create` CLI command +func CreateCLIAction(c *cli.Context) error { + // START message + SendMessage("[*] Create Go App v"+version, "yellow") + SendMessage("\n[START] Creating a new app...", "green") + + // Create main folder for app + SendMessage("\n[PROCESS] App folder and config files", "cyan") + ErrChecker(os.Mkdir(appPath, 0750)) + SendMessage("[OK] App folder was created!", "") + + // Create config files for app + ErrChecker(File(".editorconfig", box.Get("/dotfiles/.editorconfig"))) + ErrChecker(File(".gitignore", box.Get("/dotfiles/.gitignore"))) + ErrChecker(File("Makefile", box.Get("/dotfiles/Makefile"))) + + // Create backend files + SendMessage("\n[PROCESS] App backend", "cyan") + ErrChecker( + Create(&Config{ + name: strings.ToLower(appBackend), + match: "^(net/http|fiber|echo)$", + view: "backend", + folder: appPath, + }, + registry, + ), + ) + + // Create frontend files + if appFrontend != "none" { + SendMessage("\n[PROCESS] App frontend", "cyan") + ErrChecker( + Create(&Config{ + name: strings.ToLower(appFrontend), + match: "^(preact|react-js|react-ts)$", + view: "frontend", + folder: appPath, + }, + registry, + ), + ) + + // Install dependencies + SendMessage("\n[PROCESS] Frontend dependencies", "cyan") + SendMessage("[WAIT] Installing frontend dependencies (may take some time)!", "yellow") + + // Go to ./frontend folder and run npm install + cmd := exec.Command("npm", "install") + cmd.Dir = filepath.Join(appPath, "frontend") + ErrChecker(cmd.Run()) + + SendMessage("[OK] Frontend dependencies was installed!", "green") + } + + // Docker containers + SendMessage("\n[START] Configuring Docker containers...", "green") + + // Check ./frontend folder + SendMessage("\n[PROCESS] File docker-compose.yml", "cyan") + _, err := os.Stat(filepath.Join(appPath, "frontend")) + if !os.IsNotExist(err) { + // If exists, create fullstack app docker-compose override file + ErrChecker( + File( + "docker-compose.yml", + box.Get("/docker/docker-compose.fullstack.yml"), + ), + ) + } else { + // Default docker-compose.yml + ErrChecker( + File( + "docker-compose.yml", + box.Get("/docker/docker-compose.backend.yml"), + ), + ) + } + + // Production settings docker-compose.prod.yml + ErrChecker( + File( + "docker-compose.prod.yml", + box.Get("/docker/docker-compose.fullstack.yml"), + ), + ) + + // Create container files + SendMessage("\n[PROCESS] Web/proxy server", "cyan") + ErrChecker( + Create(&Config{ + name: strings.ToLower(appWebServer), + match: "^(nginx)$", + view: "webserver", + folder: appPath, + }, + registry, + ), + ) + + // Create database files + if appDatabase != "none" { + SendMessage("\n[PROCESS] Database", "cyan") + ErrChecker( + Create(&Config{ + name: strings.ToLower(appDatabase), + match: "^(postgres)$", + view: "database", + folder: appPath, + }, + registry, + ), + ) + } + + // END message + SendMessage("\n[DONE] Run `make` from `"+appPath+"` folder!", "yellow") + + return nil +} diff --git a/pkg/cgapp/actions_test.go b/pkg/cgapp/actions_test.go new file mode 100644 index 0000000..67461d6 --- /dev/null +++ b/pkg/cgapp/actions_test.go @@ -0,0 +1,37 @@ +package cgapp + +import ( + "os" + "testing" + + "github.com/urfave/cli/v2" +) + +func TestCreateCLIAction(t *testing.T) { + type args struct { + c *cli.Context + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + "success action", + args{}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := CreateCLIAction(tt.args.c); (err != nil) != tt.wantErr { + t.Errorf("CreateCLIAction() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } + + files := []string{".editorconfig", ".gitignore", "Makefile", "docker-compose.yml", "docker-compose.prod.yml"} + for _, name := range files { + os.Remove(name) + } +} diff --git a/internal/cgapp/check.go b/pkg/cgapp/check.go similarity index 86% rename from internal/cgapp/check.go rename to pkg/cgapp/check.go index 3ee5536..76b4eba 100644 --- a/internal/cgapp/check.go +++ b/pkg/cgapp/check.go @@ -1,12 +1,10 @@ package cgapp -import "os" - // ErrChecker function for check error func ErrChecker(err error) { if err != nil { // Show error report SendMessage("[ERROR] "+err.Error(), "red") - os.Exit(1) + return } } diff --git a/internal/cgapp/check_internal_test.go b/pkg/cgapp/check_test.go similarity index 73% rename from internal/cgapp/check_internal_test.go rename to pkg/cgapp/check_test.go index f5ae40f..3f6d449 100644 --- a/internal/cgapp/check_internal_test.go +++ b/pkg/cgapp/check_test.go @@ -1,6 +1,9 @@ package cgapp -import "testing" +import ( + "errors" + "testing" +) func Test_ErrChecker(t *testing.T) { type args struct { @@ -10,13 +13,18 @@ func Test_ErrChecker(t *testing.T) { name string args args }{ - // TODO: Add test cases. { "no error", args{ err: nil, }, }, + { + "error", + args{ + err: errors.New("This is error"), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/cgapp/create.go b/pkg/cgapp/create.go similarity index 100% rename from internal/cgapp/create.go rename to pkg/cgapp/create.go diff --git a/internal/cgapp/create_internal_test.go b/pkg/cgapp/create_test.go similarity index 100% rename from internal/cgapp/create_internal_test.go rename to pkg/cgapp/create_test.go diff --git a/internal/cgapp/file.go b/pkg/cgapp/file.go similarity index 100% rename from internal/cgapp/file.go rename to pkg/cgapp/file.go diff --git a/internal/cgapp/file_internal_test.go b/pkg/cgapp/file_test.go similarity index 95% rename from internal/cgapp/file_internal_test.go rename to pkg/cgapp/file_test.go index a2a7e8c..4cc7dc6 100644 --- a/internal/cgapp/file_internal_test.go +++ b/pkg/cgapp/file_test.go @@ -15,7 +15,6 @@ func TestFile(t *testing.T) { args args wantErr bool }{ - // TODO: Add test cases. { "success", args{ diff --git a/internal/cgapp/message.go b/pkg/cgapp/message.go similarity index 97% rename from internal/cgapp/message.go rename to pkg/cgapp/message.go index f74a7d3..0a89568 100644 --- a/internal/cgapp/message.go +++ b/pkg/cgapp/message.go @@ -33,8 +33,6 @@ func SendMessage(text, color string) { case "cyan": textColor = cyan break - default: - break } // Send colored text diff --git a/pkg/cgapp/message_test.go b/pkg/cgapp/message_test.go new file mode 100644 index 0000000..a7d5863 --- /dev/null +++ b/pkg/cgapp/message_test.go @@ -0,0 +1,55 @@ +package cgapp + +import "testing" + +func TestSendMessage(t *testing.T) { + type args struct { + text string + color string + } + tests := []struct { + name string + args args + }{ + { + "successfully send message", + args{ + text: "Hello World!", + color: "", + }, + }, + { + "successfully send colored message", + args{ + text: "Hello World!", + color: "green", + }, + }, + { + "successfully send colored message", + args{ + text: "Hello World!", + color: "yellow", + }, + }, + { + "successfully send colored message", + args{ + text: "Hello World!", + color: "cyan", + }, + }, + { + "successfully send colored message", + args{ + text: "Hello World!", + color: "red", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + SendMessage(tt.args.text, tt.args.color) + }) + } +} diff --git a/pkg/cgapp/new.go b/pkg/cgapp/new.go new file mode 100644 index 0000000..bfa60f0 --- /dev/null +++ b/pkg/cgapp/new.go @@ -0,0 +1,87 @@ +package cgapp + +import ( + "github.com/urfave/cli/v2" +) + +// Options +var ( + appPath string + appBackend string + appFrontend string + appWebServer string + appDatabase string + appSilentRunning string +) + +// New function for start new CLI +func New() (*cli.App, error) { + // Init + app := &cli.App{} + + // Configure + app.Name = "cgapp" + app.Usage = "set up a new Go (Golang) full stack app by running one command." + app.Version = version + app.EnableBashCompletion = true + + // CLI commands + app.Commands = []*cli.Command{ + { + Name: "create", + Usage: "create new Go app", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Value: ".", + Usage: "path to create app, ex. ~/projects/my-app (default: \".\")", + Required: false, + Destination: &appPath, + }, + &cli.StringFlag{ + Name: "backend", + Aliases: []string{"b"}, + Value: "net/http", + Usage: "backend for your app, ex. Fiber, Echo (default: \"net/http\")", + Required: false, + Destination: &appBackend, + }, + &cli.StringFlag{ + Name: "frontend", + Aliases: []string{"f"}, + Value: "none", + Usage: "frontend for your app, ex. Preact, React.js, React.ts (default: \"none\")", + Required: false, + Destination: &appFrontend, + }, + &cli.StringFlag{ + Name: "webserver", + Aliases: []string{"w"}, + Value: "nginx", + Usage: "web/proxy server for your app (default: \"nginx\")", + Required: false, + Destination: &appWebServer, + }, + &cli.StringFlag{ + Name: "database", + Aliases: []string{"d"}, + Value: "none", + Usage: "database for your app, ex. Postgres (default: \"none\")", + Required: false, + Destination: &appDatabase, + }, + &cli.StringFlag{ + Name: "silent", + Value: "none", + Usage: "silent running (default: \"none\")", + Required: false, + Destination: &appSilentRunning, + }, + }, + Action: CreateCLIAction, + }, + } + + return app, nil +} diff --git a/pkg/cgapp/new_test.go b/pkg/cgapp/new_test.go new file mode 100644 index 0000000..44f7fdf --- /dev/null +++ b/pkg/cgapp/new_test.go @@ -0,0 +1,18 @@ +package cgapp + +import "testing" + +func TestNew(t *testing.T) { + tests := []struct { + name string + }{ + { + "successfully created new CLI", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + New() + }) + } +} diff --git a/pkg/cgapp/version.go b/pkg/cgapp/version.go new file mode 100644 index 0000000..806059a --- /dev/null +++ b/pkg/cgapp/version.go @@ -0,0 +1,23 @@ +package cgapp + +var ( + // cgapp CLI version + version = "0.8.3" + + // Templates registry + registry = map[string]string{ + // Backend templates + "net/http": "create-go-app/net_http-go-template", + "echo": "create-go-app/echo-go-template", + "fiber": "create-go-app/fiber-go-template", + + // Frontend templates + "react-js": "create-go-app/react-js-template", + "react-ts": "create-go-app/react-ts-template", + "preact": "create-go-app/preact-js-template", + + // Docker containers + "nginx": "create-go-app/nginx-certbot-docker", + "postgres": "create-go-app/postgres-docker", + } +)