Support TypeScript in a more first-class way
This change lets us set runtime specific options in Pulumi.yaml, which will flow as arguments to the language hosts. We then teach the nodejs host that when the `typescript` is set to `true` that it should load ts-node before calling into user code. This allows using typescript natively without an explicit compile step outside of Pulumi. This works even when a tsconfig.json file is not present in the application and should provide a nicer inner loop for folks writing typescript (I'm pretty sure everyone has run into the "but I fixed that bug! Why isn't it getting picked up? Oh, I forgot to run tsc" problem. Fixes #958
This commit is contained in:
parent
5a52c1c080
commit
ce5eaa8343
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,6 +9,9 @@ coverage.cov
|
|||
/.idea/
|
||||
*.iml
|
||||
|
||||
# VSCode creates this binary when running tests in the debugger
|
||||
**/debug.test
|
||||
|
||||
# Go tests run "in tree" and this folder will linger if they fail (the integration test framework cleans
|
||||
# it up when they pass.)
|
||||
**/command-output/
|
||||
|
|
|
@ -25,7 +25,10 @@ import (
|
|||
)
|
||||
|
||||
func TestPrettyKeyForProject(t *testing.T) {
|
||||
proj := &workspace.Project{Name: tokens.PackageName("test-package"), Runtime: "nodejs"}
|
||||
proj := &workspace.Project{
|
||||
Name: tokens.PackageName("test-package"),
|
||||
RuntimeInfo: workspace.NewProjectRuntimeInfo("nodejs", nil),
|
||||
}
|
||||
|
||||
assert.Equal(t, "foo", prettyKeyForProject(config.MustMakeKey("test-package", "foo"), proj))
|
||||
assert.Equal(t, "other-package:bar", prettyKeyForProject(config.MustMakeKey("other-package", "bar"), proj))
|
||||
|
|
|
@ -353,10 +353,10 @@ func installDependencies() error {
|
|||
// TODO[pulumi/pulumi#1307]: move to the language plugins so we don't have to hard code here.
|
||||
var command string
|
||||
var c *exec.Cmd
|
||||
if strings.EqualFold(proj.Runtime, "nodejs") {
|
||||
if strings.EqualFold(proj.RuntimeInfo.Name(), "nodejs") {
|
||||
command = "npm install"
|
||||
c = exec.Command("npm", "install") // nolint: gas, intentionally launching with partial path
|
||||
} else if strings.EqualFold(proj.Runtime, "python") {
|
||||
} else if strings.EqualFold(proj.RuntimeInfo.Name(), "python") {
|
||||
command = "pip install -r requirements.txt"
|
||||
c = exec.Command("pip", "install", "-r", "requirements.txt") // nolint: gas, intentionally launching with partial path
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
name: minimal
|
||||
description: A minimal Pulumi program.
|
||||
runtime: nodejs
|
||||
runtime:
|
||||
name: nodejs
|
||||
options:
|
||||
typescript: true
|
||||
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
{
|
||||
"name": "minimal",
|
||||
"main": "bin/index.js",
|
||||
"typings": "bin/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^3.0.0"
|
||||
},
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "bin",
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -351,7 +351,7 @@ func (pc *Client) CreateUpdate(
|
|||
|
||||
updateRequest := apitype.UpdateProgramRequest{
|
||||
Name: string(pkg.Name),
|
||||
Runtime: pkg.Runtime,
|
||||
Runtime: pkg.RuntimeInfo.Name(),
|
||||
Main: main,
|
||||
Description: description,
|
||||
Config: wireConfig,
|
||||
|
|
|
@ -130,7 +130,7 @@ func GetStackTags() (map[apitype.StackTagName]string, error) {
|
|||
return nil, errors.Wrapf(err, "error loading project %q", projPath)
|
||||
}
|
||||
tags[apitype.ProjectNameTag] = proj.Name.String()
|
||||
tags[apitype.ProjectRuntimeTag] = proj.Runtime
|
||||
tags[apitype.ProjectRuntimeTag] = proj.RuntimeInfo.Name()
|
||||
if proj.Description != nil {
|
||||
tags[apitype.ProjectDescriptionTag] = *proj.Description
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func ProjectInfoContext(projinfo *Projinfo, config plugin.ConfigSource, pluginEv
|
|||
}
|
||||
|
||||
// Create a context for plugins.
|
||||
ctx, err := plugin.NewContext(diag, nil, config, pluginEvents, pwd, tracingSpan)
|
||||
ctx, err := plugin.NewContext(diag, nil, config, pluginEvents, pwd, projinfo.Proj.RuntimeInfo.Options(), tracingSpan)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
func TestNullPlan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil)
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
targ := &Target{Name: tokens.QName("null")}
|
||||
prev := NewSnapshot(Manifest{}, nil)
|
||||
|
@ -56,7 +56,7 @@ func TestErrorPlan(t *testing.T) {
|
|||
|
||||
// First trigger an error from Iterate:
|
||||
{
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil)
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
targ := &Target{Name: tokens.QName("errs")}
|
||||
prev := NewSnapshot(Manifest{}, nil)
|
||||
|
@ -71,7 +71,7 @@ func TestErrorPlan(t *testing.T) {
|
|||
|
||||
// Next trigger an error from Next:
|
||||
{
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil)
|
||||
ctx, err := plugin.NewContext(cmdutil.Diag(), nil, nil, nil, "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
targ := &Target{Name: tokens.QName("errs")}
|
||||
prev := NewSnapshot(Manifest{}, nil)
|
||||
|
@ -141,7 +141,7 @@ func TestBasicCRUDPlan(t *testing.T) {
|
|||
// we don't actually execute the plan, so there's no need to implement the other functions.
|
||||
}, nil
|
||||
},
|
||||
}, nil, nil, "", nil)
|
||||
}, nil, nil, "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
// Setup a fake namespace/target combination.
|
||||
|
|
|
@ -162,7 +162,7 @@ func (iter *evalSourceIterator) forkRun(opts Options) {
|
|||
go func() {
|
||||
// Next, launch the language plugin.
|
||||
run := func() error {
|
||||
rt := iter.src.runinfo.Proj.Runtime
|
||||
rt := iter.src.runinfo.Proj.RuntimeInfo.Name()
|
||||
langhost, err := iter.src.plugctx.Host.LanguageRuntime(rt)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to launch language host %s", rt)
|
||||
|
|
|
@ -36,7 +36,7 @@ type Context struct {
|
|||
// NewContext allocates a new context with a given sink and host. Note that the host is "owned" by this context from
|
||||
// here forwards, such that when the context's resources are reclaimed, so too are the host's.
|
||||
func NewContext(d diag.Sink, host Host, cfg ConfigSource, events Events,
|
||||
pwd string, parentSpan opentracing.Span) (*Context, error) {
|
||||
pwd string, runtimeOptions map[string]bool, parentSpan opentracing.Span) (*Context, error) {
|
||||
ctx := &Context{
|
||||
Diag: d,
|
||||
Host: host,
|
||||
|
@ -44,7 +44,7 @@ func NewContext(d diag.Sink, host Host, cfg ConfigSource, events Events,
|
|||
tracingSpan: parentSpan,
|
||||
}
|
||||
if host == nil {
|
||||
h, err := NewDefaultHost(ctx, cfg, events)
|
||||
h, err := NewDefaultHost(ctx, cfg, events, runtimeOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -78,11 +78,12 @@ type Events interface {
|
|||
}
|
||||
|
||||
// NewDefaultHost implements the standard plugin logic, using the standard installation root to find them.
|
||||
func NewDefaultHost(ctx *Context, config ConfigSource, events Events) (Host, error) {
|
||||
func NewDefaultHost(ctx *Context, config ConfigSource, events Events, runtimeOptions map[string]bool) (Host, error) {
|
||||
host := &defaultHost{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
events: events,
|
||||
runtimeOptions: runtimeOptions,
|
||||
analyzerPlugins: make(map[tokens.QName]*analyzerPlugin),
|
||||
languagePlugins: make(map[string]*languagePlugin),
|
||||
resourcePlugins: make(map[tokens.Package]*resourcePlugin),
|
||||
|
@ -116,6 +117,7 @@ type defaultHost struct {
|
|||
ctx *Context // the shared context for this host.
|
||||
config ConfigSource // the source for provider configuration parameters.
|
||||
events Events // optional callbacks for plugin load events
|
||||
runtimeOptions map[string]bool // options to pass to the language plugins.
|
||||
analyzerPlugins map[tokens.QName]*analyzerPlugin // a cache of analyzer plugins and their processes.
|
||||
languagePlugins map[string]*languagePlugin // a cache of language plugins and their processes.
|
||||
resourcePlugins map[tokens.Package]*resourcePlugin // a cache of resource plugins and their processes.
|
||||
|
@ -284,7 +286,7 @@ func (host *defaultHost) LanguageRuntime(runtime string) (LanguageRuntime, error
|
|||
}
|
||||
|
||||
// If not, allocate a new one.
|
||||
plug, err := NewLanguageRuntime(host, host.ctx, runtime)
|
||||
plug, err := NewLanguageRuntime(host, host.ctx, runtime, host.runtimeOptions)
|
||||
if err == nil && plug != nil {
|
||||
info, infoerr := plug.GetPluginInfo()
|
||||
if infoerr != nil {
|
||||
|
@ -356,12 +358,12 @@ func (host *defaultHost) GetRequiredPlugins(info ProgInfo, kinds Flags) ([]works
|
|||
if kinds&LanguagePlugins != 0 {
|
||||
// First make sure the language plugin is present. We need this to load the required resource plugins.
|
||||
// TODO: we need to think about how best to version this. For now, it always picks the latest.
|
||||
lang, err := host.LanguageRuntime(info.Proj.Runtime)
|
||||
lang, err := host.LanguageRuntime(info.Proj.RuntimeInfo.Name())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to load language plugin %s", info.Proj.Runtime)
|
||||
return nil, errors.Wrapf(err, "failed to load language plugin %s", info.Proj.RuntimeInfo.Name())
|
||||
}
|
||||
plugins = append(plugins, workspace.PluginInfo{
|
||||
Name: info.Proj.Runtime,
|
||||
Name: info.Proj.RuntimeInfo.Name(),
|
||||
Kind: workspace.LanguagePlugin,
|
||||
})
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver"
|
||||
|
@ -40,7 +41,7 @@ type langhost struct {
|
|||
|
||||
// NewLanguageRuntime binds to a language's runtime plugin and then creates a gRPC connection to it. If the
|
||||
// plugin could not be found, or an error occurs while creating the child process, an error is returned.
|
||||
func NewLanguageRuntime(host Host, ctx *Context, runtime string) (LanguageRuntime, error) {
|
||||
func NewLanguageRuntime(host Host, ctx *Context, runtime string, options map[string]bool) (LanguageRuntime, error) {
|
||||
// Load the plugin's path by using the standard workspace logic.
|
||||
_, path, err := workspace.GetPluginPath(
|
||||
workspace.LanguagePlugin, strings.Replace(runtime, tokens.QNameDelimiter, "_", -1), nil)
|
||||
|
@ -53,7 +54,13 @@ func NewLanguageRuntime(host Host, ctx *Context, runtime string) (LanguageRuntim
|
|||
})
|
||||
}
|
||||
|
||||
plug, err := newPlugin(ctx, path, runtime, []string{host.ServerAddr()})
|
||||
var args []string
|
||||
for k, v := range options {
|
||||
args = append(args, fmt.Sprintf("-%s=%t", k, v))
|
||||
}
|
||||
args = append(args, host.ServerAddr())
|
||||
|
||||
plug, err := newPlugin(ctx, path, runtime, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -139,8 +139,8 @@ type ProgramTestOptions struct {
|
|||
Quick bool
|
||||
// UpdateCommandlineFlags specifies flags to add to the `pulumi update` command line (e.g. "--color=raw")
|
||||
UpdateCommandlineFlags []string
|
||||
// SkipBuild indicates that the build step should be skipped (e.g. no `yarn build` for `nodejs` programs)
|
||||
SkipBuild bool
|
||||
// RunBuild indicates that the build step should be run (e.g. run `yarn build` for `nodejs` programs)
|
||||
RunBuild bool
|
||||
|
||||
// CloudURL is an optional URL to override the default Pulumi Service API (https://api.pulumi-staging.io). The
|
||||
// PULUMI_ACCESS_TOKEN environment variable must also be set to a valid access token for the target cloud.
|
||||
|
@ -286,8 +286,8 @@ func (opts ProgramTestOptions) With(overrides ProgramTestOptions) ProgramTestOpt
|
|||
if overrides.ReportStats != nil {
|
||||
opts.ReportStats = overrides.ReportStats
|
||||
}
|
||||
if overrides.SkipBuild {
|
||||
opts.SkipBuild = overrides.SkipBuild
|
||||
if overrides.RunBuild {
|
||||
opts.RunBuild = overrides.RunBuild
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
@ -924,7 +924,7 @@ func (pt *programTester) copyTestToTemporaryDirectory() (string, string, error)
|
|||
// For most projects, we will copy to a temporary directory. For Go projects, however, we must not perturb
|
||||
// the source layout, due to GOPATH and vendoring. So, skip it for Go.
|
||||
var tmpdir, projdir string
|
||||
if projinfo.Proj.Runtime == "go" {
|
||||
if projinfo.Proj.RuntimeInfo.Name() == "go" {
|
||||
projdir = projinfo.Root
|
||||
} else {
|
||||
stackName := string(pt.opts.GetStackName())
|
||||
|
@ -966,7 +966,7 @@ func (pt *programTester) getProjinfo(projectDir string) (*engine.Projinfo, error
|
|||
// prepareProject runs setup necessary to get the project ready for `pulumi` commands.
|
||||
func (pt *programTester) prepareProject(projinfo *engine.Projinfo) error {
|
||||
// Based on the language, invoke the right routine to prepare the target directory.
|
||||
switch rt := projinfo.Proj.Runtime; rt {
|
||||
switch rt := projinfo.Proj.RuntimeInfo.Name(); rt {
|
||||
case "nodejs":
|
||||
return pt.prepareNodeJSProject(projinfo)
|
||||
case "python":
|
||||
|
@ -1016,7 +1016,7 @@ func (pt *programTester) prepareNodeJSProject(projinfo *engine.Projinfo) error {
|
|||
}
|
||||
}
|
||||
|
||||
if !pt.opts.SkipBuild {
|
||||
if pt.opts.RunBuild {
|
||||
// And finally compile it using whatever build steps are in the package.json file.
|
||||
if err = pt.runYarnCommand("yarn-build", []string{"run", "build"}, cwd); err != nil {
|
||||
return err
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package workspace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -40,9 +41,9 @@ type Analyzers []tokens.QName
|
|||
// TODO[pulumi/pulumi#423]: use DOM based marshalling so we can roundtrip the seralized structure perfectly.
|
||||
// nolint: lll
|
||||
type Project struct {
|
||||
Name tokens.PackageName `json:"name" yaml:"name"` // a required fully qualified name.
|
||||
Runtime string `json:"runtime" yaml:"runtime"` // a required runtime that executes code.
|
||||
Main string `json:"main,omitempty" yaml:"main,omitempty"` // an optional override for the main program location.
|
||||
Name tokens.PackageName `json:"name" yaml:"name"` // a required fully qualified name.
|
||||
RuntimeInfo ProjectRuntimeInfo `json:"runtime" yaml:"runtime"` // a required runtime that executes code.
|
||||
Main string `json:"main,omitempty" yaml:"main,omitempty"` // an optional override for the main program location.
|
||||
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty"` // an optional informational description.
|
||||
Author *string `json:"author,omitempty" yaml:"author,omitempty"` // an optional author.
|
||||
|
@ -61,9 +62,10 @@ func (proj *Project) Validate() error {
|
|||
if proj.Name == "" {
|
||||
return errors.New("project is missing a 'name' attribute")
|
||||
}
|
||||
if proj.Runtime == "" {
|
||||
if proj.RuntimeInfo.Name() == "" {
|
||||
return errors.New("project is missing a 'runtime' attribute")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -124,6 +126,86 @@ func (ps *ProjectStack) Save(path string) error {
|
|||
return ioutil.WriteFile(path, b, 0644)
|
||||
}
|
||||
|
||||
type ProjectRuntimeInfo struct {
|
||||
name string
|
||||
options map[string]bool
|
||||
}
|
||||
|
||||
func NewProjectRuntimeInfo(name string, options map[string]bool) ProjectRuntimeInfo {
|
||||
return ProjectRuntimeInfo{
|
||||
name: name,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (info *ProjectRuntimeInfo) Name() string {
|
||||
return info.name
|
||||
}
|
||||
|
||||
func (info *ProjectRuntimeInfo) Options() map[string]bool {
|
||||
return info.options
|
||||
}
|
||||
|
||||
func (info ProjectRuntimeInfo) MarshalYAML() (interface{}, error) {
|
||||
if info.options == nil || len(info.options) == 0 {
|
||||
return info.name, nil
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"name": info.name,
|
||||
"options": info.options,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (info ProjectRuntimeInfo) MarshalJSON() ([]byte, error) {
|
||||
if info.options == nil || len(info.options) == 0 {
|
||||
return json.Marshal(info.name)
|
||||
}
|
||||
|
||||
return json.Marshal(map[string]interface{}{
|
||||
"name": info.name,
|
||||
"options": info.options,
|
||||
})
|
||||
}
|
||||
|
||||
func (info *ProjectRuntimeInfo) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &info.name); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Name string `json:"name"`
|
||||
Options map[string]bool `json:"options"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &payload); err == nil {
|
||||
info.name = payload.Name
|
||||
info.options = payload.Options
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("runtime section must be a string or an object with name and options attributes")
|
||||
}
|
||||
|
||||
func (info *ProjectRuntimeInfo) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
if err := unmarshal(&info.name); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Name string `yaml:"name"`
|
||||
Options map[string]bool `yaml:"options"`
|
||||
}
|
||||
|
||||
if err := unmarshal(&payload); err == nil {
|
||||
info.name = payload.Name
|
||||
info.options = payload.Options
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("runtime section must be a string or an object with name and options attributes")
|
||||
}
|
||||
|
||||
// LoadProject reads a project definition from a file.
|
||||
func LoadProject(path string) (*Project, error) {
|
||||
contract.Require(path != "", "path")
|
||||
|
|
36
pkg/workspace/project_test.go
Normal file
36
pkg/workspace/project_test.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package workspace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestProjectRuntimeInfoRoundtripYAML(t *testing.T) {
|
||||
doTest := func(marshal func(interface{}) ([]byte, error), unmarshal func([]byte, interface{}) error) {
|
||||
ri := NewProjectRuntimeInfo("nodejs", nil)
|
||||
byts, err := marshal(ri)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var riRountrip ProjectRuntimeInfo
|
||||
err = unmarshal(byts, &riRountrip)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "nodejs", riRountrip.Name())
|
||||
assert.Nil(t, riRountrip.Options())
|
||||
|
||||
ri = NewProjectRuntimeInfo("nodejs", map[string]bool{
|
||||
"typescript": true,
|
||||
})
|
||||
byts, err = marshal(ri)
|
||||
assert.NoError(t, err)
|
||||
err = unmarshal(byts, &riRountrip)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "nodejs", riRountrip.Name())
|
||||
assert.Equal(t, true, riRountrip.Options()["typescript"])
|
||||
}
|
||||
|
||||
doTest(yaml.Marshal, yaml.Unmarshal)
|
||||
doTest(json.Marshal, json.Unmarshal)
|
||||
}
|
|
@ -64,10 +64,13 @@ const (
|
|||
// endpoint.
|
||||
func main() {
|
||||
var tracing string
|
||||
var typescript bool
|
||||
flag.StringVar(&tracing, "tracing", "",
|
||||
"Emit tracing to a Zipkin-compatible tracing endpoint")
|
||||
|
||||
flag.BoolVar(&typescript, "typescript", true,
|
||||
"Use ts-node at runtime to support typescript source natively")
|
||||
flag.Parse()
|
||||
|
||||
args := flag.Args()
|
||||
logging.InitLogging(false, 0, false)
|
||||
cmdutil.InitTracing("pulumi-language-nodejs", "pulumi-langauge-nodejs", tracing)
|
||||
|
@ -96,7 +99,7 @@ func main() {
|
|||
// Fire up a gRPC server, letting the kernel choose a free port.
|
||||
port, done, err := rpcutil.Serve(0, nil, []func(*grpc.Server) error{
|
||||
func(srv *grpc.Server) error {
|
||||
host := newLanguageHost(nodePath, runPath, engineAddress, tracing)
|
||||
host := newLanguageHost(nodePath, runPath, engineAddress, tracing, typescript)
|
||||
pulumirpc.RegisterLanguageRuntimeServer(srv, host)
|
||||
return nil
|
||||
},
|
||||
|
@ -121,14 +124,16 @@ type nodeLanguageHost struct {
|
|||
runPath string
|
||||
engineAddress string
|
||||
tracing string
|
||||
typescript bool
|
||||
}
|
||||
|
||||
func newLanguageHost(nodePath, runPath, engineAddress, tracing string) pulumirpc.LanguageRuntimeServer {
|
||||
func newLanguageHost(nodePath, runPath, engineAddress, tracing string, typescript bool) pulumirpc.LanguageRuntimeServer {
|
||||
return &nodeLanguageHost{
|
||||
nodeBin: nodePath,
|
||||
runPath: runPath,
|
||||
engineAddress: engineAddress,
|
||||
tracing: tracing,
|
||||
typescript: typescript,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,29 +269,13 @@ func (host *nodeLanguageHost) Run(ctx context.Context, req *pulumirpc.RunRequest
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ourCmd, err := os.Executable()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "failed to find our working directory")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Older versions of the pulumi runtime used a custom node module (which only worked on node 6.10.X) to support
|
||||
// closure serialization. While we no longer use this, we continue to ship this module with the language host in
|
||||
// the SDK, so we can deploy programs using older versions of the Pulumi framework. So, for now, let's add this
|
||||
// folder with our native modules to the NODE_PATH so Node can find it.
|
||||
//
|
||||
// TODO(ellismg)[pulumi/pulumi#1298]: Remove this block of code when we no longer need to support older
|
||||
// @pulumi/pulumi versions.
|
||||
env := os.Environ()
|
||||
existingNodePath := os.Getenv("NODE_PATH")
|
||||
if existingNodePath != "" {
|
||||
env = append(env, fmt.Sprintf("NODE_PATH=%s/v6.10.2:%s", filepath.Dir(ourCmd), existingNodePath))
|
||||
} else {
|
||||
env = append(env, "NODE_PATH="+filepath.Dir(ourCmd)+"/v6.10.2")
|
||||
}
|
||||
|
||||
env = append(env, pulumiConfigVar+"="+string(config))
|
||||
|
||||
if host.typescript {
|
||||
env = append(env, "PULUMI_NODEJS_TYPESCRIPT=true")
|
||||
}
|
||||
|
||||
if logging.V(5) {
|
||||
commandStr := strings.Join(args, " ")
|
||||
logging.V(5).Infoln("Language host launching process: ", host.nodeBin, commandStr)
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
|
||||
import * as fs from "fs";
|
||||
import * as minimist from "minimist";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import * as tsnode from "ts-node";
|
||||
import * as util from "util";
|
||||
import * as pulumi from "../../";
|
||||
import { RunError } from "../../errors";
|
||||
import * as log from "../../log";
|
||||
import * as runtime from "../../runtime";
|
||||
|
@ -180,9 +181,12 @@ export function main(args: string[]): void {
|
|||
}
|
||||
}
|
||||
|
||||
// If ther is a --dry-run directive, flip the switch. This controls whether we are planning vs. really doing it.
|
||||
// If there is a --dry-run directive, flip the switch. This controls whether we are planning vs. really doing it.
|
||||
const dryRun: boolean = !!(argv["dry-run"]);
|
||||
|
||||
// If this is a typescript project, we'll want to load node-ts
|
||||
const typeScript: boolean = process.env["PULUMI_NODEJS_TYPESCRIPT"] === "true";
|
||||
|
||||
// If there is a monitor argument, connect to it.
|
||||
const monitorAddr = argv["monitor"];
|
||||
if (!monitorAddr) {
|
||||
|
@ -202,6 +206,12 @@ export function main(args: string[]): void {
|
|||
engineAddr: engineAddr,
|
||||
});
|
||||
|
||||
if (typeScript) {
|
||||
tsnode.register({
|
||||
typeCheck: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Pluck out the program and arguments.
|
||||
if (argv._.length === 0) {
|
||||
return printErrorUsageAndExit("error: Missing program to execute");
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
{
|
||||
"name": "@pulumi/pulumi",
|
||||
"version": "${VERSION}",
|
||||
"description": "Pulumi's Node.js SDK",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/pulumi/pulumi/sdk/nodejs",
|
||||
"dependencies": {
|
||||
"google-protobuf": "^3.5.0",
|
||||
"grpc": "^1.12.2",
|
||||
"minimist": "^1.2.0",
|
||||
"protobufjs": "^6.8.6",
|
||||
"require-from-string": "^2.0.1",
|
||||
"source-map-support": "^0.4.16",
|
||||
"typescript": "^3.0.0",
|
||||
"read-package-tree": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "^8.0.25",
|
||||
"@types/read-package-tree": "^5.2.0",
|
||||
"@types/semver": "^5.5.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.5.0",
|
||||
"node-gyp": "^3.6.2",
|
||||
"tslint": "^5.7.0"
|
||||
}
|
||||
"name": "@pulumi/pulumi",
|
||||
"version": "${VERSION}",
|
||||
"description": "Pulumi's Node.js SDK",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/pulumi/pulumi/sdk/nodejs",
|
||||
"dependencies": {
|
||||
"google-protobuf": "^3.5.0",
|
||||
"grpc": "^1.12.2",
|
||||
"minimist": "^1.2.0",
|
||||
"protobufjs": "^6.8.6",
|
||||
"require-from-string": "^2.0.1",
|
||||
"source-map-support": "^0.4.16",
|
||||
"ts-node": "^7.0.0",
|
||||
"typescript": "^3.0.0",
|
||||
"read-package-tree": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/minimist": "^1.2.0",
|
||||
"@types/mocha": "^2.2.42",
|
||||
"@types/node": "^8.0.25",
|
||||
"@types/read-package-tree": "^5.2.0",
|
||||
"@types/semver": "^5.5.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.5.0",
|
||||
"node-gyp": "^3.6.2",
|
||||
"tslint": "^5.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,10 @@ argparse@^1.0.7:
|
|||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
arrify@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
|
||||
asap@^2.0.0:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
|
@ -215,6 +219,10 @@ browser-stdout@1.3.0:
|
|||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
|
||||
|
||||
buffer-from@^1.0.0, buffer-from@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04"
|
||||
|
||||
builtin-modules@^1.0.0, builtin-modules@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
|
||||
|
@ -395,6 +403,10 @@ diff@3.2.0:
|
|||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
|
||||
|
||||
diff@^3.1.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
|
||||
diff@^3.2.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c"
|
||||
|
@ -847,6 +859,10 @@ longest@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535"
|
||||
|
||||
mime-db@~1.30.0:
|
||||
version "1.30.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
|
||||
|
@ -1274,6 +1290,13 @@ source-map-support@^0.4.16:
|
|||
dependencies:
|
||||
source-map "^0.5.6"
|
||||
|
||||
source-map-support@^0.5.6:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13"
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.4.4:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
|
||||
|
@ -1284,6 +1307,10 @@ source-map@^0.5.6, source-map@~0.5.1:
|
|||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
|
||||
source-map@~0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
|
||||
|
@ -1406,6 +1433,19 @@ tough-cookie@~2.3.3:
|
|||
dependencies:
|
||||
punycode "^1.4.1"
|
||||
|
||||
ts-node@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.0.tgz#a94a13c75e5e1aa6b82814b84c68deb339ba7bff"
|
||||
dependencies:
|
||||
arrify "^1.0.0"
|
||||
buffer-from "^1.1.0"
|
||||
diff "^3.1.0"
|
||||
make-error "^1.1.1"
|
||||
minimist "^1.2.0"
|
||||
mkdirp "^0.5.1"
|
||||
source-map-support "^0.5.6"
|
||||
yn "^2.0.0"
|
||||
|
||||
tslib@^1.8.0, tslib@^1.8.1:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8"
|
||||
|
@ -1560,3 +1600,7 @@ yargs@~3.10.0:
|
|||
cliui "^2.1.0"
|
||||
decamelize "^1.0.0"
|
||||
window-size "0.1.0"
|
||||
|
||||
yn@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a"
|
||||
|
|
|
@ -294,8 +294,8 @@ func TestConfigSave(t *testing.T) {
|
|||
// Initialize an empty stack.
|
||||
path := filepath.Join(e.RootPath, "Pulumi.yaml")
|
||||
err := (&workspace.Project{
|
||||
Name: "testing-config",
|
||||
Runtime: "nodejs",
|
||||
Name: "testing-config",
|
||||
RuntimeInfo: workspace.NewProjectRuntimeInfo("nodejs", nil),
|
||||
}).Save(path)
|
||||
assert.NoError(t, err)
|
||||
e.RunCommand("pulumi", "login", "--cloud-url", e.LocalURL())
|
||||
|
|
Loading…
Reference in a new issue