mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
TS extractor: Allow the Node.js runtime to be configured via environment variables.
`SEMMLE_TYPESCRIPT_NODE_RUNTIME` can be used to provide the path to the Node.js runtime executable. If this is omitted, the extractor defaults to the current behaviour of looking for `node` on the PATH. `SEMMLE_TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS` can be used to provide additional arguments to the Node.js runtime. These are passed first, before the arguments supplied by the extractor. These changes are designed to allow TypeScript extraction in controlled customer environments where we cannot control the PATH, or must use custom Node.js executables with certain arguments set.
This commit is contained in:
@@ -33,6 +33,8 @@ import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -40,7 +42,12 @@ import java.util.List;
|
||||
*
|
||||
* <p>The Node.js half of the wrapper is expected to live at {@code
|
||||
* $SEMMLE_DIST/tools/typescript-parser-wrapper/main.js}; non-standard locations can be configured
|
||||
* using the property {@link #PARSER_WRAPPER_PATH_ENV_VAR}.
|
||||
* using the property {@value #PARSER_WRAPPER_PATH_ENV_VAR}.
|
||||
*
|
||||
* <p>The script launches the Node.js wrapper in the Node.js runtime, looking for {@code node}
|
||||
* on the {@code PATH} by default. Non-standard locations can be configured using the property
|
||||
* {@value #TYPESCRIPT_NODE_RUNTIME_VAR}, and additional arguments can be configured using the
|
||||
* property {@value #TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR}.
|
||||
*
|
||||
* <p>The script is started upon parsing the first TypeScript file and then is kept running in the
|
||||
* background, passing it requests for parsing files and getting JSON-encoded ASTs as responses.
|
||||
@@ -52,6 +59,18 @@ public class TypeScriptParser {
|
||||
*/
|
||||
public static final String PARSER_WRAPPER_PATH_ENV_VAR = "SEMMLE_TYPESCRIPT_PARSER_WRAPPER";
|
||||
|
||||
/**
|
||||
* An environment variable that can be set to indicate the location of the Node.js runtime,
|
||||
* as an alternative to adding Node to the PATH.
|
||||
*/
|
||||
public static final String TYPESCRIPT_NODE_RUNTIME_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME";
|
||||
|
||||
/**
|
||||
* An environment variable that can be set to provide additional arguments to the Node.js runtime
|
||||
* each time it is invoked. Arguments should be separated by spaces.
|
||||
*/
|
||||
public static final String TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR = "SEMMLE_TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS";
|
||||
|
||||
/**
|
||||
* An environment variable that can be set to specify a timeout to use when verifying the
|
||||
* TypeScript installation, in milliseconds. Default is 10000.
|
||||
@@ -91,6 +110,15 @@ public class TypeScriptParser {
|
||||
|
||||
private String nodeJsVersionString;
|
||||
|
||||
/** Command to launch the Node.js runtime. Initialised by {@link #verifyNodeInstallation}. */
|
||||
private String nodeJsRuntime;
|
||||
|
||||
/**
|
||||
* Arguments to pass to the Node.js runtime each time it is invoked.
|
||||
* Initialised by {@link #verifyNodeInstallation}.
|
||||
*/
|
||||
private List<String> nodeJsRuntimeExtraArgs = Collections.emptyList();
|
||||
|
||||
/** If non-zero, we use this instead of relying on the corresponding environment variable. */
|
||||
private int typescriptRam = 0;
|
||||
|
||||
@@ -102,12 +130,16 @@ public class TypeScriptParser {
|
||||
/**
|
||||
* Verifies that Node.js and TypeScript are installed and throws an exception otherwise.
|
||||
*
|
||||
* @param verbose if true, log the version strings and NODE_PATH.
|
||||
* @param verbose if true, log the Node.js executable path, version strings, and any additional arguments.
|
||||
*/
|
||||
public void verifyInstallation(boolean verbose) {
|
||||
verifyNodeInstallation();
|
||||
if (verbose) {
|
||||
System.out.println("Found Node.js at: " + nodeJsRuntime);
|
||||
System.out.println("Found Node.js version: " + nodeJsVersionString);
|
||||
if (!nodeJsRuntimeExtraArgs.isEmpty()) {
|
||||
System.out.println("Additional arguments for Node.js: " + nodeJsRuntimeExtraArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +149,24 @@ public class TypeScriptParser {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream err = new ByteArrayOutputStream();
|
||||
Builder b = new Builder(out, err, getParserWrapper().getParentFile(), "node", "--version");
|
||||
|
||||
// Determine where to find the Node.js runtime.
|
||||
String explicitNodeJsRuntime = Env.systemEnv().get(TYPESCRIPT_NODE_RUNTIME_VAR);
|
||||
if (explicitNodeJsRuntime != null) {
|
||||
// Use the specified Node.js executable.
|
||||
nodeJsRuntime = explicitNodeJsRuntime;
|
||||
} else {
|
||||
// Look for `node` on the PATH.
|
||||
nodeJsRuntime = "node";
|
||||
}
|
||||
|
||||
// Determine any additional arguments to be passed to Node.js each time it's called.
|
||||
String extraArgs = Env.systemEnv().get(TYPESCRIPT_NODE_RUNTIME_EXTRA_ARGS_VAR);
|
||||
if (extraArgs != null) {
|
||||
nodeJsRuntimeExtraArgs = Arrays.asList(extraArgs.split("\\s+"));
|
||||
}
|
||||
|
||||
Builder b = new Builder(getNodeJsRuntimeInvocation("--version"), out, err, getParserWrapper().getParentFile());
|
||||
b.expectFailure(); // We want to do our own logging in case of an error.
|
||||
|
||||
int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000);
|
||||
@@ -144,6 +193,21 @@ public class TypeScriptParser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a command line to invoke the Node.js runtime.
|
||||
* Any arguments in {@link TypeScriptParser#nodeJsRuntimeExtraArgs}
|
||||
* are passed first, followed by those in {@code args}.
|
||||
*/
|
||||
private List<String> getNodeJsRuntimeInvocation(String ...args) {
|
||||
List<String> result = new ArrayList<>();
|
||||
result.add(nodeJsRuntime);
|
||||
result.addAll(nodeJsRuntimeExtraArgs);
|
||||
for(String arg : args) {
|
||||
result.add(arg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getMegabyteCountFromPrefixedEnv(String suffix, int defaultValue) {
|
||||
String envVar = "SEMMLE_" + suffix;
|
||||
String value = Env.systemEnv().get(envVar);
|
||||
@@ -172,10 +236,11 @@ public class TypeScriptParser {
|
||||
int reserveMemoryMb = getMegabyteCountFromPrefixedEnv(TYPESCRIPT_RAM_RESERVE_SUFFIX, 400);
|
||||
|
||||
File parserWrapper = getParserWrapper();
|
||||
List<String> cmd = new ArrayList<>();
|
||||
cmd.add("node");
|
||||
cmd.add("--max_old_space_size=" + (mainMemoryMb + reserveMemoryMb));
|
||||
cmd.add(parserWrapper.getAbsolutePath());
|
||||
|
||||
List<String> cmd = getNodeJsRuntimeInvocation(
|
||||
"--max_old_space_size=" + (mainMemoryMb + reserveMemoryMb),
|
||||
parserWrapper.getAbsolutePath()
|
||||
);
|
||||
ProcessBuilder pb = new ProcessBuilder(cmd);
|
||||
parserWrapperCommand = StringUtil.glue(" ", cmd);
|
||||
pb.environment().put("SEMMLE_TYPESCRIPT_MEMORY_THRESHOLD", "" + mainMemoryMb);
|
||||
|
||||
Reference in New Issue
Block a user