diff --git a/src/main/java/me/itzg/helpers/modrinth/ModrinthCommand.java b/src/main/java/me/itzg/helpers/modrinth/ModrinthCommand.java index 81119d81..7bc8e0b2 100644 --- a/src/main/java/me/itzg/helpers/modrinth/ModrinthCommand.java +++ b/src/main/java/me/itzg/helpers/modrinth/ModrinthCommand.java @@ -9,6 +9,7 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -46,10 +47,17 @@ public class ModrinthCommand implements Callable { @Option( names = "--projects", - description = "Project ID or Slug. Prefix with loader: e.g. fabric:project-id", + description = "Project ID or Slug. Can be |," + + " :|," + + " :|:," + + " '@'" + + "%nExamples: fabric-api, fabric:fabric-api, fabric:fabric-api:0.76.1+1.19.2," + + " datapack:terralith, @/path/to/modrinth-mods.txt" + + "%nValid release types: release, beta, alpha" + + "%nValid loaders: fabric, forge, paper, datapack, etc.", split = SPLIT_COMMA_NL, splitSynopsisLabel = SPLIT_SYNOPSIS_COMMA_NL, - paramLabel = "[loader:]id|slug" + paramLabel = "[loader:]id|slug[:version]" ) List projects; @@ -124,7 +132,7 @@ private List processProjects(List projects) { //noinspection DataFlowIssue since it thinks block() may return null return modrinthApiClient.bulkGetProjects( - projects.stream() + expandProjectListings(projects).stream() .filter(s -> !s.trim().isEmpty()) .map(ProjectRef::parse) ) @@ -142,6 +150,30 @@ private List processProjects(List projects) { } } + private List expandProjectListings(List projects) { + return projects.stream() + .distinct() + // handle @-file containing refs + .flatMap(ref -> { + if (ref.startsWith("@")) { + try { + return Files.readAllLines(Paths.get(ref.substring(1))).stream() + .map(s -> s + .replaceFirst("#.*", "") + .trim() + ) + .filter(s -> !s.isEmpty()); + } catch (IOException e) { + throw new GenericException("Reading project refs from file: " + ref.substring(1), e); + } + } + else { + return Stream.of(ref); + } + }) + .collect(Collectors.toList()); + } + private ModrinthManifest loadManifest() throws IOException { final Path legacyManifestPath = outputDirectory.resolve(LegacyModrinthManifest.FILENAME); diff --git a/src/test/java/me/itzg/helpers/modrinth/ModrinthCommandTest.java b/src/test/java/me/itzg/helpers/modrinth/ModrinthCommandTest.java index bc77a237..48ea9a08 100644 --- a/src/test/java/me/itzg/helpers/modrinth/ModrinthCommandTest.java +++ b/src/test/java/me/itzg/helpers/modrinth/ModrinthCommandTest.java @@ -15,6 +15,8 @@ import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; import java.nio.file.Path; +import java.nio.file.Files; +import java.util.Arrays; import java.util.function.Consumer; import me.itzg.helpers.LatchingExecutionExceptionHandler; import me.itzg.helpers.errors.InvalidParameterException; @@ -378,6 +380,37 @@ void handlesDatapacksLatestVersion(@TempDir Path tempDir) { .hasContent("content of zip"); } + @Test + void usingListingFile(@TempDir Path tempDir) throws Exception { + setupStubs(); + + final Path listingFile = Files.write(tempDir.resolve("listing.txt"), + Arrays.asList( + "fabric-api", + "# This is a comment", + "cloth-config", + "", + "# Another comment" + ) + ); + + final int exitCode = new CommandLine( + new ModrinthCommand() + ) + .execute( + "--api-base-url", wm.getRuntimeInfo().getHttpBaseUrl(), + "--output-directory", tempDir.toString(), + "--game-version", "1.19.2", + "--loader", "fabric", + "--projects=@" + listingFile + ); + + assertThat(exitCode).isEqualTo(ExitCode.OK); + + assertThat(tempDir.resolve("mods/fabric-api-0.76.1+1.19.2.jar")).exists(); + assertThat(tempDir.resolve("mods/cloth-config-8.3.103-fabric.jar")).exists(); + } + @NotNull private static RequestPatternBuilder projectVersionsRequest(String projectId) { return getRequestedFor(urlPathEqualTo("/v2/project/" + projectId + "/version"));