2022-07-20 21:38:55 +00:00
|
|
|
// Copyright 2016-2022, Pulumi Corporation.
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2024-04-10 15:26:37 +00:00
|
|
|
// The tsnode import is used for type-checking only. Do not reference it in the emitted code.
|
|
|
|
import * as tsnode from "ts-node";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
import * as fs from "fs";
|
2023-06-24 00:20:43 +00:00
|
|
|
import * as fspromises from "fs/promises";
|
2023-02-28 17:06:15 +00:00
|
|
|
import * as ini from "ini";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
import * as minimist from "minimist";
|
|
|
|
import * as path from "path";
|
2022-03-22 11:26:55 +00:00
|
|
|
import * as semver from "semver";
|
2023-02-28 17:06:15 +00:00
|
|
|
import * as url from "url";
|
|
|
|
import * as util from "util";
|
2018-09-24 23:57:20 +00:00
|
|
|
import { ResourceError, RunError } from "../../errors";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
import * as log from "../../log";
|
2023-02-28 17:06:15 +00:00
|
|
|
import { Inputs } from "../../output";
|
2022-07-20 21:38:55 +00:00
|
|
|
import * as settings from "../../runtime/settings";
|
2023-02-28 17:06:15 +00:00
|
|
|
import * as stack from "../../runtime/stack";
|
2022-07-20 21:38:55 +00:00
|
|
|
import * as tsutils from "../../tsutils";
|
2023-02-28 17:06:15 +00:00
|
|
|
import * as tracing from "./tracing";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
2019-03-27 21:40:29 +00:00
|
|
|
import * as mod from ".";
|
|
|
|
|
2023-06-24 00:20:43 +00:00
|
|
|
// Workaround for typescript transpiling dynamic import into `Promise.resolve().then(() => require`
|
|
|
|
// Follow this issue for progress on when we can remove this:
|
|
|
|
// https://github.com/microsoft/TypeScript/issues/43329
|
|
|
|
//
|
|
|
|
// Workaround inspired by es-module-shims:
|
|
|
|
// https://github.com/guybedford/es-module-shims/blob/main/src/common.js#L21
|
|
|
|
/** @internal */
|
|
|
|
// eslint-disable-next-line no-eval
|
|
|
|
const dynamicImport = (0, eval)("u=>import(u)");
|
|
|
|
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
/**
|
|
|
|
* Attempts to provide a detailed error message for module load failure if the
|
|
|
|
* module that failed to load is the top-level module.
|
|
|
|
* @param program The name of the program given to `run`, i.e. the top level module
|
|
|
|
* @param error The error that occured. Must be a module load error.
|
|
|
|
*/
|
2023-06-24 00:20:43 +00:00
|
|
|
async function reportModuleLoadFailure(program: string, error: Error): Promise<void> {
|
|
|
|
await throwOrPrintModuleLoadError(program, error);
|
2019-03-27 21:40:29 +00:00
|
|
|
|
|
|
|
// Note: from this point on, we've printed something to the user telling them about the
|
|
|
|
// problem. So we can let our langhost know it doesn't need to report any further issues.
|
|
|
|
return process.exit(mod.nodeJSProcessExitedAfterLoggingUserActionableMessage);
|
|
|
|
}
|
|
|
|
|
2023-06-24 00:20:43 +00:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
* This function searches for the nearest package.json file, scanning up from the
|
|
|
|
* program path until it finds one. If it does not find a package.json file, it
|
|
|
|
* it returns the folder enclosing the program.
|
|
|
|
* @param programPath the path to the Pulumi program; this is the project "main" directory,
|
|
|
|
* which defaults to the project "root" directory.
|
|
|
|
*/
|
|
|
|
async function npmPackageRootFromProgramPath(programPath: string): Promise<string> {
|
|
|
|
// pkg-dir is an ESM module which we use to find the location of package.json
|
|
|
|
// Because it's an ESM module, we cannot import it directly.
|
|
|
|
const { packageDirectory } = await dynamicImport("pkg-dir");
|
|
|
|
// Check if programPath is a directory. If not, then we
|
|
|
|
// look at it's parent dir for the package root.
|
|
|
|
let isDirectory = false;
|
|
|
|
try {
|
|
|
|
const fileStat = await fspromises.lstat(programPath);
|
|
|
|
isDirectory = fileStat.isDirectory();
|
|
|
|
} catch {
|
|
|
|
// Since an exception was thrown, the program path doesn't exist.
|
|
|
|
// Do nothing, because isDirectory is already false.
|
2021-12-30 03:22:01 +00:00
|
|
|
}
|
2023-06-24 00:20:43 +00:00
|
|
|
const programDirectory = isDirectory ? programPath : path.dirname(programPath);
|
|
|
|
const pkgDir = await packageDirectory({
|
|
|
|
cwd: programDirectory,
|
|
|
|
});
|
|
|
|
if (pkgDir === undefined) {
|
|
|
|
log.warn(
|
|
|
|
"Could not find a package.json file for the program. Using the Pulumi program directory as the project root.",
|
|
|
|
);
|
|
|
|
return programDirectory;
|
|
|
|
}
|
|
|
|
return pkgDir;
|
2021-12-30 03:22:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function packageObjectFromProjectRoot(projectRoot: string): Record<string, any> {
|
2023-06-24 00:20:43 +00:00
|
|
|
const packageJson = path.join(projectRoot, "package.json");
|
2021-12-30 03:22:01 +00:00
|
|
|
try {
|
2023-04-27 16:06:43 +00:00
|
|
|
return require(packageJson);
|
|
|
|
} catch {
|
2021-12-30 03:22:01 +00:00
|
|
|
// This is all best-effort so if we can't load the package.json file, that's
|
|
|
|
// fine.
|
2023-04-27 16:06:43 +00:00
|
|
|
return {};
|
2021-12-30 03:22:01 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-27 21:40:29 +00:00
|
|
|
|
2022-03-22 11:26:55 +00:00
|
|
|
// Reads and parses the contents of .npmrc file if it exists under the project root
|
|
|
|
// This assumes that .npmrc is a sibling to package.json
|
2023-04-28 22:27:10 +00:00
|
|
|
function npmRcFromProjectRoot(projectRoot: string): Record<string, any> {
|
2022-09-15 11:20:45 +00:00
|
|
|
const rcSpan = tracing.newSpan("language-runtime.reading-npm-rc");
|
2022-03-22 11:26:55 +00:00
|
|
|
const emptyConfig = {};
|
|
|
|
try {
|
|
|
|
const npmRcPath = path.join(projectRoot, ".npmrc");
|
|
|
|
if (!fs.existsSync(npmRcPath)) {
|
|
|
|
return emptyConfig;
|
|
|
|
}
|
|
|
|
// file .npmrc exists, read its contents
|
|
|
|
const npmRc = fs.readFileSync(npmRcPath, "utf-8");
|
|
|
|
// Use ini to parse the contents of the .npmrc file
|
|
|
|
// This is what node does as described in the npm docs
|
|
|
|
// https://docs.npmjs.com/cli/v8/configuring-npm/npmrc#comments
|
2022-08-31 16:46:48 +00:00
|
|
|
const parseResult = ini.parse(npmRc);
|
|
|
|
rcSpan.end();
|
|
|
|
return parseResult;
|
2022-03-22 11:26:55 +00:00
|
|
|
} catch {
|
|
|
|
// .npmrc file exists but we couldn't read or parse it
|
|
|
|
// user out of luck here
|
2022-08-31 16:46:48 +00:00
|
|
|
rcSpan.end();
|
2022-03-22 11:26:55 +00:00
|
|
|
return emptyConfig;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-24 00:20:43 +00:00
|
|
|
async function throwOrPrintModuleLoadError(program: string, error: Error): Promise<void> {
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
// error is guaranteed to be a Node module load error. Node emits a very
|
|
|
|
// specific string in its error message for module load errors, which includes
|
|
|
|
// the module it was trying to load.
|
|
|
|
const errorRegex = /Cannot find module '(.*)'/;
|
|
|
|
|
|
|
|
// If there's no match, who knows what this exception is; it's not something
|
|
|
|
// we can provide an intelligent diagnostic for.
|
|
|
|
const moduleNameMatches = errorRegex.exec(error.message);
|
|
|
|
if (moduleNameMatches === null) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is the module that failed to load exactly the one that this script considered to
|
|
|
|
// be the top-level module for this program?
|
|
|
|
//
|
|
|
|
// We are only interested in producing good diagnostics for top-level module loads,
|
|
|
|
// since anything else are probably user code issues.
|
|
|
|
const moduleName = moduleNameMatches[1];
|
|
|
|
if (moduleName !== program) {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
|
2019-03-27 21:40:29 +00:00
|
|
|
// Note: from this point on, we've printed something to the user telling them about the
|
|
|
|
// problem. So we can let our langhost know it doesn't need to report any further issues.
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
console.error(`We failed to locate the entry point for your program: ${program}`);
|
|
|
|
|
|
|
|
// From here on out, we're going to try to inspect the program we're being asked to run
|
|
|
|
// a little to see what sort of details we can glean from it, in the hopes of producing
|
|
|
|
// a better error message.
|
|
|
|
//
|
|
|
|
// The first step of this is trying to slurp up a package.json for this program, if
|
|
|
|
// one exists.
|
2023-06-24 00:20:43 +00:00
|
|
|
const packageRoot = await npmPackageRootFromProgramPath(program);
|
|
|
|
const packageObject = packageObjectFromProjectRoot(packageRoot);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
|
|
|
console.error("Here's what we think went wrong:");
|
|
|
|
|
|
|
|
// The objective here is to emit the best diagnostic we can, starting from the
|
|
|
|
// most specific to the least specific.
|
|
|
|
const deps = packageObject["dependencies"] || {};
|
|
|
|
const devDeps = packageObject["devDependencies"] || {};
|
|
|
|
const scripts = packageObject["scripts"] || {};
|
2021-11-22 19:42:39 +00:00
|
|
|
const mainProperty = packageObject["main"] || "index.js";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
|
|
|
// Is there a build script associated with this program? It's a little confusing that the
|
|
|
|
// Pulumi CLI doesn't run build scripts before running the program so call that out
|
|
|
|
// explicitly.
|
|
|
|
|
|
|
|
if ("build" in scripts) {
|
|
|
|
const command = scripts["build"];
|
|
|
|
console.error(` * Your program looks like it has a build script associated with it ('${command}').\n`);
|
2023-04-28 22:27:10 +00:00
|
|
|
console.error(
|
|
|
|
"Pulumi does not run build scripts before running your program. " +
|
|
|
|
`Please run '${command}', 'yarn build', or 'npm run build' and try again.`,
|
|
|
|
);
|
2019-03-27 21:40:29 +00:00
|
|
|
return;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Not all typescript programs have build scripts. If we think it's a typescript program,
|
|
|
|
// tell the user to run tsc.
|
|
|
|
if ("typescript" in deps || "typescript" in devDeps) {
|
|
|
|
console.error(" * Your program looks like a TypeScript program. Have you run 'tsc'?");
|
2019-03-27 21:40:29 +00:00
|
|
|
return;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Not all projects are typescript. If there's a main property, check that the file exists.
|
|
|
|
if (mainProperty !== undefined && typeof mainProperty === "string") {
|
2023-06-24 00:20:43 +00:00
|
|
|
const mainFile = path.join(packageRoot, mainProperty);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
if (!fs.existsSync(mainFile)) {
|
|
|
|
console.error(` * Your program's 'main' file (${mainFile}) does not exist.`);
|
2019-03-27 21:40:29 +00:00
|
|
|
return;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-15 14:52:12 +00:00
|
|
|
console.error(" * Pulumi encountered an unexpected error.");
|
|
|
|
console.error(` Raw exception message: ${error.message}`);
|
2019-03-27 21:40:29 +00:00
|
|
|
return;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 11:20:45 +00:00
|
|
|
function tracingIsEnabled(tracingUrl: string | boolean): boolean {
|
2023-04-28 22:27:10 +00:00
|
|
|
if (typeof tracingUrl !== "string") {
|
2022-09-15 11:20:45 +00:00
|
|
|
return false;
|
2022-09-13 15:09:47 +00:00
|
|
|
}
|
|
|
|
const experimental = process.env["PULUMI_EXPERIMENTAL"] ?? "";
|
|
|
|
const nonzeroLength = tracingUrl.length > 0;
|
|
|
|
const experimentalEnabled = experimental.length > 0;
|
|
|
|
return nonzeroLength && experimentalEnabled;
|
|
|
|
}
|
|
|
|
|
2020-01-26 17:06:35 +00:00
|
|
|
/** @internal */
|
2024-03-07 08:52:34 +00:00
|
|
|
export async function run(
|
2021-11-22 19:42:39 +00:00
|
|
|
argv: minimist.ParsedArgs,
|
|
|
|
programStarted: () => void,
|
|
|
|
reportLoggedError: (err: Error) => void,
|
2023-04-28 22:27:10 +00:00
|
|
|
isErrorReported: (err: Error) => boolean,
|
|
|
|
): Promise<Inputs | undefined> {
|
2022-09-13 15:09:47 +00:00
|
|
|
const tracingUrl: string | boolean = argv["tracing"];
|
2022-09-15 11:20:45 +00:00
|
|
|
// Start tracing. Before exiting, gracefully shutdown tracing, exporting
|
2022-08-30 20:11:34 +00:00
|
|
|
// all remaining spans in the batch.
|
2023-04-28 22:27:10 +00:00
|
|
|
if (tracingIsEnabled(tracingUrl)) {
|
2022-09-13 15:09:47 +00:00
|
|
|
tracing.start(tracingUrl as string); // safe cast, since tracingIsEnable confirmed the type
|
2022-09-06 02:35:43 +00:00
|
|
|
process.on("exit", tracing.stop);
|
2022-08-30 21:22:42 +00:00
|
|
|
}
|
2022-08-30 20:11:34 +00:00
|
|
|
// Start a new span, which we shutdown at the bottom of this method.
|
2022-09-15 11:20:45 +00:00
|
|
|
const span = tracing.newSpan("language-runtime.run");
|
2022-08-30 20:11:34 +00:00
|
|
|
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
// If there is a --pwd directive, switch directories.
|
|
|
|
const pwd: string | undefined = argv["pwd"];
|
|
|
|
if (pwd) {
|
|
|
|
process.chdir(pwd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this is a typescript project, we'll want to load node-ts.
|
|
|
|
const typeScript: boolean = process.env["PULUMI_NODEJS_TYPESCRIPT"] === "true";
|
|
|
|
|
2018-08-31 18:48:23 +00:00
|
|
|
// We provide reasonable defaults for many ts options, meaning you don't need to have a tsconfig.json present
|
|
|
|
// if you want to use TypeScript with Pulumi. However, ts-node's default behavior is to walk up from the cwd to
|
|
|
|
// find a tsconfig.json. For us, it's reasonable to say that the "root" of the project is the cwd,
|
|
|
|
// if there's a tsconfig.json file here. Otherwise, just tell ts-node to not load project options at all.
|
|
|
|
// This helps with cases like pulumi/pulumi#1772.
|
2021-11-22 19:42:39 +00:00
|
|
|
const defaultTsConfigPath = "tsconfig.json";
|
|
|
|
const tsConfigPath: string = process.env["PULUMI_NODEJS_TSCONFIG_PATH"] ?? defaultTsConfigPath;
|
2021-08-16 01:58:43 +00:00
|
|
|
const skipProject = !fs.existsSync(tsConfigPath);
|
|
|
|
|
2022-08-31 16:46:48 +00:00
|
|
|
span.setAttribute("typescript-enabled", typeScript);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
if (typeScript) {
|
2022-07-20 21:38:55 +00:00
|
|
|
const transpileOnly = (process.env["PULUMI_NODEJS_TRANSPILE_ONLY"] ?? "false") === "true";
|
|
|
|
const compilerOptions = tsutils.loadTypeScriptCompilerOptions(tsConfigPath);
|
2024-04-10 15:26:37 +00:00
|
|
|
const { tsnodeRequire, typescriptRequire } = tsutils.typeScriptRequireStrings();
|
|
|
|
const tsn: typeof tsnode = require(tsnodeRequire);
|
2022-07-20 21:38:55 +00:00
|
|
|
tsn.register({
|
2024-04-10 15:26:37 +00:00
|
|
|
compiler: typescriptRequire,
|
2022-02-15 10:48:53 +00:00
|
|
|
transpileOnly,
|
2022-02-28 16:35:44 +00:00
|
|
|
// PULUMI_NODEJS_TSCONFIG_PATH might be set to a config file such as "tsconfig.pulumi.yaml" which
|
|
|
|
// would not get picked up by tsnode by default, so we explicitly tell tsnode which config file to
|
|
|
|
// use (Which might just be ./tsconfig.yaml)
|
|
|
|
project: tsConfigPath,
|
2018-08-31 18:48:23 +00:00
|
|
|
skipProject: skipProject,
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
compilerOptions: {
|
|
|
|
target: "es6",
|
|
|
|
module: "commonjs",
|
|
|
|
moduleResolution: "node",
|
|
|
|
sourceMap: "true",
|
2021-08-16 01:58:43 +00:00
|
|
|
...compilerOptions,
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-11 11:36:34 +00:00
|
|
|
const hasEntrypoint = argv._[0] !== ".";
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
let program: string = argv._[0];
|
2022-03-04 00:26:06 +00:00
|
|
|
if (!path.isAbsolute(program)) {
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
// If this isn't an absolute path, make it relative to the working directory.
|
|
|
|
program = path.join(process.cwd(), program);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now fake out the process-wide argv, to make the program think it was run normally.
|
|
|
|
const programArgs: string[] = argv._.slice(1);
|
2021-11-22 19:42:39 +00:00
|
|
|
process.argv = [process.argv[0], process.argv[1], ...programArgs];
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
|
|
|
// Set up the process uncaught exception, unhandled rejection, and program exit handlers.
|
|
|
|
const uncaughtHandler = (err: Error) => {
|
2018-09-24 23:57:20 +00:00
|
|
|
// In node, if you throw an error in a chained promise, but the exception is not finally
|
|
|
|
// handled, then you can end up getting an unhandledRejection for each exception/promise
|
|
|
|
// pair. Because the exception is the same through all of these, we keep track of it and
|
|
|
|
// only report it once so the user doesn't get N messages for the same thing.
|
2021-05-10 22:04:03 +00:00
|
|
|
if (isErrorReported(err)) {
|
2018-09-24 23:57:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-16 18:09:59 +00:00
|
|
|
// colorize stack trace if exists
|
2023-04-28 22:27:10 +00:00
|
|
|
const stackMessage = err.stack && util.inspect(err, { colors: true });
|
2022-08-16 18:09:59 +00:00
|
|
|
|
2018-09-24 20:42:16 +00:00
|
|
|
// Default message should be to include the full stack (which includes the message), or
|
|
|
|
// fallback to just the message if we can't get the stack.
|
2019-12-17 01:27:36 +00:00
|
|
|
//
|
|
|
|
// If both the stack and message are empty, then just stringify the err object itself. This
|
|
|
|
// is also necessary as users can throw arbitrary things in JS (including non-Errors).
|
2023-04-28 22:27:10 +00:00
|
|
|
const defaultMessage = stackMessage || err.message || "" + err;
|
2018-09-24 20:42:16 +00:00
|
|
|
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
// First, log the error.
|
2022-08-24 02:33:29 +00:00
|
|
|
if (RunError.isInstance(err)) {
|
2018-09-24 23:57:20 +00:00
|
|
|
// Always hide the stack for RunErrors.
|
|
|
|
log.error(err.message);
|
2024-04-10 15:26:37 +00:00
|
|
|
} else if (err.name === "TSError" || err.name === SyntaxError.name) {
|
2022-08-24 02:33:29 +00:00
|
|
|
// Hide stack frames as TSError/SyntaxError have messages containing
|
|
|
|
// where the error is located
|
|
|
|
const errOut = err.stack?.toString() || "";
|
|
|
|
let errMsg = err.message;
|
|
|
|
|
|
|
|
const errParts = errOut.split(err.message);
|
|
|
|
if (errParts.length === 2) {
|
2023-04-28 22:27:10 +00:00
|
|
|
errMsg = errParts[0] + err.message;
|
2022-08-24 02:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.error(
|
|
|
|
`Running program '${program}' failed with an unhandled exception:
|
2023-04-28 22:27:10 +00:00
|
|
|
${errMsg}`,
|
|
|
|
);
|
2022-08-24 02:33:29 +00:00
|
|
|
} else if (ResourceError.isInstance(err)) {
|
2018-09-24 23:57:20 +00:00
|
|
|
// Hide the stack if requested to by the ResourceError creator.
|
2018-09-24 20:42:16 +00:00
|
|
|
const message = err.hideStack ? err.message : defaultMessage;
|
|
|
|
log.error(message, err.resource);
|
2022-08-24 02:33:29 +00:00
|
|
|
} else {
|
2019-03-27 19:17:38 +00:00
|
|
|
log.error(
|
2021-08-10 18:31:59 +00:00
|
|
|
`Running program '${program}' failed with an unhandled exception:
|
2023-04-28 22:27:10 +00:00
|
|
|
${defaultMessage}`,
|
|
|
|
);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
2022-09-15 11:20:45 +00:00
|
|
|
|
2022-08-30 20:11:34 +00:00
|
|
|
span.addEvent(`uncaughtError: ${err}`);
|
2019-03-27 21:40:29 +00:00
|
|
|
reportLoggedError(err);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
};
|
2018-09-24 20:42:16 +00:00
|
|
|
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
process.on("uncaughtException", uncaughtHandler);
|
2019-09-11 23:21:35 +00:00
|
|
|
// @ts-ignore 'unhandledRejection' will almost always invoke uncaughtHandler with an Error. so
|
|
|
|
// just suppress the TS strictness here.
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
process.on("unhandledRejection", uncaughtHandler);
|
2022-07-20 21:38:55 +00:00
|
|
|
process.on("exit", settings.disconnectSync);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
2023-05-17 21:02:37 +00:00
|
|
|
// Trigger callback to update a sentinel variable tracking
|
|
|
|
// whether the program is running.
|
2019-01-07 17:59:29 +00:00
|
|
|
programStarted();
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
|
2021-11-22 19:42:39 +00:00
|
|
|
// This needs to occur after `programStarted` to ensure execution of the parent process stops.
|
|
|
|
if (skipProject && tsConfigPath !== defaultTsConfigPath) {
|
2022-08-31 16:46:48 +00:00
|
|
|
span.addEvent("Missing tsconfig file");
|
2021-11-22 19:42:39 +00:00
|
|
|
return new Promise(() => {
|
|
|
|
const e = new Error(`tsconfig path was set to ${tsConfigPath} but the file was not found`);
|
|
|
|
e.stack = undefined;
|
|
|
|
throw e;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-08-16 18:41:15 +00:00
|
|
|
const containsTSAndJSModules = async (programPath: string) => {
|
|
|
|
const programStats = await fs.promises.lstat(programPath);
|
|
|
|
if (programStats.isDirectory()) {
|
|
|
|
const programDirFiles = await fs.promises.readdir(programPath);
|
|
|
|
return programDirFiles.includes("index.js") && programDirFiles.includes("index.ts");
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-12-09 19:28:20 +00:00
|
|
|
const runProgram = async () => {
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
// We run the program inside this context so that it adopts all resources.
|
|
|
|
//
|
|
|
|
// IDEA: This will miss any resources created on other turns of the event loop. I think that's a fundamental
|
|
|
|
// problem with the current Component design though - not sure what else we could do here.
|
|
|
|
//
|
|
|
|
// Now go ahead and execute the code. The process will remain alive until the message loop empties.
|
|
|
|
log.debug(`Running program '${program}' in pwd '${process.cwd()}' w/ args: ${programArgs}`);
|
2022-08-31 16:46:48 +00:00
|
|
|
|
|
|
|
// Create a new span for the execution of the user program.
|
2022-09-15 11:20:45 +00:00
|
|
|
const runProgramSpan = tracing.newSpan("language-runtime.runProgram");
|
2022-08-31 16:46:48 +00:00
|
|
|
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
try {
|
2023-06-24 00:20:43 +00:00
|
|
|
const packageRoot = await npmPackageRootFromProgramPath(program);
|
|
|
|
const packageObject = packageObjectFromProjectRoot(packageRoot);
|
2023-05-15 17:15:13 +00:00
|
|
|
let programExport: any;
|
2023-04-26 23:15:01 +00:00
|
|
|
|
2024-06-11 11:36:34 +00:00
|
|
|
// If there is no entrypoint set in Pulumi.yaml via the main
|
|
|
|
// option, look for an entrypoint defined in package.json
|
|
|
|
if (!hasEntrypoint && packageObject["main"]) {
|
2023-06-24 00:20:43 +00:00
|
|
|
const packageMainPath = path.join(packageRoot, packageObject["main"]);
|
|
|
|
if (fs.existsSync(packageMainPath)) {
|
|
|
|
program = packageMainPath;
|
|
|
|
} else {
|
|
|
|
log.warn(
|
|
|
|
`Could not find entry point '${packageMainPath}' specified in package.json; ` +
|
|
|
|
`using '${program}' instead`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-30 03:22:01 +00:00
|
|
|
// We use dynamic import instead of require for projects using native ES modules instead of commonjs
|
|
|
|
if (packageObject["type"] === "module") {
|
2022-01-05 02:54:38 +00:00
|
|
|
// Use the same behavior for loading the main entrypoint as `node <program>`.
|
|
|
|
// See https://github.com/nodejs/node/blob/master/lib/internal/modules/run_main.js#L74.
|
2023-04-28 22:27:10 +00:00
|
|
|
const mainPath: string =
|
|
|
|
require("module").Module._findPath(path.resolve(program), null, true) || program;
|
2022-01-05 02:54:38 +00:00
|
|
|
const main = path.isAbsolute(mainPath) ? url.pathToFileURL(mainPath).href : mainPath;
|
2021-12-30 03:22:01 +00:00
|
|
|
// Import the module and capture any module outputs it exported. Finally, await the value we get
|
|
|
|
// back. That way, if it is async and throws an exception, we properly capture it here
|
|
|
|
// and handle it.
|
[sdk/nodejs] Support top-level default exports in ESM (#8766)
In #7764 and #8655 we added support for ESM entrypoints. However, ESM "default exports" were handled just as "normal" in Node.js dynamic import of ESM - as a `default` proeprty in the export object.
This is not a particularly useful behaviour for Pulumi program entry points, and doesn't quite match some of the special logic we apply to non-object exports in CommonJS modules (invoking exported functions, and then awaiting exports promises).
Instead, this change adds support for default exports, treating the default export (if present) as the full returned export value.
It is for now an error to have both a default export and named exports, since it is unclear what this should mean. In the future, we could potentially relax this and define how these two sets of exports are merged.
This is technically a breaking change from the support added in the recent releases, but only in a narrow case, and in that case the Pulumi stack exports were almost certainly not what the user wanted.
Fixes #8725, which includes a motivating example where this is ~necessary.
2022-01-23 02:33:28 +00:00
|
|
|
programExport = await dynamicImport(main);
|
|
|
|
// If there is a default export, use that instead of the named exports (and error if there are both).
|
|
|
|
if (Object.getOwnPropertyDescriptor(programExport, "default") !== undefined) {
|
|
|
|
if (Object.keys(programExport).length !== 1) {
|
2023-04-28 22:27:10 +00:00
|
|
|
throw new Error(
|
|
|
|
"expected entrypoint module to have either a default export or named exports but not both",
|
|
|
|
);
|
[sdk/nodejs] Support top-level default exports in ESM (#8766)
In #7764 and #8655 we added support for ESM entrypoints. However, ESM "default exports" were handled just as "normal" in Node.js dynamic import of ESM - as a `default` proeprty in the export object.
This is not a particularly useful behaviour for Pulumi program entry points, and doesn't quite match some of the special logic we apply to non-object exports in CommonJS modules (invoking exported functions, and then awaiting exports promises).
Instead, this change adds support for default exports, treating the default export (if present) as the full returned export value.
It is for now an error to have both a default export and named exports, since it is unclear what this should mean. In the future, we could potentially relax this and define how these two sets of exports are merged.
This is technically a breaking change from the support added in the recent releases, but only in a narrow case, and in that case the Pulumi stack exports were almost certainly not what the user wanted.
Fixes #8725, which includes a motivating example where this is ~necessary.
2022-01-23 02:33:28 +00:00
|
|
|
}
|
|
|
|
programExport = programExport.default;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// It's a CommonJS module, so require the module and capture any module outputs it exported.
|
2023-05-15 17:15:13 +00:00
|
|
|
|
2023-02-28 17:06:15 +00:00
|
|
|
// If this is a folder ensure it ends with a "/" so we require the folder, not any adjacent .json file
|
|
|
|
const programStats = await fs.promises.lstat(program);
|
|
|
|
if (programStats.isDirectory() && !program.endsWith("/")) {
|
|
|
|
program = program + "/";
|
|
|
|
}
|
[sdk/nodejs] Support top-level default exports in ESM (#8766)
In #7764 and #8655 we added support for ESM entrypoints. However, ESM "default exports" were handled just as "normal" in Node.js dynamic import of ESM - as a `default` proeprty in the export object.
This is not a particularly useful behaviour for Pulumi program entry points, and doesn't quite match some of the special logic we apply to non-object exports in CommonJS modules (invoking exported functions, and then awaiting exports promises).
Instead, this change adds support for default exports, treating the default export (if present) as the full returned export value.
It is for now an error to have both a default export and named exports, since it is unclear what this should mean. In the future, we could potentially relax this and define how these two sets of exports are merged.
This is technically a breaking change from the support added in the recent releases, but only in a narrow case, and in that case the Pulumi stack exports were almost certainly not what the user wanted.
Fixes #8725, which includes a motivating example where this is ~necessary.
2022-01-23 02:33:28 +00:00
|
|
|
programExport = require(program);
|
2021-12-30 03:22:01 +00:00
|
|
|
}
|
|
|
|
|
2022-08-16 18:41:15 +00:00
|
|
|
if (await containsTSAndJSModules(program)) {
|
2023-04-28 22:27:10 +00:00
|
|
|
log.warn(
|
|
|
|
"Found a TypeScript project containing an index.js file and no explicit entrypoint in Pulumi.yaml - Pulumi will use index.js",
|
|
|
|
);
|
|
|
|
}
|
2022-08-16 18:41:15 +00:00
|
|
|
|
2022-03-22 11:26:55 +00:00
|
|
|
// Check compatible engines before running the program:
|
2023-06-24 00:20:43 +00:00
|
|
|
const npmRc = npmRcFromProjectRoot(packageRoot);
|
2022-03-22 11:26:55 +00:00
|
|
|
if (npmRc["engine-strict"] && packageObject.engines && packageObject.engines.node) {
|
|
|
|
// found:
|
|
|
|
// - { engines: { node: "<version>" } } in package.json
|
|
|
|
// - engine-strict=true in .npmrc
|
|
|
|
//
|
|
|
|
// Check that current node version satistfies the required version
|
|
|
|
const requiredNodeVersion = packageObject.engines.node;
|
|
|
|
const currentNodeVersion = process.versions.node;
|
|
|
|
if (!semver.satisfies(currentNodeVersion, requiredNodeVersion)) {
|
|
|
|
const errorMessage = [
|
2023-06-24 00:20:43 +00:00
|
|
|
`Your current Node version is incompatible to run ${packageRoot}`,
|
2022-03-22 11:26:55 +00:00
|
|
|
`Expected version: ${requiredNodeVersion} as found in package.json > engines > node`,
|
|
|
|
`Actual Node version: ${currentNodeVersion}`,
|
|
|
|
`To fix issue, install a Node version that is compatible with ${requiredNodeVersion}`,
|
|
|
|
];
|
|
|
|
|
2022-08-31 16:46:48 +00:00
|
|
|
runProgramSpan.addEvent("Incompatible Node version");
|
2022-03-22 11:26:55 +00:00
|
|
|
throw new Error(errorMessage.join("\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[sdk/nodejs] Support top-level default exports in ESM (#8766)
In #7764 and #8655 we added support for ESM entrypoints. However, ESM "default exports" were handled just as "normal" in Node.js dynamic import of ESM - as a `default` proeprty in the export object.
This is not a particularly useful behaviour for Pulumi program entry points, and doesn't quite match some of the special logic we apply to non-object exports in CommonJS modules (invoking exported functions, and then awaiting exports promises).
Instead, this change adds support for default exports, treating the default export (if present) as the full returned export value.
It is for now an error to have both a default export and named exports, since it is unclear what this should mean. In the future, we could potentially relax this and define how these two sets of exports are merged.
This is technically a breaking change from the support added in the recent releases, but only in a narrow case, and in that case the Pulumi stack exports were almost certainly not what the user wanted.
Fixes #8725, which includes a motivating example where this is ~necessary.
2022-01-23 02:33:28 +00:00
|
|
|
// If the exported value was itself a Function, then just execute it. This allows for
|
|
|
|
// exported top level async functions that pulumi programs can live in. Finally, await
|
|
|
|
// the value we get back. That way, if it is async and throws an exception, we properly
|
|
|
|
// capture it here and handle it.
|
2023-04-28 22:27:10 +00:00
|
|
|
const invokeResult = programExport instanceof Function ? programExport() : programExport;
|
2022-08-31 16:46:48 +00:00
|
|
|
runProgramSpan.end();
|
2019-12-09 19:28:20 +00:00
|
|
|
return await invokeResult;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
} catch (e) {
|
|
|
|
// User JavaScript can throw anything, so if it's not an Error it's definitely
|
|
|
|
// not something we want to catch up here.
|
|
|
|
if (!(e instanceof Error)) {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Give a better error message, if we can.
|
|
|
|
const errorCode = (<any>e).code;
|
|
|
|
if (errorCode === "MODULE_NOT_FOUND") {
|
2022-08-31 16:46:48 +00:00
|
|
|
runProgramSpan.addEvent("Module Load Failure.");
|
2023-06-24 00:20:43 +00:00
|
|
|
await reportModuleLoadFailure(program, e);
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
throw e;
|
2023-04-28 22:27:10 +00:00
|
|
|
} finally {
|
2022-08-31 16:46:48 +00:00
|
|
|
runProgramSpan.end();
|
|
|
|
}
|
2019-12-09 19:28:20 +00:00
|
|
|
};
|
2022-09-15 11:20:45 +00:00
|
|
|
|
2019-12-09 19:28:20 +00:00
|
|
|
// Construct a `Stack` resource to represent the outputs of the program.
|
2024-03-07 08:52:34 +00:00
|
|
|
const stackOutputs = await stack.runInPulumiStack(runProgram);
|
|
|
|
await settings.disconnect();
|
2022-08-30 20:11:34 +00:00
|
|
|
span.end();
|
|
|
|
return stackOutputs;
|
Do not lazy initialize config or settings
The pulumi runtime used to lazily load and parse both config and
settings data set by the language host. The initial reason for this
design was that we wanted the runtime to be usable in a normal node
environment, but we have moved away from supporting that.
In addition, while we claimed we loaded these value "lazily", we
actually forced their loading quite eagerly when we started
up. However, when capturing config (or settings, as we now do), we
would capture all the logic about loading these values from the
environment.
Even worse, in the case where you had two copies of @pulumi/pulumi
loaded, it would be possible to capture a config object which was not
initialized and then at runtime the initialization logic would try to
read PULUMI_CONFIG from the process environment and fail.
So we adopt a new model where configuration and settings are parsed as
we load their containing modules. In addition, to support SxS
scinerios, we continue to use `process.env` as a way to control both
configuration and settings. This means that `run.ts` must now ensure
that these values are present in the environment before either the
config or runtime modules have been loaded.
2018-08-03 22:33:15 +00:00
|
|
|
}
|