From 66e04f0b7d730443cef0b6f2a473867977c6290f Mon Sep 17 00:00:00 2001 From: Markus Heberling Date: Fri, 17 Sep 2021 20:52:45 +0200 Subject: [PATCH 1/3] support installing node version from package.json engines fixes #798 --- .../it/node-version-from-engines/package.json | 13 ++++ .../src/it/node-version-from-engines/pom.xml | 49 ++++++++++++ .../node-version-from-engines/verify.groovy | 9 +++ .../frontend/mojo/InstallNodeAndNpmMojo.java | 8 +- frontend-plugin-core/pom.xml | 6 ++ .../plugins/frontend/lib/NodeInstaller.java | 77 +++++++++++++++++-- 6 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 frontend-maven-plugin/src/it/node-version-from-engines/package.json create mode 100644 frontend-maven-plugin/src/it/node-version-from-engines/pom.xml create mode 100644 frontend-maven-plugin/src/it/node-version-from-engines/verify.groovy diff --git a/frontend-maven-plugin/src/it/node-version-from-engines/package.json b/frontend-maven-plugin/src/it/node-version-from-engines/package.json new file mode 100644 index 000000000..1f129e6f9 --- /dev/null +++ b/frontend-maven-plugin/src/it/node-version-from-engines/package.json @@ -0,0 +1,13 @@ +{ + "name": "example", + "version": "0.0.1", + "engines": { + "node": ">=10.3 <15" + }, + "dependencies": { + "less": "~3.0.2" + }, + "scripts": { + "prebuild": "npm install" + } +} diff --git a/frontend-maven-plugin/src/it/node-version-from-engines/pom.xml b/frontend-maven-plugin/src/it/node-version-from-engines/pom.xml new file mode 100644 index 000000000..fc8490857 --- /dev/null +++ b/frontend-maven-plugin/src/it/node-version-from-engines/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + com.github.eirslett + example + 0 + pom + + + + + com.github.eirslett + frontend-maven-plugin + + @project.version@ + + + target + + + + + + install node and npm + + install-node-and-npm + + + engines + + + + + npm install + + npm + + + + install + + + + + + + + diff --git a/frontend-maven-plugin/src/it/node-version-from-engines/verify.groovy b/frontend-maven-plugin/src/it/node-version-from-engines/verify.groovy new file mode 100644 index 000000000..0a9d23ea4 --- /dev/null +++ b/frontend-maven-plugin/src/it/node-version-from-engines/verify.groovy @@ -0,0 +1,9 @@ +assert new File(basedir, 'target/node').exists() : "Node was not installed in the custom install directory"; +assert new File(basedir, 'node_modules').exists() : "Node modules were not installed in the base directory"; +assert new File(basedir, 'target/node/npm').exists() : "npm was not copied to the node directory"; + +import org.codehaus.plexus.util.FileUtils; + +String buildLog = FileUtils.fileRead(new File(basedir, 'build.log')); + +assert buildLog.contains('BUILD SUCCESS') : 'build was not successful' diff --git a/frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndNpmMojo.java b/frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndNpmMojo.java index c80b3c0a5..c163ff42e 100644 --- a/frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndNpmMojo.java +++ b/frontend-maven-plugin/src/main/java/com/github/eirslett/maven/plugins/frontend/mojo/InstallNodeAndNpmMojo.java @@ -78,7 +78,7 @@ public void execute(FrontendPluginFactory factory) throws InstallationException String npmDownloadRoot = getNpmDownloadRoot(); Server server = MojoUtils.decryptServer(serverId, session, decrypter); if (null != server) { - factory.getNodeInstaller(proxyConfig) + String installedNodeVersion=factory.getNodeInstaller(proxyConfig) .setNodeVersion(nodeVersion) .setNodeDownloadRoot(nodeDownloadRoot) .setNpmVersion(npmVersion) @@ -86,20 +86,20 @@ public void execute(FrontendPluginFactory factory) throws InstallationException .setPassword(server.getPassword()) .install(); factory.getNPMInstaller(proxyConfig) - .setNodeVersion(nodeVersion) + .setNodeVersion(installedNodeVersion) .setNpmVersion(npmVersion) .setNpmDownloadRoot(npmDownloadRoot) .setUserName(server.getUsername()) .setPassword(server.getPassword()) .install(); } else { - factory.getNodeInstaller(proxyConfig) + String installedNodeVersion=factory.getNodeInstaller(proxyConfig) .setNodeVersion(nodeVersion) .setNodeDownloadRoot(nodeDownloadRoot) .setNpmVersion(npmVersion) .install(); factory.getNPMInstaller(proxyConfig) - .setNodeVersion(this.nodeVersion) + .setNodeVersion(installedNodeVersion) .setNpmVersion(this.npmVersion) .setNpmDownloadRoot(npmDownloadRoot) .install(); diff --git a/frontend-plugin-core/pom.xml b/frontend-plugin-core/pom.xml index 6cf18238c..f3f7a0144 100644 --- a/frontend-plugin-core/pom.xml +++ b/frontend-plugin-core/pom.xml @@ -11,6 +11,12 @@ jar + + com.vdurmont + semver4j + 3.1.0 + + com.fasterxml.jackson.core jackson-core diff --git a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeInstaller.java b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeInstaller.java index 5b5c68190..5019b3a12 100644 --- a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeInstaller.java +++ b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeInstaller.java @@ -7,7 +7,13 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.Arrays; - +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vdurmont.semver4j.Requirement; +import com.vdurmont.semver4j.Semver; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +34,8 @@ public class NodeInstaller { private final FileDownloader fileDownloader; + private Requirement nodeVersionRequirement; + NodeInstaller(InstallConfig config, ArchiveExtractor archiveExtractor, FileDownloader fileDownloader) { this.logger = LoggerFactory.getLogger(getClass()); this.config = config; @@ -74,13 +82,65 @@ private boolean npmProvided() throws InstallationException { return false; } - public void install() throws InstallationException { + public String install() throws InstallationException { // use static lock object for a synchronized block synchronized (LOCK) { if (this.nodeDownloadRoot == null || this.nodeDownloadRoot.isEmpty()) { this.nodeDownloadRoot = this.config.getPlatform().getNodeDownloadRoot(); } + + if ("engines".equals(this.nodeVersion)) { + try { + File packageFile = new File(this.config.getWorkingDirectory(), "package.json"); + HashMap data = new ObjectMapper().readValue(packageFile, HashMap.class); + if (data.containsKey("engines")) { + HashMap engines = (HashMap) data.get("engines"); + if (engines.containsKey("node")) { + this.nodeVersionRequirement = Requirement.buildNPM((String) engines.get("node")); + } else { + this.logger.info("Could not read node from engines from package.json"); + } + } else { + this.logger.info("Could not read engines from package.json"); + } + } catch (IOException e) { + throw new InstallationException("Could not read node engine version from package.json", e); + } + } + if (!nodeIsAlreadyInstalled()) { + if (this.nodeVersionRequirement != null) { + // download available node versions + try { + String downloadUrl = this.nodeDownloadRoot + + "index.json"; + + File tmpDirectory = getTempDirectory(); + + File archive = File.createTempFile("node_versions", ".json", tmpDirectory); + + downloadFile(downloadUrl, archive, this.userName, this.password); + + HashMap[] data = new ObjectMapper().readValue(archive, HashMap[].class); + + List nodeVersions = new LinkedList<>(); + for (HashMap d : data) { + if (d.containsKey("version")) { + nodeVersions.add((String) d.get("version")); + } + } + + // we want the oldest possible version, that satisfies the requirements + Collections.reverse(nodeVersions); + + logger.debug("Available node versions: {}", nodeVersions); + this.nodeVersion = nodeVersions.stream().filter(version -> nodeVersionRequirement.isSatisfiedBy(new Semver(version, Semver.SemverType.NPM))).findFirst().orElseThrow(() -> new InstallationException("Could not find matching node version satisfying requirement " + this.nodeVersionRequirement)); + this.logger.info("Found matching node version {} satisfying requirement {}.", this.nodeVersion, this.nodeVersionRequirement); + } catch (IOException | DownloadException e) { + throw new InstallationException("Could not get available node versions.", e); + } + } + this.logger.info("Installing node version {}", this.nodeVersion); if (!this.nodeVersion.startsWith("v")) { this.logger.warn("Node version does not start with naming convention 'v'."); @@ -96,6 +156,8 @@ public void install() throws InstallationException { } } } + + return nodeVersion; } private boolean nodeIsAlreadyInstalled() { @@ -104,14 +166,19 @@ private boolean nodeIsAlreadyInstalled() { File nodeFile = executorConfig.getNodePath(); if (nodeFile.exists()) { final String version = - new NodeExecutor(executorConfig, Arrays.asList("--version"), null).executeAndGetResult(logger); + new NodeExecutor(executorConfig, Arrays.asList("--version"), null).executeAndGetResult(logger); - if (version.equals(this.nodeVersion)) { + if (nodeVersionRequirement != null && nodeVersionRequirement.isSatisfiedBy(new Semver(version, Semver.SemverType.NPM))) { + //update version with installed version + this.nodeVersion = version; + this.logger.info("Node {} matches required version range {} installed.", version, nodeVersionRequirement); + return true; + } else if (version.equals(this.nodeVersion)) { this.logger.info("Node {} is already installed.", version); return true; } else { this.logger.info("Node {} was installed, but we need version {}", version, - this.nodeVersion); + this.nodeVersion); return false; } } else { From 8b1d20e92b71f816663e754fc7f98462a20056b2 Mon Sep 17 00:00:00 2001 From: Markus Heberling Date: Sun, 19 Sep 2021 11:47:25 +0200 Subject: [PATCH 2/3] support installing npm version from package.json engines fixes #798 --- .../it/npm-version-from-engines/package.json | 13 ++++ .../src/it/npm-version-from-engines/pom.xml | 50 ++++++++++++++++ .../it/npm-version-from-engines/verify.groovy | 9 +++ .../plugins/frontend/lib/NPMInstaller.java | 59 ++++++++++++++++++- 4 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 frontend-maven-plugin/src/it/npm-version-from-engines/package.json create mode 100644 frontend-maven-plugin/src/it/npm-version-from-engines/pom.xml create mode 100644 frontend-maven-plugin/src/it/npm-version-from-engines/verify.groovy diff --git a/frontend-maven-plugin/src/it/npm-version-from-engines/package.json b/frontend-maven-plugin/src/it/npm-version-from-engines/package.json new file mode 100644 index 000000000..bd375490d --- /dev/null +++ b/frontend-maven-plugin/src/it/npm-version-from-engines/package.json @@ -0,0 +1,13 @@ +{ + "name": "example", + "version": "0.0.1", + "engines": { + "npm": ">=7 <8" + }, + "dependencies": { + "less": "~3.0.2" + }, + "scripts": { + "prebuild": "npm install" + } +} diff --git a/frontend-maven-plugin/src/it/npm-version-from-engines/pom.xml b/frontend-maven-plugin/src/it/npm-version-from-engines/pom.xml new file mode 100644 index 000000000..8c1a8fd9b --- /dev/null +++ b/frontend-maven-plugin/src/it/npm-version-from-engines/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.github.eirslett + example + 0 + pom + + + + + com.github.eirslett + frontend-maven-plugin + + @project.version@ + + + target + + + + + + install node and npm + + install-node-and-npm + + + v16.0.0 + engines + + + + + npm install + + npm + + + + install + + + + + + + + diff --git a/frontend-maven-plugin/src/it/npm-version-from-engines/verify.groovy b/frontend-maven-plugin/src/it/npm-version-from-engines/verify.groovy new file mode 100644 index 000000000..0a9d23ea4 --- /dev/null +++ b/frontend-maven-plugin/src/it/npm-version-from-engines/verify.groovy @@ -0,0 +1,9 @@ +assert new File(basedir, 'target/node').exists() : "Node was not installed in the custom install directory"; +assert new File(basedir, 'node_modules').exists() : "Node modules were not installed in the base directory"; +assert new File(basedir, 'target/node/npm').exists() : "npm was not copied to the node directory"; + +import org.codehaus.plexus.util.FileUtils; + +String buildLog = FileUtils.fileRead(new File(basedir, 'build.log')); + +assert buildLog.contains('BUILD SUCCESS') : 'build was not successful' diff --git a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NPMInstaller.java b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NPMInstaller.java index 9df7a255d..7cf5b4303 100644 --- a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NPMInstaller.java +++ b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NPMInstaller.java @@ -6,6 +6,10 @@ import java.io.IOException; import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import com.vdurmont.semver4j.Requirement; +import com.vdurmont.semver4j.Semver; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +32,8 @@ public class NPMInstaller { private final FileDownloader fileDownloader; + private Requirement npmVersionRequirement; + NPMInstaller(InstallConfig config, ArchiveExtractor archiveExtractor, FileDownloader fileDownloader) { this.logger = LoggerFactory.getLogger(getClass()); this.config = config; @@ -78,7 +84,53 @@ public void install() throws InstallationException { if (this.npmDownloadRoot == null || this.npmDownloadRoot.isEmpty()) { this.npmDownloadRoot = DEFAULT_NPM_DOWNLOAD_ROOT; } + if ("engines".equals(this.npmVersion)) { + try { + File packageFile = new File(this.config.getWorkingDirectory(), "package.json"); + HashMap data = new ObjectMapper().readValue(packageFile, HashMap.class); + if (data.containsKey("engines")) { + HashMap engines = (HashMap) data.get("engines"); + if (engines.containsKey("npm")) { + this.npmVersionRequirement = Requirement.buildNPM((String) engines.get("npm")); + } else { + this.logger.info("Could not read npm from engines from package.json"); + } + } else { + this.logger.info("Could not read engines from package.json"); + } + } catch (IOException e) { + throw new InstallationException("Could not read npm engine version from package.json", e); + } + } + if (!npmProvided() && !npmIsAlreadyInstalled()) { + if (this.npmVersionRequirement != null) { + // download available node versions + try { + String downloadUrl = this.npmDownloadRoot + + ".."; + + File archive = File.createTempFile("npm_versions", ".json"); + + downloadFile(downloadUrl, archive, this.userName, this.password); + + HashMap data = new ObjectMapper().readValue(archive, HashMap.class); + + List npmVersions = new LinkedList<>(); + if (data.containsKey("versions")) { + HashMap versions = (HashMap) data.get("versions"); + npmVersions.addAll(versions.keySet()); + } else { + this.logger.info("Could not read versions from NPM registry"); + } + + logger.debug("Available NPM versions: {}", npmVersions); + this.npmVersion = npmVersions.stream().filter(version -> npmVersionRequirement.isSatisfiedBy(new Semver(version, Semver.SemverType.NPM))).findFirst().orElseThrow(() -> new InstallationException("Could not find matching node version satisfying requirement " + this.npmVersionRequirement)); + this.logger.info("Found matching NPM version {} satisfying requirement {}.", this.npmVersion, this.npmVersionRequirement); + } catch (IOException | DownloadException e) { + throw new InstallationException("Could not get available node versions.", e); + } + } installNpm(); } copyNpmScripts(); @@ -93,7 +145,12 @@ private boolean npmIsAlreadyInstalled() { HashMap data = new ObjectMapper().readValue(npmPackageJson, HashMap.class); if (data.containsKey(VERSION)) { final String foundNpmVersion = data.get(VERSION).toString(); - if (foundNpmVersion.equals(this.npmVersion)) { + if (npmVersionRequirement != null && npmVersionRequirement.isSatisfiedBy(new Semver(foundNpmVersion, Semver.SemverType.NPM))) { + //update version with installed version + this.nodeVersion = foundNpmVersion; + this.logger.info("NPM {} matches required version range {} installed.", foundNpmVersion, npmVersionRequirement); + return true; + } else if (foundNpmVersion.equals(this.npmVersion)) { this.logger.info("NPM {} is already installed.", foundNpmVersion); return true; } else { From f5c88ad67f312a74aa9ed640cb7c39885d055a46 Mon Sep 17 00:00:00 2001 From: Markus Heberling Date: Sun, 19 Sep 2021 15:11:33 +0200 Subject: [PATCH 3/3] support installing yarn version from package.json engines fixes #798 --- .../it/yarn-version-from-engines/package.json | 13 ++++ .../src/it/yarn-version-from-engines/pom.xml | 50 +++++++++++++++ .../yarn-version-from-engines/verify.groovy | 6 ++ .../plugins/frontend/lib/YarnInstaller.java | 64 ++++++++++++++++++- 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 frontend-maven-plugin/src/it/yarn-version-from-engines/package.json create mode 100644 frontend-maven-plugin/src/it/yarn-version-from-engines/pom.xml create mode 100644 frontend-maven-plugin/src/it/yarn-version-from-engines/verify.groovy diff --git a/frontend-maven-plugin/src/it/yarn-version-from-engines/package.json b/frontend-maven-plugin/src/it/yarn-version-from-engines/package.json new file mode 100644 index 000000000..e3ec43389 --- /dev/null +++ b/frontend-maven-plugin/src/it/yarn-version-from-engines/package.json @@ -0,0 +1,13 @@ +{ + "name": "example", + "version": "0.0.1", + "engines": { + "yarn": "^1.10" + }, + "dependencies": { + "less": "~3.0.2" + }, + "scripts": { + "prebuild": "npm install" + } +} diff --git a/frontend-maven-plugin/src/it/yarn-version-from-engines/pom.xml b/frontend-maven-plugin/src/it/yarn-version-from-engines/pom.xml new file mode 100644 index 000000000..7e72c4c39 --- /dev/null +++ b/frontend-maven-plugin/src/it/yarn-version-from-engines/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.github.eirslett + example + 0 + pom + + + + + com.github.eirslett + frontend-maven-plugin + + @project.version@ + + + target + + + + + + install node and yarn + + install-node-and-yarn + + + v16.0.0 + engines + + + + + yarn install + + yarn + + + + install + + + + + + + + diff --git a/frontend-maven-plugin/src/it/yarn-version-from-engines/verify.groovy b/frontend-maven-plugin/src/it/yarn-version-from-engines/verify.groovy new file mode 100644 index 000000000..1355cd8ec --- /dev/null +++ b/frontend-maven-plugin/src/it/yarn-version-from-engines/verify.groovy @@ -0,0 +1,6 @@ +assert new File(basedir, 'target/node').exists() : "Node was not installed in the custom install directory"; +assert new File(basedir, 'node_modules').exists() : "Node modules were not installed in the base directory"; +assert new File(basedir, 'node_modules/less/package.json').exists() : "Less dependency has not been installed successfully"; + +String buildLog = new File(basedir, 'build.log').text +assert buildLog.contains('BUILD SUCCESS') : 'build was not successful' diff --git a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/YarnInstaller.java b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/YarnInstaller.java index b7445d99e..1ae5a6f91 100644 --- a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/YarnInstaller.java +++ b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/YarnInstaller.java @@ -5,7 +5,14 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; - +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vdurmont.semver4j.Requirement; +import com.vdurmont.semver4j.Semver; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +40,8 @@ public class YarnInstaller { private final FileDownloader fileDownloader; + private Requirement yarnVersionRequirement; + YarnInstaller(InstallConfig config, ArchiveExtractor archiveExtractor, FileDownloader fileDownloader) { logger = LoggerFactory.getLogger(getClass()); this.config = config; @@ -71,7 +80,53 @@ public void install() throws InstallationException { if (yarnDownloadRoot == null || yarnDownloadRoot.isEmpty()) { yarnDownloadRoot = DEFAULT_YARN_DOWNLOAD_ROOT; } + if ("engines".equals(this.yarnVersion)) { + try { + File packageFile = new File(this.config.getWorkingDirectory(), "package.json"); + HashMap data = new ObjectMapper().readValue(packageFile, HashMap.class); + if (data.containsKey("engines")) { + HashMap engines = (HashMap) data.get("engines"); + if (engines.containsKey("yarn")) { + this.yarnVersionRequirement = Requirement.buildNPM((String) engines.get("yarn")); + } else { + this.logger.info("Could not read yarn from engines from package.json"); + } + } else { + this.logger.info("Could not read engines from package.json"); + } + } catch (IOException e) { + throw new InstallationException("Could not read yarn engine version from package.json", e); + } + } if (!yarnIsAlreadyInstalled()) { + if (this.yarnVersionRequirement != null) { + // download available node versions + try { + String downloadUrl = "https://api.github.com/repos/yarnpkg/yarn/releases"; + + File archive = File.createTempFile("yarn_versions", ".json"); + + downloadFile(downloadUrl, archive, this.userName, this.password); + + HashMap[] data = new ObjectMapper().readValue(archive, HashMap[].class); + + List yarnVersions = new LinkedList<>(); + for (HashMap d : data) { + if (d.containsKey("name")) { + yarnVersions.add((String) d.get("name")); + } + } + + // we want the oldest possible version, that satisfies the requirements + Collections.reverse(yarnVersions); + + logger.debug("Available Yarn versions: {}", yarnVersions); + this.yarnVersion = yarnVersions.stream().filter(version -> yarnVersionRequirement.isSatisfiedBy(new Semver(version, Semver.SemverType.NPM))).findFirst().orElseThrow(() -> new InstallationException("Could not find matching node version satisfying requirement " + this.yarnVersionRequirement)); + this.logger.info("Found matching Yarn version {} satisfying requirement {}.", this.yarnVersion, this.yarnVersionRequirement); + } catch (IOException | DownloadException e) { + throw new InstallationException("Could not get available Yarn versions.", e); + } + } if (!yarnVersion.startsWith("v")) { throw new InstallationException("Yarn version has to start with prefix 'v'."); } @@ -88,7 +143,12 @@ private boolean yarnIsAlreadyInstalled() { final String version = new YarnExecutor(executorConfig, Arrays.asList("--version"), null).executeAndGetResult(logger).trim(); - if (version.equals(yarnVersion.replaceFirst("^v", ""))) { + if (yarnVersionRequirement != null && yarnVersionRequirement.isSatisfiedBy(new Semver(version, Semver.SemverType.NPM))) { + //update version with installed version + this.yarnVersion = version; + this.logger.info("Yarn {} matches required version range {} installed.", version, yarnVersionRequirement); + return true; + } else if (version.equals(yarnVersion.replaceFirst("^v", ""))) { logger.info("Yarn {} is already installed.", version); return true; } else {