diff --git a/CHANGELOG.md b/CHANGELOG.md index 88b4e3b12..b54a62aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,9 @@ CHANGELOG - Add additional overloads to Deployment.RunAsync in .NET API. [#4286](https://github.com/pulumi/pulumi/pull/4286) +- Automate execution of `go mod download` for `pulumi new` Go templates + [#4353](https://github.com/pulumi/pulumi/pull/4353) + ## 1.14.0 (2020-04-01) - Fix error related to side-by-side versions of `@pulumi/pulumi`. [#4235](https://github.com/pulumi/pulumi/pull/4235) diff --git a/pkg/cmd/pulumi/new.go b/pkg/cmd/pulumi/new.go index 344772d79..995e760b2 100644 --- a/pkg/cmd/pulumi/new.go +++ b/pkg/cmd/pulumi/new.go @@ -19,6 +19,7 @@ import ( "fmt" "io/ioutil" "os" + "os/exec" "path/filepath" "runtime" "sort" @@ -42,6 +43,7 @@ import ( "github.com/pulumi/pulumi/sdk/go/common/tokens" "github.com/pulumi/pulumi/sdk/go/common/util/cmdutil" "github.com/pulumi/pulumi/sdk/go/common/util/contract" + "github.com/pulumi/pulumi/sdk/go/common/util/executable" "github.com/pulumi/pulumi/sdk/go/common/util/logging" "github.com/pulumi/pulumi/sdk/go/common/workspace" ) @@ -567,6 +569,11 @@ func installDependencies(proj *workspace.Project, root string) error { } } else if strings.EqualFold(proj.Runtime.Name(), "dotnet") { return dotnetInstallDependenciesAndBuild(proj, root) + } else if strings.EqualFold(proj.Runtime.Name(), "go") { + if err := goInstallDependencies(); err != nil { + return errors.Wrapf(err, "`go mod download` failed to install dependencies; rerun manually to try again, "+ + "then run 'pulumi up' to perform an initial deployment") + } } return nil @@ -616,6 +623,30 @@ func dotnetInstallDependenciesAndBuild(proj *workspace.Project, root string) err return nil } +// goInstallDependencies will install dependencies for the project by running `go mod download` +func goInstallDependencies() error { + fmt.Println("Installing dependencies...") + fmt.Println() + + gobin, err := executable.FindExecutable("go") + if err != nil { + return err + } + + cmd := exec.Command(gobin, "mod", "download") + cmd.Env = os.Environ() + cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr + + if err := cmd.Run(); err != nil { + return err + } + + fmt.Println("Finished installing dependencies") + fmt.Println() + + return nil +} + // printNextSteps prints out a series of commands that the user needs to run before their stack is able to be updated. func printNextSteps(proj *workspace.Project, originalCwd, cwd string, generateOnly bool, opts display.Options) { var commands []string diff --git a/sdk/go/common/util/executable/executable.go b/sdk/go/common/util/executable/executable.go new file mode 100644 index 000000000..12684347e --- /dev/null +++ b/sdk/go/common/util/executable/executable.go @@ -0,0 +1,51 @@ +package executable + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + + "github.com/pkg/errors" + "github.com/pulumi/pulumi/sdk/go/common/util/logging" +) + +const unableToFindProgramTemplate = "unable to find program: %s" + +// FindExecutable attempts to find the needed executable in various locations on the +// filesystem, eventually resorting to searching in $PATH. +func FindExecutable(program string) (string, error) { + if runtime.GOOS == "windows" && !strings.HasSuffix(program, ".exe") { + program = fmt.Sprintf("%s.exe", program) + } + // look in the same directory + cwd, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "unable to get current working directory") + } + + cwdProgram := filepath.Join(cwd, program) + if fileInfo, err := os.Stat(cwdProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() { + logging.V(5).Infof("program %s found in CWD", program) + return cwdProgram, nil + } + + // look in $GOPATH/bin + if goPath := os.Getenv("GOPATH"); len(goPath) > 0 { + goPathProgram := filepath.Join(goPath, "bin", program) + if fileInfo, err := os.Stat(goPathProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() { + logging.V(5).Infof("program %s found in $GOPATH/bin", program) + return goPathProgram, nil + } + } + + // look in the $PATH somewhere + if fullPath, err := exec.LookPath(program); err == nil { + logging.V(5).Infof("program %s found in $PATH", program) + return fullPath, nil + } + + return "", errors.Errorf(unableToFindProgramTemplate, program) +} diff --git a/sdk/go/pulumi-language-go/main.go b/sdk/go/pulumi-language-go/main.go index 614668974..81e50242c 100644 --- a/sdk/go/pulumi-language-go/main.go +++ b/sdk/go/pulumi-language-go/main.go @@ -24,7 +24,6 @@ import ( "os" "os/exec" "path/filepath" - "runtime" "strings" "syscall" @@ -35,6 +34,7 @@ import ( "github.com/pulumi/pulumi/sdk/go/common/util/buildutil" "github.com/pulumi/pulumi/sdk/go/common/util/cmdutil" + "github.com/pulumi/pulumi/sdk/go/common/util/executable" "github.com/pulumi/pulumi/sdk/go/common/util/logging" "github.com/pulumi/pulumi/sdk/go/common/util/rpcutil" "github.com/pulumi/pulumi/sdk/go/common/version" @@ -42,48 +42,12 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/proto/go" ) -// findExecutable attempts to find the needed executable in various locations on the -// filesystem, eventually resorting to searching in $PATH. -func findExecutable(program string) (string, error) { - if runtime.GOOS == "windows" && !strings.HasSuffix(program, ".exe") { - program = fmt.Sprintf("%s.exe", program) - } - // look in the same directory - cwd, err := os.Getwd() - if err != nil { - return "", errors.Wrap(err, "unable to get current working directory") - } - - cwdProgram := filepath.Join(cwd, program) - if fileInfo, err := os.Stat(cwdProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() { - logging.V(5).Infof("program %s found in CWD", program) - return cwdProgram, nil - } - - // look in $GOPATH/bin - if goPath := os.Getenv("GOPATH"); len(goPath) > 0 { - goPathProgram := filepath.Join(goPath, "bin", program) - if fileInfo, err := os.Stat(goPathProgram); !os.IsNotExist(err) && !fileInfo.Mode().IsDir() { - logging.V(5).Infof("program %s found in $GOPATH/bin", program) - return goPathProgram, nil - } - } - - // look in the $PATH somewhere - if fullPath, err := exec.LookPath(program); err == nil { - logging.V(5).Infof("program %s found in $PATH", program) - return fullPath, nil - } - - return "", errors.Errorf("unable to find program: %s", program) -} - func findProgram(binary string) (*exec.Cmd, error) { // we default to execution via `go run` // the user can explicitly opt in to using a binary executable by specifying // runtime.options.binary in the Pulumi.yaml if binary != "" { - program, err := findExecutable(binary) + program, err := executable.FindExecutable(binary) if err != nil { return nil, errors.Wrap(err, "expected to find prebuilt executable") } @@ -92,7 +56,7 @@ func findProgram(binary string) (*exec.Cmd, error) { // Fall back to 'go run' style executions logging.V(5).Infof("No prebuilt executable specified, attempting invocation via 'go run'") - program, err := findExecutable("go") + program, err := executable.FindExecutable("go") if err != nil { return nil, errors.Wrap(err, "problem executing program (could not run language executor)") } @@ -221,7 +185,7 @@ func (host *goLanguageHost) GetRequiredPlugins(ctx context.Context, logging.V(5).Infof("GetRequiredPlugins: Determining pulumi packages") - gobin, err := findExecutable("go") + gobin, err := executable.FindExecutable("go") if err != nil { return nil, errors.Wrap(err, "couldn't find go binary") }