diff --git a/plugin-modernizer-cli/src/main/java/io/jenkins/tools/pluginmodernizer/cli/Main.java b/plugin-modernizer-cli/src/main/java/io/jenkins/tools/pluginmodernizer/cli/Main.java index 362cab7b..f4e3570f 100644 --- a/plugin-modernizer-cli/src/main/java/io/jenkins/tools/pluginmodernizer/cli/Main.java +++ b/plugin-modernizer-cli/src/main/java/io/jenkins/tools/pluginmodernizer/cli/Main.java @@ -2,6 +2,8 @@ import java.io.InputStream; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import com.fasterxml.jackson.core.type.TypeReference; @@ -10,6 +12,7 @@ import io.jenkins.tools.pluginmodernizer.core.config.Settings; import io.jenkins.tools.pluginmodernizer.core.impl.PluginModernizer; import io.jenkins.tools.pluginmodernizer.core.model.RecipeDescriptor; +import io.jenkins.tools.pluginmodernizer.core.utils.PluginListParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; @@ -31,13 +34,16 @@ public static void main(final String[] args) { new CommandLine(new Main()).setOptionsCaseInsensitive(true).execute(args); } - @Option(names = {"-p", "--plugins"}, required = true, description = "List of Plugins to Modernize.", split = ",", parameterConsumer = CommaSeparatedParameterConsumer.class) + @Option(names = {"-p", "--plugins"}, description = "List of Plugins to Modernize.", split = ",", parameterConsumer = CommaSeparatedParameterConsumer.class) private List plugins; @Option(names = {"-r", "--recipes"}, required = true, description = "List of Recipes to be applied.", split = ",", parameterConsumer = CommaSeparatedParameterConsumer.class) private List recipes; - @Option(names = {"-g", "--github-owner"}, description = "GitHub owner for forked repositories (only username supported for now)") + @Option(names = {"-f", "--plugin-file"}, description = "Path to the file that contains a list of plugins") + private Path pluginFile; + + @Option(names = {"-g", "--github-owner"}, description = "GitHub owner for forked repositories") private String githubOwner = Settings.GITHUB_OWNER; @Option(names = {"-n", "--dry-run"}, description = "Perform a dry run without making any changes.") @@ -98,8 +104,25 @@ public void listAvailableRecipes() { } } + private List loadPlugins() { + List loadedPlugins = new ArrayList<>(); + + if (pluginFile != null) { + List pluginsFromFile = PluginListParser.loadPluginsFromFile(pluginFile); + loadedPlugins.addAll(pluginsFromFile); + } + + if (plugins != null) { + loadedPlugins.addAll(plugins); + } + + return new ArrayList<>(new HashSet<>(loadedPlugins)); + } + @Override public void run() { + plugins = loadPlugins(); + if (listRecipes) { listAvailableRecipes(); return; diff --git a/plugin-modernizer-cli/src/test/java/io/jenkins/tools/pluginmodernizer/cli/MainTest.java b/plugin-modernizer-cli/src/test/java/io/jenkins/tools/pluginmodernizer/cli/MainTest.java index 374a8321..7aa43e64 100644 --- a/plugin-modernizer-cli/src/test/java/io/jenkins/tools/pluginmodernizer/cli/MainTest.java +++ b/plugin-modernizer-cli/src/test/java/io/jenkins/tools/pluginmodernizer/cli/MainTest.java @@ -9,12 +9,14 @@ import java.io.IOException; import java.io.PrintStream; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import io.jenkins.tools.pluginmodernizer.core.config.Config; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import picocli.CommandLine; public class MainTest { @@ -23,6 +25,9 @@ public class MainTest { private ByteArrayOutputStream outputStream; private Main main; + @TempDir + Path tempDir; + @BeforeEach public void setUp() { main = new Main(); @@ -56,17 +61,54 @@ public void testGetRecipes() { } @Test - public void testMissingPluginsArgument() { - String[] args = {"-r", "recipe1,recipe2"}; + public void testMissingRecipesArgument() { + String[] args = {"-p", "plugin1,plugin2"}; int exitCode = commandLine.execute(args); assertEquals(CommandLine.ExitCode.USAGE, exitCode); } @Test - public void testMissingRecipesArgument() { - String[] args = {"-p", "plugin1,plugin2"}; - int exitCode = commandLine.execute(args); - assertEquals(CommandLine.ExitCode.USAGE, exitCode); + public void testPluginFile() throws IOException { + Path pluginFile = tempDir.resolve("plugins.txt"); + Files.write(pluginFile, List.of("plugin1", "", "plugin2", " ", "plugin3")); + String[] args = {"-f", pluginFile.toString(), "-r", "recipe1,recipe2"}; + commandLine.execute(args); + List plugins = main.setup().getPlugins(); + assertNotNull(plugins); + assertEquals(3, plugins.size()); + assertTrue(plugins.contains("plugin1")); + assertTrue(plugins.contains("plugin2")); + assertTrue(plugins.contains("plugin3")); + } + + @Test + public void testPluginFileAlongWithPluginOptionWithoutCommonPlugins() throws IOException { + Path pluginFile = tempDir.resolve("plugins.txt"); + Files.write(pluginFile, List.of("plugin1", "", "plugin2", " ", "plugin3")); + String[] args = {"-f", pluginFile.toString(), "-r", "recipe1,recipe2", "-p", "plugin4,plugin5"}; + commandLine.execute(args); + List plugins = main.setup().getPlugins(); + assertNotNull(plugins); + assertEquals(5, plugins.size()); + assertTrue(plugins.contains("plugin1")); + assertTrue(plugins.contains("plugin2")); + assertTrue(plugins.contains("plugin3")); + assertTrue(plugins.contains("plugin4")); + assertTrue(plugins.contains("plugin5")); + } + + @Test + public void testPluginFileAlongWithPluginOptionWithCommonPlugins() throws IOException { + Path pluginFile = tempDir.resolve("plugins.txt"); + Files.write(pluginFile, List.of("plugin1", "", "plugin2", " ", "plugin3")); + String[] args = {"-f", pluginFile.toString(), "-r", "recipe1,recipe2", "-p", "plugin2,plugin3"}; + commandLine.execute(args); + List plugins = main.setup().getPlugins(); + assertNotNull(plugins); + assertEquals(3, plugins.size()); + assertTrue(plugins.contains("plugin1")); + assertTrue(plugins.contains("plugin2")); + assertTrue(plugins.contains("plugin3")); } @Test diff --git a/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParser.java b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParser.java new file mode 100644 index 00000000..dd13c5cc --- /dev/null +++ b/plugin-modernizer-core/src/main/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParser.java @@ -0,0 +1,27 @@ +package io.jenkins.tools.pluginmodernizer.core.utils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PluginListParser { + + private static final Logger LOG = LoggerFactory.getLogger(PluginListParser.class); + + public static List loadPluginsFromFile(Path pluginFile) { + try (Stream lines = Files.lines(pluginFile)) { + return lines.filter(line -> !line.trim().isEmpty()) + .map(line -> line.split(":")[0]) + .collect(Collectors.toList()); + } catch (IOException e) { + LOG.error("Error reading plugins from file: {}", e.getMessage()); + return null; + } + } +} diff --git a/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParserTest.java b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParserTest.java new file mode 100644 index 00000000..b904de0e --- /dev/null +++ b/plugin-modernizer-core/src/test/java/io/jenkins/tools/pluginmodernizer/core/utils/PluginListParserTest.java @@ -0,0 +1,67 @@ +package io.jenkins.tools.pluginmodernizer.core.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class PluginListParserTest { + + @TempDir + Path tempDir; + + @Test + public void testLoadPluginsFromFileWithEmptyLines() throws IOException { + Path pluginFile = tempDir.resolve("plugins.txt"); + Files.write(pluginFile, List.of("plugin1", "", "plugin2", " ", "plugin3")); + + List plugins = PluginListParser.loadPluginsFromFile(pluginFile); + + assertNotNull(plugins); + assertEquals(3, plugins.size()); + assertTrue(plugins.contains("plugin1")); + assertTrue(plugins.contains("plugin2")); + assertTrue(plugins.contains("plugin3")); + } + + @Test + public void testLoadPluginsFromFileEmptyFile() throws IOException { + Path pluginFile = tempDir.resolve("plugins.txt"); + Files.createFile(pluginFile); + + List plugins = PluginListParser.loadPluginsFromFile(pluginFile); + assertNotNull(plugins); + assertTrue(plugins.isEmpty()); + } + + @Test + public void testLoadPluginsFromFileFileNotFound() { + Path pluginFile = tempDir.resolve("nonexistent.txt"); + + List plugins = PluginListParser.loadPluginsFromFile(pluginFile); + + assertNull(plugins); + } + + @Test + public void testLoadPluginsFromResourceFile() { + Path resourceFilePath = Path.of("src", "test", "resources", "plugins.txt"); + + List plugins = PluginListParser.loadPluginsFromFile(resourceFilePath); + + assertNotNull(plugins); + assertEquals(4, plugins.size()); + assertTrue(plugins.contains("jobcacher")); + assertTrue(plugins.contains("login-theme")); + assertTrue(plugins.contains("next-executions")); + assertTrue(plugins.contains("cloudbees-bitbucket-branch-source")); + } +} diff --git a/plugin-modernizer-core/src/test/resources/plugins.txt b/plugin-modernizer-core/src/test/resources/plugins.txt new file mode 100644 index 00000000..38ef6141 --- /dev/null +++ b/plugin-modernizer-core/src/test/resources/plugins.txt @@ -0,0 +1,4 @@ +jobcacher +login-theme +next-executions:1.0.0 +cloudbees-bitbucket-branch-source:2.4.4