diff --git a/README.md b/README.md index e2a39d8..1dce3c1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,6 @@ aah - A secure, flexible, rapid Go web framework. ### News - * aah CLI Release Version [released](https://github.com/go-aah/tools/releases/latest) and tagged on Jul 06, 2018. + * aah CLI Release Version [released](https://github.com/go-aah/tools/releases/latest) and tagged on Jul 08, 2018. Visit official website https://aahframework.org to learn more about `aah` framework. diff --git a/aah/aah.go b/aah/aah.go index f7b953a..c40d67e 100644 --- a/aah/aah.go +++ b/aah/aah.go @@ -48,9 +48,10 @@ var ( var errStopHere = errors.New("stop here") func checkPrerequisites() error { + gocmdName := goCmdName() // check go is installed or not - if !ess.LookExecutable("go") { - return errors.New("Unable to find Go executable in PATH") + if !ess.LookExecutable(gocmdName) { + return fmt.Errorf("Unable to find '%s' executable in PATH", gocmdName) } var err error @@ -61,36 +62,16 @@ func checkPrerequisites() error { } // Go executable - gocmdName := goCmdName() if gocmd, err = exec.LookPath(gocmdName); err != nil { return err } - gosrcDir = filepath.Join(gopath, "src") - // git if gitcmd, err = exec.LookPath("git"); err != nil { return err } - // aah - if aahVer, err = aahVersion(); err == errVersionNotExists { - if collectYesOrNo(reader, "aah framework is not installed in GOPATH, would you like to install [Y]es or [N]o") { - args := []string{"get"} - if gocmdName == "go" { - args = append(args, "-u") - } - args = append(args, "aahframework.org/aah.v0") - if _, err := execCmd(gocmd, args, false); err != nil { - return err - } - aahVer, _ = aahVersion() - fmt.Printf("\naah framework successfully installed in GOPATH\n\n") - return nil - } - fmt.Printf("\nOkay, you could do it manually, run '%s get aahframework.org/aah.v0'\n", gocmdName) - return errStopHere - } + gosrcDir = filepath.Join(gopath, "src") return nil } @@ -135,6 +116,14 @@ func main() { migrateCmd, } + // Global flags + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "y, yes", + Usage: `Automatic yes to prompts. Assume "yes" as answer to all prompts and run non-interactively.`, + }, + } + sort.Sort(cli.FlagsByName(app.Flags)) _ = app.Run(os.Args) } @@ -144,13 +133,14 @@ func main() { //___________________________________ func printHeader(c *cli.Context) error { - hdrCont := fmt.Sprintf("aah framework v%s", aahVer) + aahVer, _ = aahVersion(c) + hdr := fmt.Sprintf("aah framework v%s", aahVer) improveRpt := "# Report improvements/bugs at https://aahframework.org/issues #" cnt := len(improveRpt) - sp := (cnt - len(hdrCont)) / 2 + sp := ((cnt - len(hdr)) / 2) - 1 fmt.Println(chr2str("-", cnt)) - fmt.Println(chr2str(" ", sp) + hdrCont) + fmt.Println(chr2str(" ", sp) + hdr) fmt.Println(chr2str("-", cnt)) fmt.Printf(improveRpt + "\n\n") diff --git a/aah/compile.go b/aah/compile.go index bfcf807..55ccc6f 100644 --- a/aah/compile.go +++ b/aah/compile.go @@ -5,7 +5,6 @@ package main import ( - "bufio" "bytes" "errors" "fmt" @@ -232,30 +231,12 @@ var notExistRegex = regexp.MustCompile(`cannot find package "(.*)" in any of`) // go list -f '{{ join .Imports "\n" }}' aah-app/import/path/app/... // func checkAndGetAppDeps(appImportPath string, cfg *config.Config) error { - importPath := path.Join(appImportPath, "app", "...") - args := []string{"list", "-f", "{{.Imports}}", importPath} - output, err := execCmd(gocmd, args, false) - if err != nil { - return err - } - - pkgList := make(map[string]string) - replacer := strings.NewReplacer("[", "", "]", "") - scanner := bufio.NewScanner(strings.NewReader(output)) - for scanner.Scan() { - if ln := replacer.Replace(strings.TrimSpace(scanner.Text())); ln != "" { - for _, p := range strings.Fields(ln) { - if p := strings.TrimSpace(p); p != "" { - pkgList[p] = p - } - } - } + debList := libDependencyImports(path.Join(appImportPath, "app", "...")) + if len(debList) == 0 { + return nil } - args = []string{"list"} - for _, p := range pkgList { - args = append(args, p) - } + args := append([]string{"list"}, debList...) b, _ := exec.Command(gocmd, args...).CombinedOutput() notExistsPkgs := []string{} matches := notExistRegex.FindAllStringSubmatch(string(b), -1) @@ -264,7 +245,8 @@ func checkAndGetAppDeps(appImportPath string, cfg *config.Config) error { } if cfg.BoolDefault("build.dep_get", true) && len(notExistsPkgs) > 0 { - cliLog.Info("Getting application dependencies ...", notExistsPkgs) + cliLog.Infof("Getting application dependencies ...\n---> %s", + strings.Join(notExistsPkgs, "\n---> ")) if err := goGet(notExistsPkgs...); err != nil { return err } diff --git a/aah/generate.go b/aah/generate.go index f744207..89a5497 100644 --- a/aah/generate.go +++ b/aah/generate.go @@ -105,7 +105,7 @@ func generateSystemdScript(c *cli.Context) error { fileName := fmt.Sprintf("%s.service", aah.AppName()) destFile := filepath.Join(aah.AppBaseDir(), fileName) - if checkAndConfirmOverwrite(destFile) { + if checkAndConfirmOverwrite(c, destFile) { return nil } @@ -143,13 +143,13 @@ func generateDockerScript(c *cli.Context) error { devFileName := "Dockerfile.dev" devDestFile := filepath.Join(aah.AppBaseDir(), devFileName) - if checkAndConfirmOverwrite(devDestFile) { + if checkAndConfirmOverwrite(c, devDestFile) { return nil } prodFileName := "Dockerfile.prod" prodDestFile := filepath.Join(aah.AppBaseDir(), prodFileName) - if checkAndConfirmOverwrite(prodDestFile) { + if checkAndConfirmOverwrite(c, prodDestFile) { return nil } @@ -198,12 +198,17 @@ func generateDockerScript(c *cli.Context) error { return nil } -func checkAndConfirmOverwrite(destFile string) bool { +func checkAndConfirmOverwrite(c *cli.Context, destFile string) bool { if ess.IsFileExists(destFile) { cliLog.Warnf("File: %s already exists, it will be overwritten.", destFile) + if c.GlobalBool("y") || c.GlobalBool("yes") { + fmt.Println("\nWould you like to continue? [y/N]: y") + return true + } + var input string for { - input = readInput(reader, "\nWould you like to continue [Y]es or [N]o, default is 'N'? ") + input = readInput(reader, "\nWould you like to continue? [y/N]: ") input = strings.ToLower(strings.TrimSpace(input)) if ess.IsStrEmpty(input) || input == "n" { // do not overwrite the file, abort diff --git a/aah/migrate.go b/aah/migrate.go index 7141d11..27a82fa 100644 --- a/aah/migrate.go +++ b/aah/migrate.go @@ -6,6 +6,7 @@ package main import ( "bytes" + "fmt" "go/format" "io/ioutil" "os" @@ -73,7 +74,9 @@ func migrateCodeAction(c *cli.Context) error { cliLog = initCLILogger(projectCfg) cliLog.Warn("Migrate command does not take file backup. It assumes application use version control.") - if !collectYesOrNo(reader, "Would you like to continue ([Y]es or [N]o)? default is 'N'") { + if c.GlobalBool("y") || c.GlobalBool("yes") { + fmt.Println("\nWould you like to continue? [y/N]: y") + } else if !collectYesOrNo(reader, "Would you like to continue? [y/N]") { cliLog.Info("Okay, I respect your choice. Bye.") return nil } diff --git a/aah/new.go b/aah/new.go index 0e0eeb0..8bad277 100644 --- a/aah/new.go +++ b/aah/new.go @@ -61,9 +61,9 @@ func newAction(c *cli.Context) error { switch appType { case typeWeb: - collectInputsForWebApp(app) + collectInputsForWebApp(c, app) case typeAPI: - collectInputsForAPIApp(app) + collectInputsForAPIApp(c, app) } // Process it @@ -143,7 +143,7 @@ func collectAppType(reader *bufio.Reader) string { // Collecting inputs for Web App //______________________________________________________________________________ -func collectInputsForWebApp(app *appTmplData) { +func collectInputsForWebApp(c *cli.Context, app *appTmplData) { viewEngine(reader, app) authScheme(reader, app) @@ -157,16 +157,16 @@ func collectInputsForWebApp(app *appTmplData) { sessionInfo(reader, app) // In the web application user may like to have API also WebSocket within it. - collectAppSubTypesChoice(reader, app) + collectAppSubTypesChoice(c, reader, app) - app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o)? default is 'N'") + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS? [y/N]") } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ // Collecting inputs for API App //______________________________________________________________________________ -func collectInputsForAPIApp(app *appTmplData) { +func collectInputsForAPIApp(c *cli.Context, app *appTmplData) { authScheme(reader, app) if app.AuthScheme == authBasic { @@ -175,20 +175,20 @@ func collectInputsForAPIApp(app *appTmplData) { passwordHashAlgorithm(reader, app) - app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS ([Y]es or [N]o)? default is 'N'") + app.CORSEnable = collectYesOrNo(reader, "Would you like to enable CORS? [y/N]") } -func collectAppSubTypesChoice(reader *bufio.Reader, app *appTmplData) { +func collectAppSubTypesChoice(c *cli.Context, reader *bufio.Reader, app *appTmplData) { app.SubTypes = make([]string, 0) // API choice - choice := collectYesOrNo(reader, "Would you like to add API [/api/v1/*] within your Web App ([Y]es or [N]o)? default is 'N'") + choice := collectYesOrNo(reader, "Would you like to add API (/api/v1/*) within your Web App? [y/N]") if choice { app.SubTypes = append(app.SubTypes, typeAPI) } // WebSocket choice - choice = collectYesOrNo(reader, "Would you like to add WebSocket [/ws/*] within your Web App ([Y]es or [N]o)? default is 'N'") + choice = collectYesOrNo(reader, "Would you like to add WebSocket (/ws/*) within your Web App? [y/N]") if choice { app.SubTypes = append(app.SubTypes, typeWebSocket) } diff --git a/aah/switch.go b/aah/switch.go index 7bcd7da..35d9038 100644 --- a/aah/switch.go +++ b/aah/switch.go @@ -67,7 +67,7 @@ func switchAction(c *cli.Context) error { return doRefresh(branchName) } - return doSwitch(branchName, strings.ToLower(firstNonEmpty(c.String("v"), c.String("version")))) + return doSwitch(c, branchName, strings.ToLower(firstNonEmpty(c.String("v"), c.String("version")))) } func whoami(branchName string) error { @@ -106,14 +106,20 @@ func doRefresh(branchName string) error { return nil } -func doSwitch(branchName, target string) error { +func doSwitch(c *cli.Context, branchName, target string) error { fname := friendlyName(branchName) if target == fname { cliLog.Infof("Currently you're on aah '%s' version.\n", fname) cliLog.Infof("To switch to release version. Run 'aah s -v release'\n") if fname == "edge" { - ans := collectYesOrNo(reader, "Would you like to refresh 'edge' to latest updates? ([Y]es or [N]o), default is 'N'") + var ans bool + if c.GlobalBool("y") || c.GlobalBool("yes") { + fmt.Println("\nWould you like to refresh 'edge' to latest updates? [y/N]: y") + ans = true + } else { + ans = collectYesOrNo(reader, "Would you like to refresh 'edge' to latest updates? [y/N]") + } fmt.Println() if ans { doRefresh(branchName) diff --git a/aah/util.go b/aah/util.go index 334bf2e..6591c22 100644 --- a/aah/util.go +++ b/aah/util.go @@ -5,6 +5,7 @@ package main import ( + "bufio" "fmt" "io" "io/ioutil" @@ -259,6 +260,10 @@ func initCLILogger(cfg *config.Config) *log.Logger { return l } +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Git methods +//___________________________________________________________________________ + func gitCheckout(dir, branch string) error { if ess.IsFileExists(filepath.Join(dir, ".git")) { _, err := execCmd(gitcmd, []string{"-C", dir, "checkout", branch}, false) @@ -267,15 +272,6 @@ func gitCheckout(dir, branch string) error { return nil } -func libImportPath(name string) string { - return fmt.Sprintf("%s/%s.%s", importPrefix, name, inferVersionSeries()) -} - -func libDir(name string) string { - importPath := libImportPath(name) - return filepath.FromSlash(filepath.Join(gopath, "src", importPath)) -} - func gitBranchName(dir string) string { if !ess.IsDir(dir) { cliLog.Tracef("Given path '%s' is not a directory", dir) @@ -299,6 +295,35 @@ func gitPull(dir string) error { return nil } +func checkoutBranch(aahLibDirs []string, branchName string) { + var wg sync.WaitGroup + for _, dir := range aahLibDirs { + wg.Add(1) + go func(d string) { + defer wg.Done() + baseName := filepath.Base(d) + if err := gitCheckout(d, branchName); err != nil { + logErrorf("Unable to switch library version, possibliy you may have local changes[%s]: %s", baseName, err) + } + cliLog.Tracef("Library '%s' have been switched to '%s' successfully", baseName, branchName) + }(dir) + } + wg.Wait() +} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// aah discovery and processing methods +//___________________________________________________________________________ + +func libImportPath(name string) string { + return fmt.Sprintf("%s/%s.%s", importPrefix, name, inferVersionSeries()) +} + +func libDir(name string) string { + importPath := libImportPath(name) + return filepath.FromSlash(filepath.Join(gosrcDir, importPath)) +} + func goGet(pkgs ...string) error { for _, pkg := range pkgs { if _, err := execCmd(gocmd, []string{"get", pkg}, false); err != nil { @@ -308,22 +333,6 @@ func goGet(pkgs ...string) error { return nil } -func waitForConnReady(port string) { - port = ":" + port - startTime := time.Now() - for { - if _, err := net.Dial("tcp", port); err != nil { - if time.Since(startTime).Seconds() > (30 * time.Second).Seconds() { - return - } - - time.Sleep(10 * time.Millisecond) - continue - } - return - } -} - func installCLI() { if CliPackaged != "" { return @@ -371,47 +380,6 @@ func refreshLibCode(libDirs []string) { wg.Wait() } -func appImportPath(c *cli.Context) string { - importPath := firstNonEmpty(c.String("i"), c.String("importpath")) - if ess.IsStrEmpty(importPath) { - importPath = importPathRelwd() - } - - if !ess.IsImportPathExists(importPath) { - logFatalf("Given import path '%s' does not exists", importPath) - } - - return importPath -} - -func logFatal(v ...interface{}) { - if cliLog == nil { - _ = log.SetPattern("%level %message") - fatal(v...) - _ = log.SetPattern(log.DefaultPattern) - } else { - cliLog.Fatal(append([]interface{}{"FATAL"}, v...)) - } -} - -func logFatalf(format string, v ...interface{}) { - if cliLog == nil { - _ = log.SetPattern("%level %message") - fatalf(format, v...) - _ = log.SetPattern(log.DefaultPattern) - } else { - cliLog.Fatalf("FATAL "+format, v...) - } -} - -func logError(v ...interface{}) { - cliLog.Error(append([]interface{}{"ERROR"}, v...)) -} - -func logErrorf(format string, v ...interface{}) { - cliLog.Errorf("ERROR "+format, v...) -} - func stripGoSrcPath(pkgFilePath string) string { idx := strings.Index(pkgFilePath, "src") return filepath.Clean(pkgFilePath[idx+4:]) @@ -449,33 +417,30 @@ func aahImportPaths() []string { return importPaths } -func checkoutBranch(aahLibDirs []string, branchName string) { - var wg sync.WaitGroup - for _, dir := range aahLibDirs { - wg.Add(1) - go func(d string) { - defer wg.Done() - baseName := filepath.Base(d) - if err := gitCheckout(d, branchName); err != nil { - logErrorf("Unable to switch library version, possibliy you may have local changes[%s]: %s", baseName, err) - } - cliLog.Tracef("Library '%s' have been switched to '%s' successfully", baseName, branchName) - }(dir) - } - wg.Wait() -} - func libDependencyImports(importPath string) []string { - var depList []string - str, err := execCmd(gocmd, []string{"list", "-f", "{{.Imports}}", importPath}, false) + args := []string{"list", "-f", "{{.Imports}}", importPath} + output, err := execCmd(gocmd, args, false) if err != nil { logErrorf("Unable to infer dependency imports for %s", importPath) return []string{} } - str = strings.TrimSpace(str) - for _, i := range strings.Fields(str[1 : len(str)-1]) { - depList = append(depList, strings.TrimSpace(i)) + pkgList := make(map[string]string) + replacer := strings.NewReplacer("[", "", "]", "") + scanner := bufio.NewScanner(strings.NewReader(output)) + for scanner.Scan() { + if ln := replacer.Replace(strings.TrimSpace(scanner.Text())); ln != "" { + for _, p := range strings.Fields(ln) { + if p := strings.TrimSpace(p); p != "" { + pkgList[p] = p + } + } + } + } + + var depList []string + for _, p := range pkgList { + depList = append(depList, p) } return depList @@ -510,6 +475,67 @@ func readVersionNo(baseDir string) (string, error) { return "Unknown", nil } +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// other helper methods +//___________________________________________________________________________ + +func appImportPath(c *cli.Context) string { + importPath := firstNonEmpty(c.String("i"), c.String("importpath")) + if ess.IsStrEmpty(importPath) { + importPath = importPathRelwd() + } + + if !ess.IsImportPathExists(importPath) { + logFatalf("Given import path '%s' does not exists", importPath) + } + + return importPath +} + +func logFatal(v ...interface{}) { + if cliLog == nil { + _ = log.SetPattern("%level %message") + fatal(v...) + _ = log.SetPattern(log.DefaultPattern) + } else { + cliLog.Fatal(append([]interface{}{"FATAL"}, v...)) + } +} + +func logFatalf(format string, v ...interface{}) { + if cliLog == nil { + _ = log.SetPattern("%level %message") + fatalf(format, v...) + _ = log.SetPattern(log.DefaultPattern) + } else { + cliLog.Fatalf("FATAL "+format, v...) + } +} + +func logError(v ...interface{}) { + cliLog.Error(append([]interface{}{"ERROR"}, v...)) +} + +func logErrorf(format string, v ...interface{}) { + cliLog.Errorf("ERROR "+format, v...) +} + +func waitForConnReady(port string) { + port = ":" + port + startTime := time.Now() + for { + if _, err := net.Dial("tcp", port); err != nil { + if time.Since(startTime).Seconds() > (30 * time.Second).Seconds() { + return + } + + time.Sleep(10 * time.Millisecond) + continue + } + return + } +} + func cleanupAutoGenFiles(appBaseDir string) { appMainGoFile := filepath.Join(appBaseDir, "app", "aah.go") appBuildDir := filepath.Join(appBaseDir, "build") diff --git a/aah/version.go b/aah/version.go index 585e5c8..62a4e5e 100644 --- a/aah/version.go +++ b/aah/version.go @@ -11,11 +11,13 @@ import ( "regexp" "strings" + "aahframework.org/essentials.v0" + "gopkg.in/urfave/cli.v1" ) // Version no. of aah framework CLI tool -const Version = "0.11.0" +const Version = "0.12.0" var ( errVersionNotExists = errors.New("version not exists") @@ -26,7 +28,10 @@ var ( func VersionPrinter(c *cli.Context) { cliLog = initCLILogger(nil) fmt.Printf("%-3s v%s\n", "cli", Version) - fmt.Printf("%-3s v%s\n", "aah", aahVer) + aahVer, _ = aahVersion(c) + if len(aahVer) > 0 { + fmt.Printf("%-3s v%s\n", "aah", aahVer) + } if goVer := goVersion(); len(goVer) > 0 { fmt.Printf("%-3s v%s\n", "go", goVer) } @@ -46,8 +51,21 @@ func VersionPrinter(c *cli.Context) { fmt.Println() } -func aahVersion() (string, error) { - return readVersionNo(libDir("aah")) +func aahVersion(c *cli.Context) (string, error) { + // Vendor Directory + importPath := importPathRelwd() + if len(importPath) > 0 { + vendorPath := filepath.Join(gosrcDir, importPath, "vendor") + if ess.IsFileExists(vendorPath) { + ver, _ := readVersionNo(filepath.Join(vendorPath, importPrefix, "aah.v0")) + if len(ver) > 0 && ver != "Unknown" { + return ver, nil + } + } + } + + // GOPATH + return readVersionNo(filepath.Join(gosrcDir, importPrefix, "aah.v0")) } func goVersion() string {