github.com/pojntfx/hydrapp/hydrapp@v0.0.0-20240516002902-d08759d6ca9f/cmd/new.go (about)

     1  package cmd
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"net/http"
     9  	"os"
    10  	"os/exec"
    11  	"path"
    12  	"path/filepath"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/manifoldco/promptui"
    17  	"github.com/pojntfx/hydrapp/hydrapp/pkg/config"
    18  	"github.com/pojntfx/hydrapp/hydrapp/pkg/generators"
    19  	"github.com/pojntfx/hydrapp/hydrapp/pkg/renderers"
    20  	"github.com/pojntfx/hydrapp/hydrapp/pkg/renderers/rpm"
    21  	"github.com/pojntfx/hydrapp/hydrapp/pkg/utils"
    22  	"github.com/spf13/cobra"
    23  	"github.com/spf13/viper"
    24  	"gopkg.in/yaml.v2"
    25  )
    26  
    27  const (
    28  	noNetworkFlag = "no-network"
    29  
    30  	vanillaJSRESTKey  = "vanillajs-rest"
    31  	vanillaJSFormsKey = "vanillajs-forms"
    32  	reactPanrpcKey    = "react-panrpc"
    33  )
    34  
    35  var (
    36  	errUnknownProjectType = errors.New("unknown project type")
    37  
    38  	projectTypeItems = []generators.ProjectTypeOption{
    39  		{
    40  			Name:        vanillaJSRESTKey,
    41  			Description: "Simple starter project with a REST API to connect the Vanilla JS frontend and backend",
    42  		},
    43  		{
    44  			Name:        vanillaJSFormsKey,
    45  			Description: "Traditional starter project with Web 1.0-style forms to connect the Vanilla JS frontend and backend",
    46  		},
    47  		{
    48  			Name:        reactPanrpcKey,
    49  			Description: "Complete starter project with panrpc RPCs to connect the React frontend and backend",
    50  		},
    51  	}
    52  )
    53  
    54  var newCmd = &cobra.Command{
    55  	Use:     "new",
    56  	Aliases: []string{"n"},
    57  	Short:   "Generate a new hydrapp project",
    58  	RunE: func(cmd *cobra.Command, args []string) error {
    59  		if err := viper.BindPFlags(cmd.PersistentFlags()); err != nil {
    60  			return err
    61  		}
    62  
    63  		projectTypeIndex, _, err := (&promptui.Select{
    64  			Templates: &promptui.SelectTemplates{
    65  				Label:    fmt.Sprintf("%s {{.}}: ", promptui.IconInitial),
    66  				Active:   fmt.Sprintf("%s {{ .Name | underline }}: {{ .Description | faint }}", promptui.IconSelect),
    67  				Inactive: "  {{ .Name }}: {{ .Description | faint }}",
    68  				Selected: fmt.Sprintf(`{{ "%s" | green }} {{ .Name | faint }}: {{ .Description | faint }}`, promptui.IconGood),
    69  			},
    70  			Label: "Which project type do you want to generate?",
    71  			Items: projectTypeItems,
    72  		}).Run()
    73  		if err != nil {
    74  			return err
    75  		}
    76  
    77  		appID, err := (&promptui.Prompt{
    78  			Label:   "App ID in reverse domain notation",
    79  			Default: "com.github.example.myapp",
    80  		}).Run()
    81  		if err != nil {
    82  			return err
    83  		}
    84  
    85  		appName, err := (&promptui.Prompt{
    86  			Label:   "App name",
    87  			Default: "My App",
    88  		}).Run()
    89  		if err != nil {
    90  			return err
    91  		}
    92  
    93  		appSummary, err := (&promptui.Prompt{
    94  			Label:   "App summary",
    95  			Default: "My first app",
    96  		}).Run()
    97  		if err != nil {
    98  			return err
    99  		}
   100  
   101  		appDescription, err := (&promptui.Prompt{
   102  			Label:   "App description",
   103  			Default: "My first application, built with hydrapp.",
   104  		}).Run()
   105  		if err != nil {
   106  			return err
   107  		}
   108  
   109  		appHomepage, err := (&promptui.Prompt{
   110  			Label:   "App homepage",
   111  			Default: "https://github.com/example/myapp",
   112  		}).Run()
   113  		if err != nil {
   114  			return err
   115  		}
   116  
   117  		appGit, err := (&promptui.Prompt{
   118  			Label:   "App git repo",
   119  			Default: appHomepage + ".git",
   120  		}).Run()
   121  		if err != nil {
   122  			return err
   123  		}
   124  
   125  		appBaseurl, err := (&promptui.Prompt{
   126  			Label:   "App base URL to expect the built assets to be published to",
   127  			Default: "https://example.github.io/myapp/",
   128  		}).Run()
   129  		if err != nil {
   130  			return err
   131  		}
   132  
   133  		goMod, err := (&promptui.Prompt{
   134  			Label:   "Go module name",
   135  			Default: "github.com/example/myapp",
   136  		}).Run()
   137  		if err != nil {
   138  			return err
   139  		}
   140  
   141  		licenseSPDX, err := (&promptui.Prompt{
   142  			Label:   "License SPDX identifier (see https://spdx.org/licenses/)",
   143  			Default: "Apache-2.0",
   144  		}).Run()
   145  		if err != nil {
   146  			return err
   147  		}
   148  
   149  		releaseAuthor, err := (&promptui.Prompt{
   150  			Label:   "Release author name",
   151  			Default: "Jean Doe",
   152  		}).Run()
   153  		if err != nil {
   154  			return err
   155  		}
   156  
   157  		releaseEmail, err := (&promptui.Prompt{
   158  			Label:   "Release author email",
   159  			Default: "jean.doe@example.com",
   160  		}).Run()
   161  		if err != nil {
   162  			return err
   163  		}
   164  
   165  		dir, err := (&promptui.Prompt{
   166  			Label:   "Directory to write the app to",
   167  			Default: "myapp",
   168  		}).Run()
   169  		if err != nil {
   170  			return err
   171  		}
   172  
   173  		_, advancedConfiguration, err := (&promptui.Select{
   174  			Label: "Do you want to do any advanced configuration?",
   175  			Items: []string{"no", "yes"},
   176  		}).Run()
   177  		if err != nil {
   178  			return err
   179  		}
   180  
   181  		goMain := "."
   182  		goFlags := ""
   183  		goGenerate := "go generate ./..."
   184  		goTests := "go test ./..."
   185  		goImg := "ghcr.io/pojntfx/hydrapp-build-tests:main"
   186  
   187  		debArchitectures := "amd64"
   188  		flatpakArchitectures := "amd64"
   189  		msiArchitectures := "amd64"
   190  		rpmArchitectures := "amd64"
   191  
   192  		binariesExclude := "(android/*|ios/*|plan9/*|aix/*|linux/loong64|freebsd/riscv64|wasip1/wasm|js/wasm|openbsd/mips64)"
   193  
   194  		if advancedConfiguration == "yes" {
   195  			goMain, err = (&promptui.Prompt{
   196  				Label:   "Go main package path",
   197  				Default: goMain,
   198  			}).Run()
   199  			if err != nil {
   200  				return err
   201  			}
   202  
   203  			goFlags, err = (&promptui.Prompt{
   204  				Label:   "Go flags to pass to the compiler",
   205  				Default: goFlags,
   206  			}).Run()
   207  			if err != nil {
   208  				return err
   209  			}
   210  
   211  			goGenerate, err = (&promptui.Prompt{
   212  				Label:   "Go generate command to run",
   213  				Default: goGenerate,
   214  			}).Run()
   215  			if err != nil {
   216  				return err
   217  			}
   218  
   219  			goTests, err = (&promptui.Prompt{
   220  				Label:   "Go test command to run",
   221  				Default: goTests,
   222  			}).Run()
   223  			if err != nil {
   224  				return err
   225  			}
   226  
   227  			goImg, err = (&promptui.Prompt{
   228  				Label:   "Go test OCI image to use",
   229  				Default: goImg,
   230  			}).Run()
   231  			if err != nil {
   232  				return err
   233  			}
   234  
   235  			debArchitectures, err = (&promptui.Prompt{
   236  				Label:   "DEB architectures to build for (comma-seperated list of GOARCH values)",
   237  				Default: debArchitectures,
   238  			}).Run()
   239  			if err != nil {
   240  				return err
   241  			}
   242  
   243  			flatpakArchitectures, err = (&promptui.Prompt{
   244  				Label:   "Flatpak architectures to build for (comma-seperated list of GOARCH values)",
   245  				Default: flatpakArchitectures,
   246  			}).Run()
   247  			if err != nil {
   248  				return err
   249  			}
   250  
   251  			msiArchitectures, err = (&promptui.Prompt{
   252  				Label:   "MSI architectures to build for (comma-seperated list of GOARCH values)",
   253  				Default: msiArchitectures,
   254  			}).Run()
   255  			if err != nil {
   256  				return err
   257  			}
   258  
   259  			rpmArchitectures, err = (&promptui.Prompt{
   260  				Label:   "RPM architectures to build for (comma-seperated list of GOARCH values)",
   261  				Default: rpmArchitectures,
   262  			}).Run()
   263  			if err != nil {
   264  				return err
   265  			}
   266  
   267  			binariesExclude, err = (&promptui.Prompt{
   268  				Label:   "Regex of binaries to exclude from compilation",
   269  				Default: binariesExclude,
   270  			}).Run()
   271  			if err != nil {
   272  				return err
   273  			}
   274  		}
   275  
   276  		licenseText := ""
   277  		if !viper.GetBool(noNetworkFlag) {
   278  			log.Println("Fetching full license text from SPDX ...")
   279  
   280  			res, err := http.Get("https://raw.githubusercontent.com/spdx/license-list-data/main/text/" + licenseSPDX + ".txt")
   281  			if err != nil {
   282  				return err
   283  			}
   284  			if res.Body != nil {
   285  				defer res.Body.Close()
   286  			}
   287  			if res.StatusCode != http.StatusOK {
   288  				panic(errors.New(res.Status))
   289  			}
   290  
   291  			b, err := ioutil.ReadAll(res.Body)
   292  			if err != nil {
   293  				return err
   294  			}
   295  
   296  			licenseText = string(b)
   297  
   298  			log.Println("Success!")
   299  		}
   300  
   301  		{
   302  			cfg := config.Root{}
   303  			cfg.App = config.App{
   304  				ID:          appID,
   305  				Name:        appName,
   306  				Summary:     appSummary,
   307  				Description: appDescription,
   308  				License:     licenseSPDX,
   309  				Homepage:    appHomepage,
   310  				Git:         appGit,
   311  				BaseURL:     appBaseurl,
   312  			}
   313  			cfg.Go = config.Go{
   314  				Main:     goMain,
   315  				Flags:    goFlags,
   316  				Generate: goGenerate,
   317  				Tests:    goTests,
   318  				Image:    goImg,
   319  			}
   320  			cfg.Releases = []renderers.Release{
   321  				{
   322  					Version:     "0.0.1",
   323  					Date:        time.Now(),
   324  					Description: "Initial release",
   325  					Author:      releaseAuthor,
   326  					Email:       releaseEmail,
   327  				},
   328  			}
   329  
   330  			cfg.APK = config.APK{
   331  				Path: "apk",
   332  			}
   333  
   334  			debs := []config.DEB{}
   335  			for _, arch := range strings.Split(debArchitectures, ",") {
   336  				debs = append(debs, config.DEB{
   337  					Path:            path.Join("deb", "debian", "sid", utils.GetArchIdentifier(arch)),
   338  					OS:              "debian",
   339  					Distro:          "sid",
   340  					Mirrorsite:      "http://http.us.debian.org/debian",
   341  					Components:      []string{"main", "contrib"},
   342  					Debootstrapopts: "",
   343  					Architecture:    arch,
   344  					Packages:        []rpm.Package{},
   345  				})
   346  			}
   347  			cfg.DEB = debs
   348  
   349  			cfg.DMG = config.DMG{
   350  				Path:     "dmg",
   351  				Packages: []string{},
   352  			}
   353  
   354  			flatpaks := []config.Flatpak{}
   355  			for _, arch := range strings.Split(flatpakArchitectures, ",") {
   356  				flatpaks = append(flatpaks, config.Flatpak{
   357  					Path:         path.Join("flatpak", utils.GetArchIdentifier(arch)),
   358  					Architecture: arch,
   359  				})
   360  			}
   361  			cfg.Flatpak = flatpaks
   362  
   363  			msis := []config.MSI{}
   364  			for _, arch := range strings.Split(msiArchitectures, ",") {
   365  				msis = append(msis, config.MSI{
   366  					Path:         path.Join("msi", utils.GetArchIdentifier(arch)),
   367  					Architecture: arch,
   368  					Include:      `^\\b$`,
   369  					Packages:     []string{},
   370  				})
   371  			}
   372  			cfg.MSI = msis
   373  
   374  			rpms := []config.RPM{}
   375  			for _, arch := range strings.Split(rpmArchitectures, ",") {
   376  				rpms = append(rpms, config.RPM{
   377  					Path:         path.Join("rpm", "fedora", "40", utils.GetArchIdentifier(arch)),
   378  					Trailer:      "fc40",
   379  					Distro:       "fedora-40",
   380  					Architecture: arch,
   381  					Packages:     []rpm.Package{},
   382  				})
   383  			}
   384  			cfg.RPM = rpms
   385  
   386  			cfg.Binaries = config.Binaries{
   387  				Path:     "binaries",
   388  				Exclude:  binariesExclude,
   389  				Packages: []string{},
   390  			}
   391  			cfg.Docs = config.Docs{
   392  				Path: "docs",
   393  			}
   394  
   395  			b, err := yaml.Marshal(cfg)
   396  			if err != nil {
   397  				return err
   398  			}
   399  
   400  			if err := os.MkdirAll(dir, 0755); err != nil {
   401  				return err
   402  			}
   403  
   404  			if err := ioutil.WriteFile(filepath.Join(dir, "hydrapp.yaml"), b, 0664); err != nil {
   405  				return err
   406  			}
   407  		}
   408  
   409  		if err := ioutil.WriteFile(filepath.Join(dir, "icon.png"), generators.IconTpl, 0664); err != nil {
   410  			return err
   411  		}
   412  
   413  		if err := generators.RenderTemplate(
   414  			filepath.Join(dir, "go.mod"),
   415  			generators.GoModTpl,
   416  			generators.GoModData{
   417  				GoMod: goMod,
   418  			},
   419  		); err != nil {
   420  			return err
   421  		}
   422  
   423  		switch projectTypeItems[projectTypeIndex].Name {
   424  		case vanillaJSRESTKey:
   425  			if err := generators.RenderTemplate(
   426  				filepath.Join(dir, "main.go"),
   427  				generators.GoMainVanillaJSRESTTpl,
   428  				generators.GoMainData{
   429  					GoMod: goMod,
   430  				},
   431  			); err != nil {
   432  				return err
   433  			}
   434  
   435  			if err := generators.RenderTemplate(
   436  				filepath.Join(dir, "android.go"),
   437  				generators.AndroidVanillaJSRESTTpl,
   438  				generators.AndroidData{
   439  					GoMod:     goMod,
   440  					JNIExport: strings.Replace(appID, ".", "_", -1),
   441  				},
   442  			); err != nil {
   443  				return err
   444  			}
   445  
   446  			if err := generators.RenderTemplate(
   447  				filepath.Join(dir, ".gitignore"),
   448  				generators.GitignoreVanillaJSRESTTpl,
   449  				nil,
   450  			); err != nil {
   451  				return err
   452  			}
   453  
   454  			if err := generators.RenderTemplate(
   455  				filepath.Join(dir, "pkg", "backend", "server.go"),
   456  				generators.BackendVanillaJSRESTTpl,
   457  				nil,
   458  			); err != nil {
   459  				return err
   460  			}
   461  
   462  			if err := generators.RenderTemplate(
   463  				filepath.Join(dir, "pkg", "frontend", "server.go"),
   464  				generators.FrontendVanillaJSRESTTpl,
   465  				nil,
   466  			); err != nil {
   467  				return err
   468  			}
   469  
   470  			if err := generators.RenderTemplate(
   471  				filepath.Join(dir, "pkg", "frontend", "index.html"),
   472  				generators.IndexHTMLVanillaJSRESTTpl,
   473  				generators.IndexHTMLData{
   474  					AppName: appName,
   475  				},
   476  			); err != nil {
   477  				return err
   478  			}
   479  
   480  			if err := generators.RenderTemplate(
   481  				filepath.Join(dir, "README.md"),
   482  				generators.ReadmeMDVanillaJSRESTTpl,
   483  				generators.ReadmeMDData{
   484  					AppName:        appName,
   485  					AppSummary:     appSummary,
   486  					AppGitWeb:      strings.TrimSuffix(appGit, ".git"),
   487  					AppDescription: appDescription,
   488  					AppBaseURL:     appBaseurl,
   489  					AppGit:         appGit,
   490  					CurrentYear:    time.Now().Format("2006"),
   491  					ReleaseAuthor:  releaseAuthor,
   492  					LicenseSPDX:    licenseSPDX,
   493  					Dir:            dir,
   494  				},
   495  			); err != nil {
   496  				return err
   497  			}
   498  		case vanillaJSFormsKey:
   499  			if err := generators.RenderTemplate(
   500  				filepath.Join(dir, "main.go"),
   501  				generators.GoMainVanillaJSFormsTpl,
   502  				generators.GoMainData{
   503  					GoMod: goMod,
   504  				},
   505  			); err != nil {
   506  				return err
   507  			}
   508  
   509  			if err := generators.RenderTemplate(
   510  				filepath.Join(dir, "android.go"),
   511  				generators.AndroidVanillaJSFormsTpl,
   512  				generators.AndroidData{
   513  					GoMod:     goMod,
   514  					JNIExport: strings.Replace(appID, ".", "_", -1),
   515  				},
   516  			); err != nil {
   517  				return err
   518  			}
   519  
   520  			if err := generators.RenderTemplate(
   521  				filepath.Join(dir, "pkg", "frontend", "server.go"),
   522  				generators.FrontendVanillaJSFormsTpl,
   523  				nil,
   524  			); err != nil {
   525  				return err
   526  			}
   527  
   528  			if err := generators.RenderTemplate(
   529  				filepath.Join(dir, "pkg", "frontend", "index.html"),
   530  				generators.IndexHTMLVanillaJSFormsTpl,
   531  				generators.IndexHTMLData{
   532  					AppName: appName,
   533  				},
   534  			); err != nil {
   535  				return err
   536  			}
   537  
   538  			if err := generators.RenderTemplate(
   539  				filepath.Join(dir, "README.md"),
   540  				generators.ReadmeMDVanillaJSRESTTpl,
   541  				generators.ReadmeMDData{
   542  					AppName:        appName,
   543  					AppSummary:     appSummary,
   544  					AppGitWeb:      strings.TrimSuffix(appGit, ".git"),
   545  					AppDescription: appDescription,
   546  					AppBaseURL:     appBaseurl,
   547  					AppGit:         appGit,
   548  					CurrentYear:    time.Now().Format("2006"),
   549  					ReleaseAuthor:  releaseAuthor,
   550  					LicenseSPDX:    licenseSPDX,
   551  					Dir:            dir,
   552  				},
   553  			); err != nil {
   554  				return err
   555  			}
   556  		case reactPanrpcKey:
   557  			if err := generators.RenderTemplate(
   558  				filepath.Join(dir, "main.go"),
   559  				generators.GoMainReactPanrpcTpl,
   560  				generators.GoMainData{
   561  					GoMod: goMod,
   562  				},
   563  			); err != nil {
   564  				return err
   565  			}
   566  
   567  			if err := generators.RenderTemplate(
   568  				filepath.Join(dir, "android.go"),
   569  				generators.AndroidReactPanrpcTpl,
   570  				generators.AndroidData{
   571  					GoMod:     goMod,
   572  					JNIExport: strings.Replace(appID, ".", "_", -1),
   573  				},
   574  			); err != nil {
   575  				return err
   576  			}
   577  
   578  			if err := generators.RenderTemplate(
   579  				filepath.Join(dir, ".gitignore"),
   580  				generators.GitignoreReactPanrpcTpl,
   581  				nil,
   582  			); err != nil {
   583  				return err
   584  			}
   585  
   586  			if err := generators.RenderTemplate(
   587  				filepath.Join(dir, "pkg", "backend", "server.go"),
   588  				generators.BackendReactPanrpcTpl,
   589  				nil,
   590  			); err != nil {
   591  				return err
   592  			}
   593  
   594  			if err := generators.RenderTemplate(
   595  				filepath.Join(dir, "pkg", "frontend", "server.go"),
   596  				generators.FrontendReactPanrpcTpl,
   597  				nil,
   598  			); err != nil {
   599  				return err
   600  			}
   601  
   602  			if err := generators.RenderTemplate(
   603  				filepath.Join(dir, "pkg", "frontend", "src", "App.tsx"),
   604  				generators.AppTSXTpl,
   605  				generators.AppTSXData{
   606  					AppName: appName,
   607  				},
   608  			); err != nil {
   609  				return err
   610  			}
   611  
   612  			if err := generators.RenderTemplate(
   613  				filepath.Join(dir, "pkg", "frontend", "src", "main.tsx"),
   614  				generators.MainTSXTpl,
   615  				nil,
   616  			); err != nil {
   617  				return err
   618  			}
   619  
   620  			if err := generators.RenderTemplate(
   621  				filepath.Join(dir, "pkg", "frontend", "index.html"),
   622  				generators.IndexHTMLReactPanrpcTpl,
   623  				generators.IndexHTMLData{
   624  					AppName: appName,
   625  				},
   626  			); err != nil {
   627  				return err
   628  			}
   629  
   630  			if err := generators.RenderTemplate(
   631  				filepath.Join(dir, "pkg", "frontend", "package.json"),
   632  				generators.PackageJSONReactPanrpcTpl,
   633  				generators.PackageJSONData{
   634  					AppID:          appID,
   635  					AppDescription: appDescription,
   636  					ReleaseAuthor:  releaseAuthor,
   637  					ReleaseEmail:   releaseEmail,
   638  					LicenseSPDX:    licenseSPDX,
   639  				},
   640  			); err != nil {
   641  				return err
   642  			}
   643  
   644  			if err := generators.RenderTemplate(
   645  				filepath.Join(dir, "pkg", "frontend", "tsconfig.json"),
   646  				generators.TsconfigJSONTpl,
   647  				nil,
   648  			); err != nil {
   649  				return err
   650  			}
   651  
   652  			if err := generators.RenderTemplate(
   653  				filepath.Join(dir, "README.md"),
   654  				generators.ReadmeMDReactPanrpcTpl,
   655  				generators.ReadmeMDData{
   656  					AppName:        appName,
   657  					AppSummary:     appSummary,
   658  					AppGitWeb:      strings.TrimSuffix(appGit, ".git"),
   659  					AppDescription: appDescription,
   660  					AppBaseURL:     appBaseurl,
   661  					AppGit:         appGit,
   662  					CurrentYear:    time.Now().Format("2006"),
   663  					ReleaseAuthor:  releaseAuthor,
   664  					LicenseSPDX:    licenseSPDX,
   665  					Dir:            dir,
   666  				},
   667  			); err != nil {
   668  				return err
   669  			}
   670  		default:
   671  			panic(errUnknownProjectType)
   672  		}
   673  
   674  		if err := generators.RenderTemplate(
   675  			filepath.Join(dir, "LICENSE"),
   676  			licenseText,
   677  			nil,
   678  		); err != nil {
   679  			return err
   680  		}
   681  
   682  		if err := generators.RenderTemplate(
   683  			filepath.Join(dir, "CODE_OF_CONDUCT.md"),
   684  			generators.CodeOfConductMDTpl,
   685  			generators.CodeOfConductMDData{
   686  				ReleaseEmail: releaseEmail,
   687  			},
   688  		); err != nil {
   689  			return err
   690  		}
   691  
   692  		if err := generators.RenderTemplate(
   693  			filepath.Join(dir, ".github", "workflows", "hydrapp.yaml"),
   694  			generators.HydrappYAMLTpl,
   695  			generators.HydrappYAMLData{
   696  				AppID: appID,
   697  			},
   698  		); err != nil {
   699  			return err
   700  		}
   701  
   702  		if !viper.GetBool(noNetworkFlag) {
   703  			{
   704  				log.Println("Downloading Go dependencies ...")
   705  
   706  				cmd := exec.Command("go", "get", "-x", "./...")
   707  				cmd.Dir = dir
   708  				cmd.Stdin = os.Stdin
   709  				cmd.Stdout = os.Stdout
   710  				cmd.Stderr = os.Stderr
   711  
   712  				if err := cmd.Run(); err != nil {
   713  					return err
   714  				}
   715  
   716  				log.Println("Success!")
   717  			}
   718  
   719  			{
   720  				log.Println("Generating Go dependencies ...")
   721  
   722  				cmd := exec.Command("go", "generate", "-x", "./...")
   723  				cmd.Dir = dir
   724  				cmd.Stdin = os.Stdin
   725  				cmd.Stdout = os.Stdout
   726  				cmd.Stderr = os.Stderr
   727  
   728  				if err := cmd.Run(); err != nil {
   729  					return err
   730  				}
   731  
   732  				log.Println("Success!")
   733  			}
   734  		}
   735  
   736  		{
   737  			fmt.Printf(`Succesfully generated application. To start it, run the following:
   738  
   739  cd %v
   740  go run %v
   741  
   742  You can find more information in the generated README.
   743  `, dir, goMain)
   744  		}
   745  
   746  		return nil
   747  	},
   748  }
   749  
   750  func init() {
   751  	newCmd.PersistentFlags().Bool(noNetworkFlag, false, "Disable all network interaction")
   752  
   753  	viper.AutomaticEnv()
   754  
   755  	rootCmd.AddCommand(newCmd)
   756  }