Skip to content
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
30 changes: 28 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,25 @@
<version.slf4j>2.0.17</version.slf4j>
<version.spotless>3.0.0</version.spotless>
<version.google-java-format>1.22.0</version.google-java-format>
<version.junit>6.0.0</version.junit>
<version.junit>6.0.3</version.junit>
<version.junit-pioneer>2.3.0</version.junit-pioneer>
<version.mockito>5.20.0</version.mockito>
<version.assertj>3.27.6</version.assertj>
<version.maven-enforcer>3.5.0</version.maven-enforcer>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${version.junit}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>eu.maveniverse.maven.mima</groupId>
Expand Down Expand Up @@ -98,7 +111,17 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit-pioneer</groupId>
<artifactId>junit-pioneer</artifactId>
<version>${version.junit-pioneer}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -279,6 +302,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
38 changes: 28 additions & 10 deletions src/main/java/org/codejive/jpm/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -90,7 +91,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(artifactsMixin.directory)
.noLinks(artifactsMixin.noLinks)
.cacheDir(artifactsMixin.cacheDir)
.cacheDir(artifactsMixin.getCacheDir())
.build()
.copy(
artifactsMixin.artifactNames,
Expand Down Expand Up @@ -163,7 +164,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.install(
Expand All @@ -177,7 +178,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.copy(
Expand Down Expand Up @@ -219,7 +220,7 @@ String[] search(String artifactPattern) {
return Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.search(artifactPattern, Math.min(max, 200), backend);
Expand Down Expand Up @@ -320,7 +321,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(optionalArtifactsMixin.directory)
.noLinks(optionalArtifactsMixin.noLinks)
.cacheDir(optionalArtifactsMixin.cacheDir)
.cacheDir(optionalArtifactsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.install(
Expand Down Expand Up @@ -351,7 +352,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(optionalArtifactsMixin.directory)
.noLinks(optionalArtifactsMixin.noLinks)
.cacheDir(optionalArtifactsMixin.cacheDir)
.cacheDir(optionalArtifactsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.path(
Expand Down Expand Up @@ -409,7 +410,7 @@ public Integer call() throws Exception {
return Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.verbose(!quietMixin.quiet)
.build()
Expand Down Expand Up @@ -468,7 +469,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.listActions();
Expand Down Expand Up @@ -514,7 +515,7 @@ public Integer call() throws Exception {
Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.verbose(!quietMixin.quiet)
.build()
Expand Down Expand Up @@ -548,7 +549,7 @@ public Integer call() throws Exception {
return Jpm.builder()
.directory(depsMixin.directory)
.noLinks(depsMixin.noLinks)
.cacheDir(depsMixin.cacheDir)
.cacheDir(depsMixin.getCacheDir())
.appFile(appInfoFileMixin.appInfoFile)
.build()
.executeAction(actionName(), args, depsMixin.getRepositoryMap());
Expand Down Expand Up @@ -624,6 +625,23 @@ static class DepsMixin {
"Directory where downloaded artifacts will be cached (default: value of JPM_CACHE environment variable; whatever is set in Maven's settings.xml or $HOME/.m2/repository")
Path cacheDir;

Path getCacheDir() {
if (cacheDir != null) {
return cacheDir;
}
String envCache = System.getenv("JPM_CACHE");
if (envCache != null && !envCache.isEmpty()) {
try {
return Path.of(envCache);
} catch (InvalidPathException e) {
System.err.println(
"Warning: Invalid path in JPM_CACHE environment variable, ignoring: "
+ envCache);
}
}
return null;
}

Map<String, String> getRepositoryMap() {
Map<String, String> repoMap = new HashMap<>();
for (String repo : repositories) {
Expand Down
166 changes: 166 additions & 0 deletions src/test/java/org/codejive/jpm/MainCacheIntegrationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package org.codejive.jpm;

import static org.assertj.core.api.Assertions.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junitpioneer.jupiter.ClearEnvironmentVariable;
import org.junitpioneer.jupiter.SetEnvironmentVariable;
import picocli.CommandLine;

/** Integration tests for the --cache option and JPM_CACHE environment variable. */
class MainCacheIntegrationTest {

@TempDir Path tempDir;
@TempDir Path cacheDir1;
@TempDir Path cacheDir2;

private String originalUserDir;
private PrintStream originalOut;
private PrintStream originalErr;
private ByteArrayOutputStream outContent;
private ByteArrayOutputStream errContent;

@BeforeEach
void setUp() {
originalUserDir = System.getProperty("user.dir");
System.setProperty("user.dir", tempDir.toString());
System.setProperty("picocli.ansi", "false");

// Capture stdout and stderr
originalOut = System.out;
originalErr = System.err;
outContent = new ByteArrayOutputStream();
errContent = new ByteArrayOutputStream();
System.setOut(new PrintStream(outContent));
System.setErr(new PrintStream(errContent));
}

@AfterEach
void tearDown() {
System.setProperty("user.dir", originalUserDir);
System.setOut(originalOut);
System.setErr(originalErr);
}

@Test
@ClearEnvironmentVariable(key = "JPM_CACHE")
void testPathCommandWithCacheOption() throws IOException {
// Create app.yml
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
int exitCode = cmd.execute("path", "--cache", cacheDir1.toString());

// Exit code 0 or 1 is acceptable (1 means dependency not found, which is expected)
assertThat(exitCode).isIn(0, 1);
}

@Test
@SetEnvironmentVariable(key = "JPM_CACHE", value = "/tmp/env-cache")
void testPathCommandWithEnvironmentVariable() throws IOException {
// Create app.yml
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
int exitCode = cmd.execute("path");

// The command should succeed with JPM_CACHE set
assertThat(exitCode).isIn(0, 1);
}

@Test
@SetEnvironmentVariable(key = "JPM_CACHE", value = "/tmp/env-cache")
void testCopyCommandCacheOptionOverridesEnvironmentVariable() throws IOException {
CommandLine cmd = Main.getCommandLine();
int exitCode =
cmd.execute(
"copy", "--cache", cacheDir1.toString(), "--quiet", "fake:artifact:1.0.0");

// The command should use cacheDir1 (from --cache) not /tmp/env-cache
// Even though it will fail to resolve, it should parse correctly
assertThat(exitCode).isIn(0, 1); // May fail to resolve, but shouldn't crash
}

@Test
@ClearEnvironmentVariable(key = "JPM_CACHE")
void testInstallCommandWithShortCacheOption() throws IOException {
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
int exitCode =
cmd.execute(
"install", "-c", cacheDir1.toString(), "--quiet", "fake:artifact:1.0.0");

// The -c short form should work the same as --cache
assertThat(exitCode).isIn(0, 1); // May fail to resolve, but shouldn't crash
}

@Test
void testCacheOptionInHelp() {
CommandLine cmd = Main.getCommandLine();
int exitCode = cmd.execute("copy", "--help");

// PicoCLI may return 0 or 2 for help depending on configuration
// What matters is that the help text is displayed
String output = outContent.toString() + errContent.toString();
assertThat(output)
.contains("-c, --cache")
.contains("Directory where downloaded artifacts will be cached")
.contains("JPM_CACHE");
}

@Test
@SetEnvironmentVariable(key = "JPM_CACHE", value = " ")
void testGetCacheDirWithWhitespaceOnlyEnvironmentVariable() throws IOException {
// An environment variable with only whitespace should be treated as empty
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
// This should not crash - whitespace-only JPM_CACHE should be ignored
int exitCode = cmd.execute("path");

assertThat(exitCode).isIn(0, 1);
}

@Test
@ClearEnvironmentVariable(key = "JPM_CACHE")
void testCacheOptionWithRelativePath() throws IOException {
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
int exitCode = cmd.execute("path", "--cache", "./my-cache");

assertThat(exitCode).isIn(0, 1);
// Should accept relative paths
}

@Test
@ClearEnvironmentVariable(key = "JPM_CACHE")
void testCacheOptionWithAbsolutePath() throws IOException {
createSimpleAppYml();

CommandLine cmd = Main.getCommandLine();
int exitCode = cmd.execute("path", "--cache", cacheDir1.toAbsolutePath().toString());

assertThat(exitCode).isIn(0, 1);
// Should accept absolute paths
}

private void createSimpleAppYml() throws IOException {
String yamlContent =
"dependencies:\n"
+ " fake:dummy: \"1.2.3\"\n"
+ "\n"
+ "actions:\n"
+ " build: \"echo building\"\n";
Files.writeString(tempDir.resolve("app.yml"), yamlContent);
}
}
Loading