Skip to content

Commit

Permalink
Include all pre-requisites for go app run in app init (#288)
Browse files Browse the repository at this point in the history
* Include all pre-requisites for go app run in app init
  • Loading branch information
janelletavares authored Mar 29, 2022
1 parent a25a9b8 commit 00e64fd
Show file tree
Hide file tree
Showing 223 changed files with 87,592 additions and 33 deletions.
15 changes: 11 additions & 4 deletions cmd/meroxa/root/apps/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ type Init struct {
}

flags struct {
Lang string `long:"lang" short:"l" usage:"language to use (js|go)" required:"true"`
Path string `long:"path" usage:"path where application will be initialized (current directory as default)"`
Lang string `long:"lang" short:"l" usage:"language to use (js|go)" required:"true"`
Path string `long:"path" usage:"path where application will be initialized (current directory as default)"`
ModVendor bool `long:"mod-vendor" usage:"whether to download modules to vendor or globally while initializing a Go application"`
SkipModInit bool `long:"skip-mod-init" usage:"whether to run 'go mod init' while initializing a Go application"`
}
}

Expand All @@ -44,7 +46,9 @@ func (*Init) Docs() builder.Docs {
return builder.Docs{
Short: "Initialize a Meroxa Data Application",
Example: "meroxa apps init my-app --path ~/code --lang js" +
"meroxa apps init my-app --lang go # will be initialized in current directory",
"meroxa apps init my-app --lang go # will be initialized in current directory" +
"meroxa apps init my-app --lang go --skip-mod-init # will not initialize the new go module" +
"meroxa apps init my-app --lang go --mod-vendor # will initialize the new go module and download dependendies to the vendor directory",
}
}

Expand Down Expand Up @@ -76,7 +80,6 @@ func (i *Init) GitInit(ctx context.Context, path string) error {
i.logger.Error(ctx, string(output))
return err
}

return nil
}

Expand All @@ -94,6 +97,10 @@ func (i *Init) Execute(ctx context.Context) error {
switch lang {
case "go", GoLang:
err = turbine.Init(name, i.path)
if err != nil {
return err
}
err = turbineCLI.GoInit(ctx, i.logger, i.path+"/"+name, i.flags.SkipModInit, i.flags.ModVendor)
case "js", JavaScript, NodeJs:
err = turbinejs.Init(ctx, i.logger, name, i.path)
case "py", Python:
Expand Down
87 changes: 87 additions & 0 deletions cmd/meroxa/root/apps/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"os"
"path/filepath"
"testing"

"github.com/google/uuid"
Expand Down Expand Up @@ -107,3 +108,89 @@ func TestGitInit(t *testing.T) {

os.RemoveAll(testDir)
}

func TestGoInit(t *testing.T) {
gopath := os.Getenv("GOPATH")
tests := []struct {
desc string
path string
skipModInit bool
vendor bool
err error
}{
{
desc: "Init without go mod init",
path: func() string {
return filepath.Join(gopath, "src/github.com/meroxa/tests", uuid.New().String()) //nolint:gocritic
}(),
skipModInit: true,
vendor: false,
err: nil,
},
{
desc: "Init with go mod init and without vendoring",
path: func() string {
return filepath.Join(gopath, "src/github.com/meroxa/tests", uuid.New().String()) //nolint:gocritic
}(),
skipModInit: false,
vendor: false,
err: nil,
},
{
desc: "Init with go mod init and with vendoring",
path: func() string {
return filepath.Join(gopath, "src/github.com/meroxa/tests", uuid.New().String()) //nolint:gocritic
}(),
skipModInit: false,
vendor: true,
err: nil,
},
{
desc: "Init without go mod init and with vendor flag",
path: func() string {
return filepath.Join(gopath, "src/github.com/meroxa/tests", uuid.New().String()) //nolint:gocritic
}(),
skipModInit: true,
vendor: true,
err: nil,
},
}

for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
cc := &Init{}
cc.Logger(log.NewTestLogger())
cc.flags.Path = tt.path
cc.flags.Lang = "golang"
cc.flags.ModVendor = tt.vendor
cc.flags.SkipModInit = tt.skipModInit
cc.args.appName = "app-name"

err := cc.Execute(context.Background())
if err != nil {
if tt.err == nil {
t.Fatalf("unexpected error \"%s\"", err)
} else if tt.err.Error() != err.Error() {
t.Fatalf("expected \"%s\" got \"%s\"", tt.err, err)
}
}

if err == nil && tt.err == nil {
if !tt.skipModInit {
p, _ := filepath.Abs(tt.path + "/" + cc.args.appName + "/go.mod")
if _, err := os.Stat(p); os.IsNotExist(err) {
t.Fatalf("expected file \"%s\" to be created", p)
}

if tt.vendor {
p, _ = filepath.Abs(tt.path + "/" + cc.args.appName + "/go.mod")
if _, err := os.Stat(p); os.IsNotExist(err) {
t.Fatalf("expected directory \"%s\" to be created", p)
}
}
}
}
os.RemoveAll(tt.path)
})
}
}
12 changes: 7 additions & 5 deletions cmd/meroxa/turbine_cli/golang/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"os/exec"
"path"

