Skip to content

Commit 198b3b3

Browse files
authored
Merge pull request #2432 from asger-semmle/install-typescript-deps
Approved by max-schaefer
2 parents 2051356 + 6eb2c26 commit 198b3b3

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import com.semmle.util.process.Env;
2222
import com.semmle.util.projectstructure.ProjectLayout;
2323
import com.semmle.util.trap.TrapWriter;
24+
import java.io.BufferedReader;
2425
import java.io.File;
2526
import java.io.IOException;
27+
import java.io.InputStreamReader;
2628
import java.io.Reader;
2729
import java.lang.ProcessBuilder.Redirect;
2830
import java.net.URI;
@@ -195,6 +197,11 @@ public class AutoBuild {
195197
private final String defaultEncoding;
196198
private ExecutorService threadPool;
197199
private volatile boolean seenCode = false;
200+
private boolean installDependencies = false;
201+
private int installDependenciesTimeout;
202+
203+
/** The default timeout when running <code>yarn</code>, in milliseconds. */
204+
public static final int INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT = 10 * 60 * 1000; // 10 minutes
198205

199206
public AutoBuild() {
200207
this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC"));
@@ -204,6 +211,11 @@ public AutoBuild() {
204211
this.typeScriptMode =
205212
getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL);
206213
this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING");
214+
this.installDependencies = Boolean.valueOf(getEnvVar("LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS"));
215+
this.installDependenciesTimeout =
216+
Env.systemEnv()
217+
.getInt(
218+
"LGTM_INDEX_TYPESCRIPT_INSTALL_DEPS_TIMEOUT", INSTALL_DEPENDENCIES_DEFAULT_TIMEOUT);
207219
setupFileTypes();
208220
setupXmlMode();
209221
setupMatchers();
@@ -533,6 +545,10 @@ private void extractSource() throws IOException {
533545
List<Path> tsconfigFiles = new ArrayList<>();
534546
findFilesToExtract(defaultExtractor, filesToExtract, tsconfigFiles);
535547

548+
if (!tsconfigFiles.isEmpty() && this.installDependencies) {
549+
this.installDependencies(filesToExtract);
550+
}
551+
536552
// extract TypeScript projects and files
537553
Set<Path> extractedFiles = extractTypeScript(defaultExtractor, filesToExtract, tsconfigFiles);
538554

@@ -549,6 +565,61 @@ private void extractSource() throws IOException {
549565
}
550566
}
551567

568+
/** Returns true if yarn is installed, otherwise prints a warning and returns false. */
569+
private boolean verifyYarnInstallation() {
570+
ProcessBuilder pb = new ProcessBuilder(Arrays.asList("yarn", "-v"));
571+
try {
572+
Process process = pb.start();
573+
boolean completed = process.waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS);
574+
if (!completed) {
575+
System.err.println("Yarn could not be launched. Timeout during 'yarn -v'.");
576+
return false;
577+
}
578+
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
579+
String version = reader.readLine();
580+
System.out.println("Found yarn version: " + version);
581+
return true;
582+
} catch (IOException | InterruptedException ex) {
583+
System.err.println(
584+
"Yarn not found. Please put 'yarn' on the PATH for automatic dependency installation.");
585+
Exceptions.ignore(ex, "Continue without dependency installation");
586+
return false;
587+
}
588+
}
589+
590+
protected void installDependencies(Set<Path> filesToExtract) {
591+
if (!verifyYarnInstallation()) {
592+
return;
593+
}
594+
for (Path file : filesToExtract) {
595+
if (file.getFileName().toString().equals("package.json")) {
596+
System.out.println("Installing dependencies from " + file);
597+
ProcessBuilder pb =
598+
new ProcessBuilder(
599+
Arrays.asList(
600+
"yarn",
601+
"install",
602+
"--verbose",
603+
"--non-interactive",
604+
"--ignore-scripts",
605+
"--ignore-platform",
606+
"--ignore-engines",
607+
"--ignore-optional",
608+
"--no-default-rc",
609+
"--no-bin-links",
610+
"--pure-lockfile"));
611+
pb.directory(file.getParent().toFile());
612+
pb.redirectOutput(Redirect.INHERIT);
613+
pb.redirectError(Redirect.INHERIT);
614+
try {
615+
pb.start().waitFor(this.installDependenciesTimeout, TimeUnit.MILLISECONDS);
616+
} catch (IOException | InterruptedException ex) {
617+
throw new ResourceError("Could not install dependencies from " + file, ex);
618+
}
619+
}
620+
}
621+
}
622+
552623
private ExtractorConfig mkExtractorConfig() {
553624
ExtractorConfig config = new ExtractorConfig(true);
554625
config = config.withSourceType(getSourceType());

javascript/extractor/src/com/semmle/js/extractor/test/AutoBuildTests.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public void extractTypeScriptFiles(
128128
}
129129
}
130130

131+
@Override
132+
protected void installDependencies(Set<Path> filesToExtract) {
133+
// never install dependencies during testing
134+
}
135+
131136
@Override
132137
protected void extractXml() throws IOException {
133138
Files.walkFileTree(

0 commit comments

Comments
 (0)