Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jar-in-Jar: Throw when only incompatible version ranges were specified #30

Merged
merged 3 commits into from
Jun 20, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.neoforged.jarjar.metadata.ContainedJarIdentifier;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.gradle.api.GradleException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.component.ComponentSelector;
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
Expand Down Expand Up @@ -130,10 +131,9 @@ private static String getEmbeddedFilename(ResolvedArtifactResult result, Contain

private static void collectFromComponent(ResolvedComponentResult rootComponent, Set<ContainedJarIdentifier> knownIdentifiers, Map<ContainedJarIdentifier, String> versions, Map<ContainedJarIdentifier, String> versionRanges) {
for (DependencyResult result : rootComponent.getDependencies()) {
if (!(result instanceof ResolvedDependencyResult)) {
if (!(result instanceof ResolvedDependencyResult resolvedResult)) {
continue;
}
ResolvedDependencyResult resolvedResult = (ResolvedDependencyResult) result;
ComponentSelector requested = resolvedResult.getRequested();
ResolvedVariantResult variant = resolvedResult.getResolvedVariant();

Expand All @@ -147,16 +147,19 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,

String versionRange = null;
if (requested instanceof ModuleComponentSelector requestedModule) {
if (isValidVersionRange(requestedModule.getVersionConstraint().getStrictVersion())) {
versionRange = requestedModule.getVersionConstraint().getStrictVersion();
} else if (isValidVersionRange(requestedModule.getVersionConstraint().getRequiredVersion())) {
versionRange = requestedModule.getVersionConstraint().getRequiredVersion();
} else if (isValidVersionRange(requestedModule.getVersionConstraint().getPreferredVersion())) {
versionRange = requestedModule.getVersionConstraint().getPreferredVersion();
} if (isValidVersionRange(requestedModule.getVersion())) {
versionRange = requestedModule.getVersion();
var constraint = requestedModule.getVersionConstraint();
if (!constraint.getStrictVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getStrictVersion(), requestedModule);
} else if (!constraint.getRequiredVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getRequiredVersion(), requestedModule);
} else if (!constraint.getPreferredVersion().isEmpty()) {
versionRange = validateVersionRange(constraint.getPreferredVersion(), requestedModule);
} else {
versionRange = validateVersionRange(requestedModule.getVersion(), requestedModule);
}
}

// If no range was specified, or this is a project-dependency, use a loose version range
if (versionRange == null) {
versionRange = makeOpenRange(variant);
}
Expand All @@ -174,8 +177,7 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,

private static @Nullable ArtifactIdentifier capabilityOrModule(final ResolvedVariantResult variant) {
ArtifactIdentifier moduleIdentifier = null;
if (variant.getOwner() instanceof ModuleComponentIdentifier) {
ModuleComponentIdentifier moduleComponentIdentifier = (ModuleComponentIdentifier) variant.getOwner();
if (variant.getOwner() instanceof ModuleComponentIdentifier moduleComponentIdentifier) {
moduleIdentifier = new ArtifactIdentifier(
moduleComponentIdentifier.getGroup(),
moduleComponentIdentifier.getModule(),
Expand All @@ -189,7 +191,7 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,
capability.getName(),
capability.getVersion()
))
.collect(Collectors.toList());
.toList();

if (moduleIdentifier != null && capabilityIdentifiers.contains(moduleIdentifier)) {
return moduleIdentifier;
Expand Down Expand Up @@ -221,16 +223,25 @@ private static void collectFromComponent(ResolvedComponentResult rootComponent,
return moduleOrCapabilityVersion(variant);
}

private static boolean isValidVersionRange(final @Nullable String range) {
if (range == null) {
return false;
}
private static String validateVersionRange(String range, ModuleComponentSelector module) {
var errorPrefix = "Unsupported version constraint '" + range + "' on Jar-in-Jar dependency " + module.getModuleIdentifier() + ": ";

VersionRange data;
try {
final VersionRange data = VersionRange.createFromVersionSpec(range);
return data.hasRestrictions() && data.getRecommendedVersion() == null && !range.contains("+");
data = VersionRange.createFromVersionSpec(range);
} catch (InvalidVersionSpecificationException e) {
return false;
throw new GradleException(errorPrefix + e.getMessage());
}

if (!data.hasRestrictions()) {
throw new GradleException(errorPrefix + "no restrictions");
} else if (data.getRecommendedVersion() != null) {
throw new GradleException(errorPrefix + "recommended versions are unsupported");
} else if (range.contains("+")) {
throw new GradleException(errorPrefix + "dynamic versions are unsupported");
}

return range;
}

/**
Expand Down
112 changes: 112 additions & 0 deletions src/test/java/net/neoforged/moddevgradle/tasks/JarJarTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package net.neoforged.moddevgradle.tasks;

import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.UnexpectedBuildFailure;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import static org.assertj.core.api.Assertions.assertThat;
import static org.gradle.testkit.runner.TaskOutcome.NO_SOURCE;
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

class JarJarTest {
@TempDir
Path tempDir;

@Test
public void testNoSourceWhenNoDependenciesAreDefined() throws IOException {
var result = runWithSource("");
assertEquals(NO_SOURCE, result.task(":jarJar").getOutcome());
}

@Test
public void testSuccessfulEmbed() throws IOException {
var result = runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
strictly '[0.1, 3.0)'
prefer '2.0.13'
}
}
}
""");
assertEquals(SUCCESS, result.task(":jarJar").getOutcome());
}

@Test
public void testUnsupportedStrictlyRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
strictly '[0.1, 3.0['
prefer '2.0.13'
}
}
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedRequiredRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api:[0.1, 3.0["))
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedPreferredRange() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api")) {
version {
prefer '[0.1, 3.0['
}
}
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '[0.1, 3.0[' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

@Test
public void testUnsupportedDynamicVersion() {
var e = assertThrows(UnexpectedBuildFailure.class, () -> runWithSource("""
dependencies {
jarJar(implementation("org.slf4j:slf4j-api:2.0.+"))
}
"""));
assertThat(e).hasMessageContaining("Unsupported version constraint '2.0.+' on Jar-in-Jar dependency org.slf4j:slf4j-api");
}

private BuildResult runWithSource(String source) throws IOException {
Files.writeString(tempDir.resolve("settings.gradle"), "");
Files.writeString(tempDir.resolve("build.gradle"), """
plugins {
id "net.neoforged.moddev"
}
repositories {
mavenCentral()
}
""" + source);

return GradleRunner.create()
.withPluginClasspath()
.withProjectDir(tempDir.toFile())
.withArguments("jarjar")
.withDebug(true)
.build();
}

}
Loading