turbinecli "github.com/meroxa/cli/cmd/meroxa/turbine_cli"

"github.com/meroxa/cli/log"
)

Expand All @@ -28,7 +26,11 @@ func Run(ctx context.Context, appPath string, l log.Logger) error {
}

cmd := exec.Command("./" + appName) //nolint:gosec

_, err = turbinecli.RunCmdWithErrorDetection(ctx, cmd, l)
return err
output, err := cmd.CombinedOutput()
if err != nil {
l.Error(ctx, string(output))
return err
}
l.Info(ctx, string(output))
return nil
}
142 changes: 124 additions & 18 deletions cmd/meroxa/turbine_cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,24 @@ import (
)

type AppConfig struct {
Name string `json:"name"`
Language string `json:"language"`
Name string `json:"name"`
Environment string `json:"environment"`
Language string `json:"language"`
Resources map[string]string `json:"resources"`
Vendor string `json:"vendor"`
ModuleInit string `json:"module_init"`
}

var prefetched *AppConfig

func GetPath(flag string) (string, error) {
if flag == "." || flag == "" {
var err error
flag, err = filepath.Abs(".")
if err != nil {
return "", err
}
if flag == "" {
flag = "."
}
var err error
flag, err = filepath.Abs(flag)
if err != nil {
return "", err
}
return flag, nil
}
Expand Down Expand Up @@ -71,21 +78,66 @@ func GetAppNameFromAppJSON(pwd string) (string, error) {
return appConfig.Name, nil
}

// SetModuleInitInAppJSON returns whether to run mod init.
func SetModuleInitInAppJSON(pwd string, skipInit bool) error {
appConfig, err := readConfigFile(pwd)
if err != nil {
return err
}
appConfig.ModuleInit = "true"
if skipInit {
appConfig.ModuleInit = "false"
}
err = writeConfigFile(pwd, appConfig) // will never be programmatically read again, but a marker of what turbine did
return err
}

// SetVendorInAppJSON returns whether to vendor modules.
func SetVendorInAppJSON(pwd string, vendor bool) error {
appConfig, err := readConfigFile(pwd)
if err != nil {
return err
}
appConfig.Vendor = "false"
if vendor {
appConfig.Vendor = "true"
}
err = writeConfigFile(pwd, appConfig) // will never be programmatically read again, but a marker of what turbine did
return err
}

// readConfigFile will read the content of an app.json based on path.
func readConfigFile(appPath string) (AppConfig, error) {
var appConfig AppConfig

if prefetched == nil {
appConfigPath := path.Join(appPath, "app.json")
appConfigBytes, err := os.ReadFile(appConfigPath)
if err != nil {
return appConfig, fmt.Errorf("%v\n"+
"We couldn't find an app.json file on path %q. Maybe try using a different value for `--path`", err, appPath)
}
if err := json.Unmarshal(appConfigBytes, &appConfig); err != nil {
return appConfig, err
}
prefetched = &appConfig
}

return *prefetched, nil
}

func writeConfigFile(appPath string, cfg AppConfig) error {
appConfigPath := path.Join(appPath, "app.json")
appConfigBytes, err := os.ReadFile(appConfigPath)
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return appConfig, fmt.Errorf("%v\n"+
"We couldn't find an app.json file on path %q. Maybe try in another using `--path`", err, appPath)
return err
}
if err := json.Unmarshal(appConfigBytes, &appConfig); err != nil {
return appConfig, err
err = os.WriteFile(appConfigPath, data, 0664) //nolint:gosec,gomnd
if err != nil {
return fmt.Errorf("%v\n"+
"unable to update app.json file on path %q. Maybe try using a different value for `--path`", err, appPath)
}

return appConfig, nil
return nil
}

// GitChecks prints warnings about uncommitted tracked and untracked files.
Expand Down Expand Up @@ -177,6 +229,62 @@ func GetGitSha(appPath string) (string, error) {
return string(output), nil
}

func GoInit(ctx context.Context, l log.Logger, appPath string, skipInit, vendor bool) error {
// temporarily switching to the app's directory
pwd, err := switchToAppDirectory(appPath)
if err != nil {
return err
}

// initialize the user's module
err = SetModuleInitInAppJSON(appPath, skipInit)
if err != nil {
return err
}
if !skipInit {
l.Info(ctx, "Initializing the application's go module...")
cmd := exec.Command("go", "mod", "init")
output, err := cmd.CombinedOutput()
if err != nil {
l.Error(ctx, string(output))
return err
}
cmd = exec.Command("go", "get", "github.com/meroxa/turbine")
output, err = cmd.CombinedOutput()
if err != nil {
l.Error(ctx, string(output))
return err
}
cmd = exec.Command("go", "get", "github.com/meroxa/turbine/runner")
output, err = cmd.CombinedOutput()
if err != nil {
l.Error(ctx, string(output))
return err
}

// download dependencies
err = SetVendorInAppJSON(appPath, vendor)
if err != nil {
return err
}
depsLog := "Downloading dependencies "
cmd = exec.Command("go", "mod", "download")
if vendor {
depsLog += "to vendor"
cmd = exec.Command("go", "mod", "vendor")
}
depsLog += "..."
l.Info(ctx, depsLog)
output, err = cmd.CombinedOutput()
if err != nil {
l.Error(ctx, string(output))
return err
}
}

return os.Chdir(pwd)
}

// switchToAppDirectory switches temporarily to the application's directory.
func switchToAppDirectory(appPath string) (string, error) {
pwd, err := os.Getwd()
Expand All @@ -194,9 +302,7 @@ func RunCmdWithErrorDetection(ctx context.Context, cmd *exec.Cmd, l log.Logger)
err := cmd.Run()
successMsg := stdout.String()
errMsg := stderr.String()
if err != nil {
return "", err
} else if errMsg != "" {
if err != nil || errMsg != "" {
return "", errors.New(successMsg + errMsg)
}
l.Info(ctx, successMsg)
Expand Down
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
require (
github.com/docker/docker v20.10.12+incompatible
github.com/mattn/go-shellwords v1.0.12
github.com/meroxa/turbine v0.0.0-20220315122820-92658c705129
github.com/meroxa/turbine v0.0.0-20220329164249-53623bb694e6
github.com/volatiletech/null/v8 v8.1.2
)

Expand All @@ -36,6 +36,7 @@ require github.com/cristalhq/jwt/v3 v3.1.0 // indirect
require (
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/caarlos0/env/v6 v6.7.2 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/containerd/cgroups v1.0.1 // indirect
github.com/containerd/containerd v1.5.9 // indirect
Expand All @@ -57,9 +58,11 @@ require (
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/meroxa/funtime v0.0.0-20220113012133-85e6e898fc73 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/moby/sys/mount v0.3.0 // indirect
github.com/moby/sys/mountinfo v0.5.0 // indirect
github.com/oklog/run v1.1.1-0.20200508094559-c7096881717e // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.1.0 // indirect
Expand All @@ -72,6 +75,10 @@ require (
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/gjson v1.14.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.4 // indirect
github.com/volatiletech/inflect v0.0.1 // indirect
github.com/volatiletech/randomize v0.0.1 // indirect
github.com/volatiletech/strmangle v0.0.2 // indirect
Expand Down
Loading

0 comments on commit 00e64fd

Please sign in to comment.