diff --git a/CHANGELOG.md b/CHANGELOG.md index 41d6c1a..269c3fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes will be docmented here. +## 1.2.0 + +* Fixed VueJS linter errors +* Using DbName in all places instead of AppName +* Better help messages when answering questions +* Go moduels, Node modules are automatically installed for you +* The Vue app is built for you for the first time. This reduces errors when just trying to do a first run + ## 1.1.0 * Add ability to generate Go model files from database tables diff --git a/Makefile b/Makefile index 1481c32..8f6ecf2 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,9 @@ endif # Build tasks # +run: ## Run the app + go run . + build-windows: ## Create a compiled Windows binary GOOS=windows GOARCH=amd64 ${GC}.exe diff --git a/VERSION b/VERSION index 9084fa2..26aaba0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/main.go b/main.go index 6f86119..0a32a91 100644 --- a/main.go +++ b/main.go @@ -8,11 +8,13 @@ import ( "io" "io/fs" "os" + "os/exec" "strings" "text/template" "time" "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" "github.com/app-nerds/postgresr" "github.com/iancoleman/strcase" "github.com/jackc/pgx/v4" @@ -52,14 +54,6 @@ type appValues struct { GithubToken string } -type dbValues struct { - DbHost string - DbUser string - DbPassword string - DbName string - WantModel bool -} - type tableStruct struct { Name string Columns []tableColumn @@ -100,48 +94,61 @@ func main() { WantDatabase: false, } - fmt.Printf("Welcome to App Nerds Web Application Generator (%s)\n\n", Version) + fmt.Printf("\nšŸ™ Welcome to App Nerds Web Application Generator (%s)\n\n", Version) + fmt.Printf("Please note that you'll need the following tools to develop in this application:\n") + fmt.Printf(" Go - https://golang.org\n") + fmt.Printf(" Swag - https://github.com/swaggo/swag\n") + fmt.Printf(" NodeJS - https://nodejs.org\n") + fmt.Printf(" Vue CLI - https://cli.vuejs.org\n") + fmt.Printf("\n") firstQuestions := []*survey.Question{ { - Name: "GithubPath", - Prompt: &survey.Input{Message: "Enter the Github path for this application's home repo"}, + Name: "GithubPath", + Prompt: &survey.Input{ + Message: "Enter the Github path for this application's home repo", + Help: "This should be the path to a Github repository. eg. github.com/app-nerds/my-new-app", + }, Validate: survey.Required, }, { Name: "CompanyName", - Prompt: &survey.Input{Message: "Company name"}, + Prompt: &survey.Input{Message: "Company name", Default: "App Nerds"}, }, { - Name: "Title", - Prompt: &survey.Input{Message: "Title", Help: "Used in browser and README"}, + Name: "Title", + Prompt: &survey.Input{ + Message: "Title", + Help: "This value is used in the browser title and README", + }, }, { - Name: "AppName", - Prompt: &survey.Input{Message: "Application name", Help: "No spaces"}, - Validate: func(val interface{}) error { - value := val.(string) - - if hasSpace(value) { - return errors.New("Application name cannot contain spaces!") - } - - return nil + Name: "AppName", + Prompt: &survey.Input{ + Message: "Application name", + Help: "This is used primary as the final executable name. There should be no spaces in this name. Spaces will be replaced with hyphens", }, + Transform: survey.TransformString(func(s string) string { + lower := strings.ToLower(s) + nospaces := strings.ReplaceAll(lower, " ", "-") + notabs := strings.ReplaceAll(nospaces, "\t", "-") + + return notabs + }), }, { - Name: "EnvPrefix", - Prompt: &survey.Input{Message: "Environment variable prefix", Help: "No spaces"}, - Transform: survey.TransformString(strings.ToUpper), - Validate: func(val interface{}) error { - value := val.(string) - - if hasSpace(value) { - return errors.New("Environment prefix cannot have spaces!") - } - - return nil + Name: "EnvPrefix", + Prompt: &survey.Input{ + Message: "Environment variable prefix", + Help: "This prefix will be applied to configuration values pulled from the environment. eg. PREFIX_SERVER_HOST. No spaces allowed.", }, + Transform: survey.TransformString(func(s string) string { + upper := strings.ToUpper(s) + nospaces := strings.ReplaceAll(upper, " ", "") + notabs := strings.ReplaceAll(nospaces, "\t", "") + + return notabs + }), }, { Name: "Description", @@ -152,16 +159,24 @@ func main() { Prompt: &survey.Input{Message: "Enter your email address"}, }, { - Name: "GithubToken", - Prompt: &survey.Input{Message: "Enter your Github Personal Access Token", Help: "This is used for accessing private repos in Docker builds"}, + Name: "GithubToken", + Prompt: &survey.Input{ + Message: "Enter your Github Personal Access Token", + Help: "This is used for accessing private repos in Docker builds. See https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token", + }, }, { Name: "WantDatabase", - Prompt: &survey.Confirm{Message: "Would you like to use a database?"}, + Prompt: &survey.Confirm{Message: "Would you like to use a database?", Default: false}, }, } if err = survey.Ask(firstQuestions, &values); err != nil { + if err == terminal.InterruptErr { + fmt.Printf("\nCancelling.\n") + os.Exit(-1) + } + logrus.WithError(err).Fatalf("There was an error!") } @@ -194,6 +209,11 @@ func main() { } if err = survey.Ask(dbQuestions, &values); err != nil { + if err == terminal.InterruptErr { + fmt.Printf("\nCancelling.\n") + os.Exit(-1) + } + logrus.WithError(err).Fatalf("There was an error!") } @@ -208,7 +228,14 @@ func main() { Options: tableNames, } - survey.AskOne(whichTablesPrompt, &selectedTables) + if err = survey.AskOne(whichTablesPrompt, &selectedTables); err != nil { + if err == terminal.InterruptErr { + fmt.Printf("\nCancelling.\n") + os.Exit(-1) + } + + logrus.WithError(err).Fatalf("There was an error!") + } if tables, err = getColumnsForTables(values, selectedTables); err != nil { logrus.WithError(err).Fatalf("Error getting column information!") @@ -269,6 +296,10 @@ func main() { "templates/app/app-nerds-logo.jpg": "app/src/assets/images/app-nerds-logo.jpg", } + fmt.Printf("\nCreating your new application!\n") + fmt.Printf(" This might take a couple of minutes... ā˜•ļø\n") + fmt.Printf("\n") + /* * Create directories */ @@ -390,17 +421,22 @@ func main() { } } + /* + * Do the steps the initialze the new app + */ + if err = modDownload(); err != nil { + logrus.WithError(err).Fatalf("Error downloading Go modules") + } + + if err = npmInstall(); err != nil { + logrus.WithError(err).Fatalf("Error installing Node modules") + } + + if err = buildNode(); err != nil { + logrus.WithError(err).Fatalf("Error building NodeJS app") + } + fmt.Printf("\nšŸŽ‰ Congratulations! Your new application is ready.\n") - fmt.Printf("Please note that you'll need the following tools to develop in this application:\n") - fmt.Printf(" Go - https://golang.org\n") - fmt.Printf(" Swag - https://github.com/swaggo/swag\n") - fmt.Printf(" NodeJS - https://nodejs.org\n") - fmt.Printf(" Vue CLI - https://cli.vuejs.org\n") - fmt.Printf("\nTo begin execute the following:\n\n") - fmt.Printf(" cd %s/app\n", values.AppName) - fmt.Printf(" npm install && npm run build\n") - fmt.Printf(" cd ..\n") - fmt.Printf(" go get\n") if values.WantDatabase { fmt.Printf("\nšŸ‘©ā€šŸ’» Since you opted to have a database setup, in a new terminal window run: \n") @@ -608,3 +644,57 @@ type {{.Name}} struct { func hasSpace(value string) bool { return strings.ContainsAny(value, " \t") } + +func modDownload() error { + cmd := exec.Command("go", "mod", "download") + fmt.Printf("Downloading Go modules...\n") + + err := cmd.Run() + return err +} + +func npmInstall() error { + var ( + err error + ) + + if err = os.Chdir("app"); err != nil { + return errors.New("error changing to app directory in npmInstall()") + } + + cmd := exec.Command("npm", "install") + fmt.Printf("Installing NodeJS modules...\n") + + if err = cmd.Run(); err != nil { + return err + } + + if err = os.Chdir(".."); err != nil { + return errors.New("error changing back to project root directory in npmInstall()") + } + + return nil +} + +func buildNode() error { + var ( + err error + ) + + if err = os.Chdir("app"); err != nil { + return errors.New("error changing to app directory in buildNode()") + } + + cmd := exec.Command("npm", "run", "build") + fmt.Printf("Building NodeJS app...\n") + + if err = cmd.Run(); err != nil { + return err + } + + if err = os.Chdir(".."); err != nil { + return errors.New("error changing back to project root directory in buildNode()") + } + + return nil +} diff --git a/templates/Config.go.tmpl b/templates/Config.go.tmpl index d973be1..b935ec7 100644 --- a/templates/Config.go.tmpl +++ b/templates/Config.go.tmpl @@ -3,7 +3,6 @@ package main import ( "flag" "fmt" - "strings" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -23,11 +22,11 @@ func NewConfig(serverVersion string) *viper.Viper { result.SetDefault("server.cert", "") result.BindEnv("server.cert", fmt.Sprintf("%s_SERVER_CERT", ENV_PREFIX)) - flag.String("server.cert", "", "SSL certificate file name (minus extension)") + flag.String("server.cert", "", "SSL certificate file name (minus extension)"){{if .WantDatabase}} - result.SetDefault("database.url", fmt.Sprintf("postgres://%s:%s@%s/%s", DEFAULT_DB_USER, DEFAULT_DB_PASSWORD, DEFAULT_DB_HOST, strings.ToLower(APP_NAME))) + result.SetDefault("database.url", fmt.Sprintf("postgres://%s:%s@%s/%s", DEFAULT_DB_USER, DEFAULT_DB_PASSWORD, DEFAULT_DB_HOST, DEFAULT_DB_NAME)) result.BindEnv("database.url", fmt.Sprintf("%s_DATABASE_URL", ENV_PREFIX)) - flag.String("database.url", fmt.Sprintf("postgres://%s:%s@%s/%s", DEFAULT_DB_USER, DEFAULT_DB_PASSWORD, DEFAULT_DB_HOST, strings.ToLower(APP_NAME)), "URL for connecting to a database") + flag.String("database.url", fmt.Sprintf("postgres://%s:%s@%s/%s", DEFAULT_DB_USER, DEFAULT_DB_PASSWORD, DEFAULT_DB_HOST, DEFAULT_DB_NAME), "URL for connecting to a database"){{end}} pflag.CommandLine.AddGoFlagSet(flag.CommandLine) pflag.Parse() diff --git a/templates/app/AlertService.js.tmpl b/templates/app/AlertService.js.tmpl index d86b44f..cb63573 100644 --- a/templates/app/AlertService.js.tmpl +++ b/templates/app/AlertService.js.tmpl @@ -54,4 +54,3 @@ export class AlertService { export function AlertServiceInstaller(Vue) { Vue.prototype.alertService = new AlertService(Vue.toasted); } - diff --git a/templates/app/App.vue.tmpl b/templates/app/App.vue.tmpl index c96b1ff..54d13f7 100644 --- a/templates/app/App.vue.tmpl +++ b/templates/app/App.vue.tmpl @@ -35,4 +35,3 @@ export default { }, }; - diff --git a/templates/app/AppFooter.vue.tmpl b/templates/app/AppFooter.vue.tmpl index 07fcfa7..a618b19 100644 --- a/templates/app/AppFooter.vue.tmpl +++ b/templates/app/AppFooter.vue.tmpl @@ -34,4 +34,3 @@ export default { }, }; - diff --git a/templates/app/AppHeader.vue.tmpl b/templates/app/AppHeader.vue.tmpl index daed1c6..2fbb668 100644 --- a/templates/app/AppHeader.vue.tmpl +++ b/templates/app/AppHeader.vue.tmpl @@ -63,4 +63,3 @@ export default { }, }; - diff --git a/templates/app/BaseURL.js.tmpl b/templates/app/BaseURL.js.tmpl index 5d104f7..6fdcea5 100644 --- a/templates/app/BaseURL.js.tmpl +++ b/templates/app/BaseURL.js.tmpl @@ -6,4 +6,3 @@ export const baseURL = process.env.NODE_ENV === "development" ? "http://localhost:8080" : process.env.VUE_APP_{{.EnvPrefix}}_SERVER_HOST; - diff --git a/templates/app/Home.vue.tmpl b/templates/app/Home.vue.tmpl index 07398ab..b9ec964 100644 --- a/templates/app/Home.vue.tmpl +++ b/templates/app/Home.vue.tmpl @@ -71,4 +71,3 @@ export default { }, }; - diff --git a/templates/app/HttpInterceptors.js.tmpl b/templates/app/HttpInterceptors.js.tmpl index f26670b..fe0d7fe 100644 --- a/templates/app/HttpInterceptors.js.tmpl +++ b/templates/app/HttpInterceptors.js.tmpl @@ -31,4 +31,3 @@ export function InstallHttpInterceptors(Vue) { }; }); } - diff --git a/templates/app/Logo.vue.tmpl b/templates/app/Logo.vue.tmpl index 2388acc..4694915 100644 --- a/templates/app/Logo.vue.tmpl +++ b/templates/app/Logo.vue.tmpl @@ -14,6 +14,4 @@ /* * Copyright Ā© {{.Year}}. {{.CompanyName}} All Rights Reserved */ - - diff --git a/templates/app/PageTwo.vue.tmpl b/templates/app/PageTwo.vue.tmpl index ca8e331..4d6eabb 100644 --- a/templates/app/PageTwo.vue.tmpl +++ b/templates/app/PageTwo.vue.tmpl @@ -9,4 +9,3 @@ export default { name: "PageTwo", }; - diff --git a/templates/app/VersionService.js.tmpl b/templates/app/VersionService.js.tmpl index 30eb6f4..54e8938 100644 --- a/templates/app/VersionService.js.tmpl +++ b/templates/app/VersionService.js.tmpl @@ -17,4 +17,3 @@ export class VersionService { export function VersionServiceInstaller(Vue) { Vue.prototype.versionService = new VersionService(Vue.http); } - diff --git a/templates/app/index.js.tmpl b/templates/app/index.js.tmpl index ef1270a..496bc39 100644 --- a/templates/app/index.js.tmpl +++ b/templates/app/index.js.tmpl @@ -55,4 +55,3 @@ router.beforeEach(async (to, from, next) => { }); export default router; - diff --git a/templates/app/main.js.tmpl b/templates/app/main.js.tmpl index e0ce740..4777f67 100644 --- a/templates/app/main.js.tmpl +++ b/templates/app/main.js.tmpl @@ -35,4 +35,3 @@ new Vue({ router, render: h => h(App), }).$mount("#app"); - diff --git a/templates/env.tmpl b/templates/env.tmpl index 74a8caa..15b9830 100644 --- a/templates/env.tmpl +++ b/templates/env.tmpl @@ -3,7 +3,7 @@ STARTER_SERVER_LOGLEVEL=debug STARTER_SERVER_CERT= {{if .WantDatabase}}STARTER_DATABASE_URL=postgres://{{.DbUser}}:{{.DbPassword}}@postgres/{{.AppName}}{{end}} -POSTGRES_DB={{.AppName}} +POSTGRES_DB={{.DbName}} POSTGRES_PORT=5432 POSTGRES_USER={{.DbUser}} POSTGRES_PASSWORD={{.DbPassword}} diff --git a/templates/main.go.tmpl b/templates/main.go.tmpl index a21fd23..bfa8771 100644 --- a/templates/main.go.tmpl +++ b/templates/main.go.tmpl @@ -30,10 +30,11 @@ import ( const ( APP_NAME string = "{{.AppName}}" ENV_PREFIX string = "{{.EnvPrefix}}"{{if .WantDatabase}} - + DEFAULT_DB_HOST string = "{{.DbHost}}" DEFAULT_DB_USER string = "{{.DbUser}}" DEFAULT_DB_PASSWORD string = "{{.DbPassword}}" + DEFAULT_DB_NAME string = "{{.DbName}}" MAX_DB_CONNECTION_ATTEMPTS int = 5{{end}} )