Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: --openssl-legacy-provider also in build-frontend (#12733) (CP: 2.7) #12757

Merged
merged 1 commit into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void execute() throws MojoExecutionException, MojoFailureException {

if (generateBundle) {
try {
runWebpack();
runWebpack(getFrontendTools());
} catch (IllegalStateException exception) {
throw new MojoExecutionException(exception.getMessage(),
exception);
Expand Down Expand Up @@ -200,7 +200,7 @@ private void runNodeUpdater() throws ExecutionFailedException, MojoExecutionExce
.execute();
}

private void runWebpack() throws MojoExecutionException {
void runWebpack(FrontendTools tools) throws MojoExecutionException {
String webpackCommand = "webpack/bin/webpack.js";
File webpackExecutable = new File(npmFolder,
NODE_MODULES + webpackCommand);
Expand All @@ -211,19 +211,9 @@ private void runWebpack() throws MojoExecutionException {
webpackExecutable.getAbsolutePath()));
}

final URI nodeDownloadRootURI;
try {
nodeDownloadRootURI = new URI(nodeDownloadRoot);
} catch (URISyntaxException e) {
throw new MojoExecutionException("Failed to parse " + nodeDownloadRoot, e);
}
String nodePath;
FrontendTools tools = new FrontendTools(npmFolder.getAbsolutePath(),
()-> FrontendUtils.getVaadinHomeDirectory().getAbsolutePath(),
nodeVersion, nodeDownloadRootURI, requireHomeNodeExec);
if (requireHomeNodeExec) {
nodePath = tools
.forceAlternativeNodeExecutable();
nodePath = tools.forceAlternativeNodeExecutable();
} else {
nodePath = tools.getNodeExecutable();
}
Expand All @@ -232,6 +222,8 @@ private void runWebpack() throws MojoExecutionException {
webpackExecutable.getAbsolutePath());
ProcessBuilder builder = FrontendUtils.createProcessBuilder(command)
.directory(project.getBasedir()).inheritIO();
builder.environment().putAll(tools.getWebpackNodeEnvironment());

getLog().info("Running webpack ...");
if ( getLog().isDebugEnabled()) {
getLog().debug(FrontendUtils.commandToString(npmFolder.getAbsolutePath(),
Expand All @@ -255,6 +247,18 @@ private void runWebpack() throws MojoExecutionException {
}
}

private FrontendTools getFrontendTools() throws MojoExecutionException {
final URI nodeDownloadRootURI;
try {
nodeDownloadRootURI = new URI(nodeDownloadRoot);
} catch (URISyntaxException e) {
throw new MojoExecutionException("Failed to parse " + nodeDownloadRoot, e);
}
return new FrontendTools(npmFolder.getAbsolutePath(),
()-> FrontendUtils.getVaadinHomeDirectory().getAbsolutePath(),
nodeVersion, nodeDownloadRootURI, requireHomeNodeExec);
}

private void readDetailsAndThrowException(Process webpackLaunch) {
String stderr = readFullyAndClose(
"Failed to read webpack process stderr",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
Expand All @@ -28,7 +29,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -42,6 +45,7 @@
import org.codehaus.plexus.util.ReflectionUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -51,6 +55,7 @@
import com.vaadin.flow.plugin.TestUtils;
import com.vaadin.flow.server.Constants;
import com.vaadin.flow.server.frontend.FrontendTools;
import com.vaadin.flow.server.frontend.FrontendUtils;
import com.vaadin.flow.server.frontend.installer.NodeInstaller;

import elemental.json.Json;
Expand Down Expand Up @@ -389,6 +394,46 @@ public void noTokenFile_noTokenFileShouldBeCreated()
Assert.assertFalse(tokenFile.exists());
}

@Test
public void testWebpackRequiredFlagsPassedToNodeEnvironment()
throws IOException, MojoExecutionException {
Assume.assumeFalse("Test not runnable on Windows",
FrontendUtils.isWindows());
Assume.assumeTrue("Test requires /bin/bash",
new File("/bin/bash").exists());

File npmFolder = temporaryFolder.getRoot();

// setup: mock a webpack executable
File webpackBin = new File(npmFolder, "node_modules/webpack/bin");
Assert.assertTrue(webpackBin.mkdirs());
File webPackExecutableMock = new File(webpackBin, "webpack.js");
Assert.assertTrue(webPackExecutableMock.createNewFile());

FrontendTools tools = Mockito.mock(FrontendTools.class);

// given: "node" stub that exits normally only if expected environment
// set
File fakeNode = new File(npmFolder, "node");
try (PrintWriter out = new PrintWriter(fakeNode)) {
out.println("#!/bin/bash");
out.println("[ x$NODE_OPTIONS == xexpected ]");
out.println("exit $?");
}
Assert.assertTrue(fakeNode.setExecutable(true));
Mockito.when(tools.getNodeExecutable())
.thenReturn(fakeNode.getAbsolutePath());

Map<String, String> environment = new HashMap<>();
environment.put("NODE_OPTIONS", "expected");
Mockito.when(tools.getWebpackNodeEnvironment()).thenReturn(environment);

// then
mojo.runWebpack(tools);

// terminates successfully
}

static void assertContainsPackage(JsonObject dependencies,
String... packages) {
Arrays.asList(packages).forEach(dep -> Assert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -637,12 +637,7 @@ private boolean doStartWebpack(DeploymentConfiguration config,
() -> FrontendUtils.getVaadinHomeDirectory().getAbsolutePath(),
nodeVersion, URI.create(nodeDownloadRoot), useHomeNodeExec);
tools.validateNodeAndNpmVersion();

if (requiresOpenSslLegacyProvider(npmFolder,
tools.getNodeExecutable())) {
processBuilder.environment().put("NODE_OPTIONS",
"--openssl-legacy-provider");
}
processBuilder.environment().putAll(tools.getWebpackNodeEnvironment());

String nodeExec = null;
if (useHomeNodeExec) {
Expand Down Expand Up @@ -882,34 +877,6 @@ void join() {
devServerStartFuture.join();
}

private boolean requiresOpenSslLegacyProvider(File baseDir, String nodeExec) {
// Determine whether webpack requires Node.js to be started with the
// --openssl-legacy-provider parameter. This is a webpack 4 workaround
// of the issue https://github.com/webpack/webpack/issues/14532
// See: https://github.com/vaadin/flow/issues/12649
ProcessBuilder processBuilder = new ProcessBuilder()
.directory(baseDir)
.command(nodeExec, "-p", "crypto.createHash('md4')");
try {
Process process = processBuilder.start();
int errorLevel = process.waitFor();
return errorLevel != 0;
} catch (IOException e) {
getLogger().error(
"IO error while determining --openssl-legacy-provider "
+ "parameter requirement",
e);
} catch (InterruptedException e) {
getLogger().error(
"Interrupted while determining --openssl-legacy-provider "
+ "parameter requirement",
e);
// re-interrupt the thread
Thread.currentThread().interrupt();
}
return false;
}

private static final class LazyDevServerPortFileInit {

private static final File DEV_SERVER_PORT_FILE = createDevServerPortFile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
Expand Down Expand Up @@ -571,6 +573,41 @@ private FrontendVersion getNpmVersion() throws UnknownVersionException {
return FrontendUtils.getVersion("npm", npmVersionCommand);
}

/**
* Returns flags required to pass to Node for Webpack to function. Determine
* whether webpack requires Node.js to be started with the
* --openssl-legacy-provider parameter. This is a webpack 4 workaround of
* the issue https://github.com/webpack/webpack/issues/14532 See:
* https://github.com/vaadin/flow/issues/12649
*
* @return the flags
*/
public Map<String, String> getWebpackNodeEnvironment() {
Map<String, String> environment = new HashMap<>();
ProcessBuilder processBuilder = new ProcessBuilder()
.command(getNodeExecutable(), "-p", "crypto.createHash('md4')");
try {
Process process = processBuilder.start();
int errorLevel = process.waitFor();
if (errorLevel != 0) {
environment.put("NODE_OPTIONS", "--openssl-legacy-provider");
}
} catch (IOException e) {
getLogger().error(
"IO error while determining --openssl-legacy-provider "
+ "parameter requirement",
e);
} catch (InterruptedException e) {
getLogger().error(
"Interrupted while determining --openssl-legacy-provider "
+ "parameter requirement",
e);
// re-interrupt the thread
Thread.currentThread().interrupt();
}
return environment;
}

private File getExecutable(String dir, String location) {
File file = new File(dir, location);
if (frontendToolsLocator.verifyTool(file)) {
Expand Down