Skip to content

Commit 80f367c

Browse files
authored
Show npm run failures more clearly (#7)
2 parents 3170355 + 666c403 commit 80f367c

File tree

5 files changed

+67
-13
lines changed

5 files changed

+67
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## [Unreleased]
44
### Added
55
- Task like `npm run lint:fix` get turned into `npm_run_lint-fix` (so the colons don't screw up Gradle)
6+
- When `npm run` commands fail, they dump their console output as a Gradle error.
67

78
## [1.1.0] - 2024-08-04
89
### Added

src/main/java/com/diffplug/webtools/node/NodePlugin.java

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@
1515
*/
1616
package com.diffplug.webtools.node;
1717

18-
import com.github.eirslett.maven.plugins.frontend.lib.ProxyConfig;
18+
import java.io.BufferedReader;
1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.InputStreamReader;
2123
import java.nio.charset.StandardCharsets;
2224
import java.nio.file.Files;
23-
import java.util.Collections;
25+
import java.util.ArrayList;
26+
import java.util.List;
2427
import java.util.Objects;
2528
import java.util.TreeMap;
26-
import org.gradle.api.Action;
27-
import org.gradle.api.DefaultTask;
28-
import org.gradle.api.Plugin;
29-
import org.gradle.api.Project;
29+
import java.util.concurrent.CompletableFuture;
30+
import org.gradle.api.*;
3031
import org.gradle.api.file.DirectoryProperty;
3132
import org.gradle.api.provider.Property;
3233
import org.gradle.api.tasks.CacheableTask;
@@ -94,14 +95,66 @@ public TreeMap<String, String> getEnvironment() {
9495
@Internal
9596
public abstract DirectoryProperty getProjectDir();
9697

98+
private static CompletableFuture<Void> readStream(InputStream inputStream, List<String> outputLines, String streamName) {
99+
return CompletableFuture.runAsync(() -> {
100+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
101+
String line;
102+
while ((line = reader.readLine()) != null) {
103+
synchronized (outputLines) {
104+
outputLines.add(line);
105+
}
106+
}
107+
} catch (IOException e) {
108+
synchronized (outputLines) {
109+
outputLines.add("Error reading " + streamName + ": " + e.getMessage());
110+
}
111+
}
112+
});
113+
}
114+
97115
@TaskAction
98116
public void npmCiRunTask() throws Exception {
99117
SetupCleanupNode setup = getSetup().get();
118+
File projectDir = getProjectDir().get().getAsFile();
100119
// install node, npm, and package-lock.json
101-
setup.start(getProjectDir().get().getAsFile());
102-
// run the gulp task
103-
ProxyConfig proxyConfig = new ProxyConfig(Collections.emptyList());
104-
setup.factory().getNpmRunner(proxyConfig, null).execute("run " + npmTaskName, environment);
120+
setup.start(projectDir);
121+
122+
// Use ProcessBuilder for direct console output instead of NpmRunner
123+
File installDir = new File(projectDir, "build/node-install");
124+
File npmExe;
125+
if (System.getProperty("os.name").toLowerCase().contains("win")) {
126+
npmExe = new File(installDir, "node/npm.cmd");
127+
} else {
128+
npmExe = new File(installDir, "node/npm");
129+
}
130+
131+
ProcessBuilder processBuilder = new ProcessBuilder(npmExe.getAbsolutePath(), "run", npmTaskName);
132+
processBuilder.directory(projectDir);
133+
processBuilder.environment().putAll(environment);
134+
Process process = processBuilder.start();
135+
136+
// Buffer output to only show on failure
137+
List<String> stdoutLines = new ArrayList<>();
138+
List<String> stderrLines = new ArrayList<>();
139+
140+
// Create threads to read stdout and stderr concurrently
141+
CompletableFuture<Void> stdoutFuture = readStream(process.getInputStream(), stdoutLines, "stdout");
142+
CompletableFuture<Void> stderrFuture = readStream(process.getErrorStream(), stderrLines, "stderr");
143+
int exitCode = process.waitFor();
144+
CompletableFuture.allOf(stdoutFuture, stderrFuture).join();
145+
if (exitCode == 0) {
146+
return;
147+
}
148+
149+
var cmd = new StringBuilder().append("> npm run ").append(npmTaskName).append(" FAILED\n");
150+
environment.forEach((key, value) -> cmd.append(" env ").append(key).append("=").append(value).append("\n"));
151+
for (String line : stdoutLines) {
152+
cmd.append(line).append("\n");
153+
}
154+
for (String line : stderrLines) {
155+
cmd.append(line).append("\n");
156+
}
157+
throw new GradleException(cmd.toString());
105158
}
106159
}
107160

src/main/java/com/diffplug/webtools/node/SetupCleanup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2024 DiffPlug
2+
* Copyright (C) 2024-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/main/java/com/diffplug/webtools/node/SetupCleanupNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2024 DiffPlug
2+
* Copyright (C) 2024-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

src/main/java/com/diffplug/webtools/serve/StaticServerTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2024 DiffPlug
2+
* Copyright (C) 2024-2025 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)