From fa66d87f75650acb406ade13db83538375025df6 Mon Sep 17 00:00:00 2001 From: Michael Kemp Date: Fri, 20 Sep 2024 16:57:16 +0200 Subject: [PATCH] DCA11Y-1145: Add support for mise config files --- frontend-maven-plugin/pom.xml | 6 ++ .../src/it/mise-config-file/.mise.toml | 10 ++ .../src/it/mise-config-file/package-lock.json | 12 +++ .../src/it/mise-config-file/package.json | 4 + .../src/it/mise-config-file/pom.xml | 34 +++++++ .../src/it/mise-config-file/verify.groovy | 5 + .../TESTDIR/mise.TESTPROFILE.toml | 10 ++ .../it/mise-env-config-file/package-lock.json | 12 +++ .../src/it/mise-env-config-file/package.json | 4 + .../src/it/mise-env-config-file/pom.xml | 34 +++++++ .../src/it/mise-env-config-file/verify.groovy | 5 + frontend-plugin-core/pom.xml | 6 ++ .../frontend/lib/NodeVersionDetector.java | 99 ++++++++++++++++++- .../frontend/lib/NodeVersionDetectorTest.java | 44 +++++++++ 14 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 frontend-maven-plugin/src/it/mise-config-file/.mise.toml create mode 100644 frontend-maven-plugin/src/it/mise-config-file/package-lock.json create mode 100644 frontend-maven-plugin/src/it/mise-config-file/package.json create mode 100644 frontend-maven-plugin/src/it/mise-config-file/pom.xml create mode 100644 frontend-maven-plugin/src/it/mise-config-file/verify.groovy create mode 100644 frontend-maven-plugin/src/it/mise-env-config-file/TESTDIR/mise.TESTPROFILE.toml create mode 100644 frontend-maven-plugin/src/it/mise-env-config-file/package-lock.json create mode 100644 frontend-maven-plugin/src/it/mise-env-config-file/package.json create mode 100644 frontend-maven-plugin/src/it/mise-env-config-file/pom.xml create mode 100644 frontend-maven-plugin/src/it/mise-env-config-file/verify.groovy diff --git a/frontend-maven-plugin/pom.xml b/frontend-maven-plugin/pom.xml index df38f20db..6a48535b4 100644 --- a/frontend-maven-plugin/pom.xml +++ b/frontend-maven-plugin/pom.xml @@ -138,6 +138,12 @@ run verify + + + TESTDIR + TESTPROFILE + + diff --git a/frontend-maven-plugin/src/it/mise-config-file/.mise.toml b/frontend-maven-plugin/src/it/mise-config-file/.mise.toml new file mode 100644 index 000000000..1cd6234fa --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-config-file/.mise.toml @@ -0,0 +1,10 @@ +[tools] +java = "temurin-21" +maven = "3.9" +pre-commit = "latest" +ktlint = "latest" +python = "3.12" +# node 22.5.1 +node = "22.5.1" +# node 22.5.1 +yarn = "1.22.22" diff --git a/frontend-maven-plugin/src/it/mise-config-file/package-lock.json b/frontend-maven-plugin/src/it/mise-config-file/package-lock.json new file mode 100644 index 000000000..aa1c583f3 --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-config-file/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "example", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "example", + "version": "0.0.1" + } + } +} diff --git a/frontend-maven-plugin/src/it/mise-config-file/package.json b/frontend-maven-plugin/src/it/mise-config-file/package.json new file mode 100644 index 000000000..30551a8fe --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-config-file/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "version": "0.0.1" +} diff --git a/frontend-maven-plugin/src/it/mise-config-file/pom.xml b/frontend-maven-plugin/src/it/mise-config-file/pom.xml new file mode 100644 index 000000000..2dd31616a --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-config-file/pom.xml @@ -0,0 +1,34 @@ + + + 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 + + + + + + + diff --git a/frontend-maven-plugin/src/it/mise-config-file/verify.groovy b/frontend-maven-plugin/src/it/mise-config-file/verify.groovy new file mode 100644 index 000000000..b7a37416c --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-config-file/verify.groovy @@ -0,0 +1,5 @@ +assert new File(basedir, 'target/node').exists() : "Node was not installed in the custom install directory"; + +String buildLog = new File(basedir, 'build.log').text +assert buildLog.contains('.mise.toml') : 'The wrong file was used' +assert buildLog.contains('Installing node version v22.5.1') : 'The correct node version was not detected' diff --git a/frontend-maven-plugin/src/it/mise-env-config-file/TESTDIR/mise.TESTPROFILE.toml b/frontend-maven-plugin/src/it/mise-env-config-file/TESTDIR/mise.TESTPROFILE.toml new file mode 100644 index 000000000..1cd6234fa --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-env-config-file/TESTDIR/mise.TESTPROFILE.toml @@ -0,0 +1,10 @@ +[tools] +java = "temurin-21" +maven = "3.9" +pre-commit = "latest" +ktlint = "latest" +python = "3.12" +# node 22.5.1 +node = "22.5.1" +# node 22.5.1 +yarn = "1.22.22" diff --git a/frontend-maven-plugin/src/it/mise-env-config-file/package-lock.json b/frontend-maven-plugin/src/it/mise-env-config-file/package-lock.json new file mode 100644 index 000000000..aa1c583f3 --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-env-config-file/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "example", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "example", + "version": "0.0.1" + } + } +} diff --git a/frontend-maven-plugin/src/it/mise-env-config-file/package.json b/frontend-maven-plugin/src/it/mise-env-config-file/package.json new file mode 100644 index 000000000..30551a8fe --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-env-config-file/package.json @@ -0,0 +1,4 @@ +{ + "name": "example", + "version": "0.0.1" +} diff --git a/frontend-maven-plugin/src/it/mise-env-config-file/pom.xml b/frontend-maven-plugin/src/it/mise-env-config-file/pom.xml new file mode 100644 index 000000000..2dd31616a --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-env-config-file/pom.xml @@ -0,0 +1,34 @@ + + + 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 + + + + + + + diff --git a/frontend-maven-plugin/src/it/mise-env-config-file/verify.groovy b/frontend-maven-plugin/src/it/mise-env-config-file/verify.groovy new file mode 100644 index 000000000..46bea0a1e --- /dev/null +++ b/frontend-maven-plugin/src/it/mise-env-config-file/verify.groovy @@ -0,0 +1,5 @@ +assert new File(basedir, 'target/node').exists() : "Node was not installed in the custom install directory"; + +String buildLog = new File(basedir, 'build.log').text +assert buildLog.matches("TESTDIR[/\\]mise\\.TESTPROFILE\\.toml") : 'The wrong file was used' +assert buildLog.contains('Installing node version v22.5.1') : 'The correct node version was not detected' diff --git a/frontend-plugin-core/pom.xml b/frontend-plugin-core/pom.xml index 8eae8157a..f108e3b61 100644 --- a/frontend-plugin-core/pom.xml +++ b/frontend-plugin-core/pom.xml @@ -77,6 +77,12 @@ 33.1.0-jre provided + + com.github.stefanbirkner + system-lambda + 1.2.1 + test + diff --git a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetector.java b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetector.java index e6d1796d1..fd1cc161d 100644 --- a/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetector.java +++ b/frontend-plugin-core/src/main/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetector.java @@ -7,15 +7,18 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static java.lang.String.format; import static java.util.Objects.isNull; import static java.util.Optional.empty; import static org.slf4j.LoggerFactory.getLogger; public class NodeVersionDetector { + private static final List MISE_CONFIG_FILENAMES = listMiseConfigFilenames(); private static final String TOOL_VERSIONS_FILENAME = ".tool-versions"; public static String getNodeVersion(File workingDir, String providedNodeVersion, String genericNodeVersionFile) throws Exception { @@ -32,7 +35,9 @@ public static String getNodeVersion(File workingDir, String providedNodeVersion, throw new Exception("The Node version file doesn't seem to exist: " + genericNodeVersionFileFile); } - if (genericNodeVersionFile.endsWith(TOOL_VERSIONS_FILENAME)) { + if (genericNodeVersionFile.endsWith(".toml") && genericNodeVersionFile.contains("mise")) { + return readMiseConfigTomlFile(genericNodeVersionFileFile, genericNodeVersionFileFile.toPath(), logger); + } else if (genericNodeVersionFile.endsWith(TOOL_VERSIONS_FILENAME)) { return readToolVersionsFile(genericNodeVersionFileFile, genericNodeVersionFileFile.toPath(), logger); } else { return readNvmrcFile(genericNodeVersionFileFile, genericNodeVersionFileFile.toPath(), logger); @@ -48,6 +53,49 @@ public static String getNodeVersion(File workingDir, String providedNodeVersion, } } + /** + * Mise has way too many options, see: + * https://mise.jdx.dev/profiles.html + * https://mise.jdx.dev/configuration.html#mise-toml + */ + public static List listMiseConfigFilenames() { + final String miseConfigDir = System.getenv("MISE_CONFIG_DIR"); + final String miseEnv = System.getenv("MISE_ENV"); + + // The order is important and should respect mises' ordering + final List allMiseConfigFilenames = new ArrayList<>(); + + allMiseConfigFilenames.add(format("%s/config.%s.toml", miseConfigDir, miseEnv)); + allMiseConfigFilenames.add(format("%s/mise.%s.toml", miseConfigDir, miseEnv)); + + allMiseConfigFilenames.add(".config/mise/config.toml"); + allMiseConfigFilenames.add("mise/config.toml"); + allMiseConfigFilenames.add("mise.toml"); + allMiseConfigFilenames.add(".mise/config.toml"); + allMiseConfigFilenames.add(".mise.toml"); + allMiseConfigFilenames.add(".config/mise/config.local.toml"); + allMiseConfigFilenames.add("mise/config.local.toml"); + allMiseConfigFilenames.add("mise.local.toml"); + allMiseConfigFilenames.add(".mise/config.local.toml"); + allMiseConfigFilenames.add(".mise.local.toml"); + + allMiseConfigFilenames.add(format(".config/mise/config.%s.toml", miseEnv)); + allMiseConfigFilenames.add(format("mise/config.%s.toml", miseEnv)); + allMiseConfigFilenames.add(format("mise.%s.toml", miseEnv)); + allMiseConfigFilenames.add(format(".mise/config.%s.toml", miseEnv)); + allMiseConfigFilenames.add(format(".mise.%s.toml", miseEnv)); + allMiseConfigFilenames.add(format(".config/mise/config.%s.local.toml", miseEnv)); + allMiseConfigFilenames.add(format("mise/config.%s.local.toml", miseEnv)); + allMiseConfigFilenames.add(format(".mise/config.%s.local.toml", miseEnv)); + allMiseConfigFilenames.add(format(".mise.%s.local.toml", miseEnv)); + + return allMiseConfigFilenames; + } + + /** + * Ordering this hierarchy of reading the files isn't just the most idiomatic, it's also probably the best + * for performance. + */ public static String recursivelyFindVersion(File directory) throws Exception { Logger logger = getLogger(NodeVersionDetector.class); @@ -79,6 +127,20 @@ public static String recursivelyFindVersion(File directory) throws Exception { if (trimmedLine != null) return trimmedLine; } + for (String miseConfigFilename: MISE_CONFIG_FILENAMES) { + // We don't know if MISE_CONFIG_DIR can result in absolute or relative file paths, try to do our best + String[] splitMiseConfigFilename = miseConfigFilename.split("/"); + Path potentiallyAbsoluteFilepath = Paths.get("", splitMiseConfigFilename); + Path miseConfigFilePath = potentiallyAbsoluteFilepath.isAbsolute() ? + potentiallyAbsoluteFilepath : Paths.get(directoryPath, splitMiseConfigFilename); + + File miseConfigFile = miseConfigFilePath.toFile(); + if (miseConfigFile.exists()) { + String trimmedVersion = readMiseConfigTomlFile(miseConfigFile, miseConfigFilePath, logger); + if (trimmedVersion != null) return trimmedVersion; + } + } + File parent = directory.getParentFile(); if (isNull(parent) || directory.equals(parent)) { throw new Exception("Reach root-level without finding a suitable file"); @@ -131,6 +193,41 @@ static Optional readNvmrcFileLines(List lines) { return empty(); } + /** + * If this gets any more complicated we'll add a reader, not sure how strict mise is with the spec, we want to be + * at least as loose. + */ + @VisibleForTesting + static String readMiseConfigTomlFile(File miseTomlFile, Path miseTomlFilePath, Logger logger) throws Exception { + assertNodeVersionFileIsReadable(miseTomlFile); + + List lines = Files.readAllLines(miseTomlFilePath); + for (String line: lines) { + if (!isNull(line)) { + String trimmedLine = line.trim(); + + if (trimmedLine.isEmpty()) { + continue; + } + + if (!trimmedLine.startsWith("node")) { // naturally skips over comments + continue; + } + + logger.info("Found the version of Node in: " + miseTomlFilePath); + + if (trimmedLine.contains("[")) { + throw new Exception("mise file support is limited to a single version"); + } + + return trimmedLine + .replaceAll("node(js)?\\s*=\\s*", "") + .replaceAll("\"", ""); // destringify the version + } + } + return null; + } + private static String readToolVersionsFile(File toolVersionsFile, Path toolVersionsFilePath, Logger logger) throws Exception { assertNodeVersionFileIsReadable(toolVersionsFile); diff --git a/frontend-plugin-core/src/test/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetectorTest.java b/frontend-plugin-core/src/test/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetectorTest.java index bbbc1bcb1..8ae1270db 100644 --- a/frontend-plugin-core/src/test/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetectorTest.java +++ b/frontend-plugin-core/src/test/java/com/github/eirslett/maven/plugins/frontend/lib/NodeVersionDetectorTest.java @@ -1,8 +1,23 @@ package com.github.eirslett.maven.plugins.frontend.lib; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermissions; import static com.github.eirslett.maven.plugins.frontend.lib.NodeVersionDetector.readNvmrcFileLines; +import static com.github.eirslett.maven.plugins.frontend.lib.NodeVersionDetector.recursivelyFindVersion; +import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable; +import static java.lang.String.format; +import static java.nio.charset.Charset.defaultCharset; +import static java.nio.file.StandardOpenOption.WRITE; +import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute; +import static java.nio.file.attribute.PosixFilePermissions.fromString; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,4 +54,33 @@ public void testNvmrcFileParsing_shouldIgnoreEmptyLines() { "" )).get()); } + + @Test + public void testAbsoluteMiseConfigFilePath( + @TempDir File tempMiseConfigDir, + @TempDir File tempUnrelatedDir + ) throws Exception { + // setup + String expectedVersion = "9.8.7"; + String miseProfile = "testabsolute"; + + String tempMiseConfigDirAbsolutePath = tempMiseConfigDir.getAbsolutePath(); + String tempMiseConfigFilename = format("mise.%s.toml", miseProfile); + Path tempMiseConfigFilePath = Paths.get(tempMiseConfigDirAbsolutePath, tempMiseConfigFilename); + Path tempMiseConfigFile = Files.createFile(tempMiseConfigFilePath, asFileAttribute(fromString("777"))); + + // given + withEnvironmentVariable("MISE_CONFIG_DIR", tempMiseConfigDirAbsolutePath) + .and("MISE_ENV", miseProfile) + .execute(() -> { + String miseConfigFileContents = format("node = \"%s\"", expectedVersion); + Files.write(tempMiseConfigFile, singletonList(miseConfigFileContents), defaultCharset(), WRITE); + + // when + String readVersion = recursivelyFindVersion(tempUnrelatedDir); + + // then + assertEquals(expectedVersion, readVersion, "versions didn't match"); + }); + } }