Skip to content

Commit

Permalink
[fix] VersionsPropsPlugin always creates rootConfiguration (#52)
Browse files Browse the repository at this point in the history
## Before this PR

If `versions.props` was missing, VersionsPropsPlugin exited early and didn't do any of the other logic that scripts might expect, such as creating the `rootConfiguration` configuration, and adding hooks to fix platform imports in published boms.

## After this PR

It now treats a missing `versions.props` as if it was empty, and still performs all of the logic the plugin would usually perform.
  • Loading branch information
dansanduleac authored and bulldozer-bot[bot] committed Mar 7, 2019
1 parent b2f913e commit a2068b1
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 58 deletions.
24 changes: 13 additions & 11 deletions src/main/java/com/palantir/gradle/versions/VersionsProps.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@
public final class VersionsProps {
private final FuzzyPatternResolver fuzzyResolver;
private final Map<String, String> patternToPlatform;
private final Path path;

public VersionsProps(Path path) {
this.path = path;
private VersionsProps(FuzzyPatternResolver fuzzyResolver) {
this.fuzzyResolver = fuzzyResolver;
this.patternToPlatform = Sets
.difference(fuzzyResolver.versions().keySet(), fuzzyResolver.exactMatches())
.stream()
.collect(Collectors.toMap(key -> key, this::constructPlatform));
}

public static VersionsProps loadFromFile(Path path) {
Properties recommendations = new Properties();
try (BufferedReader reader = Files.newBufferedReader(path)) {
recommendations.load(new EolCommentFilteringReader(new ColonFilteringReader(reader)));
Expand All @@ -53,16 +59,12 @@ public VersionsProps(Path path) {
.forEach(name -> builder.putVersions(
name.replaceAll("/", ":"),
recommendations.getProperty(name).trim()));
fuzzyResolver = builder.build();

patternToPlatform = Sets
.difference(fuzzyResolver.versions().keySet(), fuzzyResolver.exactMatches())
.stream()
.collect(Collectors.toMap(key -> key, this::constructPlatform));
return new VersionsProps(builder.build());
}

public Path getPath() {
return path;
/** Construct a trivial {@link VersionsProps} that has no version recommendations. */
static VersionsProps empty() {
return new VersionsProps(FuzzyPatternResolver.builder().build());
}

public Stream<DependencyConstraint> constructConstraints(DependencyConstraintHandler handler) {
Expand Down
91 changes: 45 additions & 46 deletions src/main/java/com/palantir/gradle/versions/VersionsPropsPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import org.gradle.api.GradleException;
import org.gradle.api.NamedDomainObjectProvider;
import org.gradle.api.Plugin;
Expand Down Expand Up @@ -49,51 +48,55 @@ public class VersionsPropsPlugin implements Plugin<Project> {

@Override
public final void apply(Project project) {
checkPreconditions(project);
checkPreconditions();
if (project.getRootProject().equals(project)) {
applyToRootProject(project);
}

VersionRecommendationsExtension extension = project.getExtensions()
.create(VersionRecommendationsExtension.EXTENSION, VersionRecommendationsExtension.class, project);
VersionRecommendationsExtension extension =
project.getRootProject().getExtensions().getByType(VersionRecommendationsExtension.class);

Optional<VersionsProps> versionsProps = computeVersionsProps(project.file("versions.props").toPath());
if (!versionsProps.isPresent()) {
return;
}
VersionsProps versionsProps = loadVersionsProps(project.getRootProject().file("versions.props").toPath());

NamedDomainObjectProvider<Configuration> rootConfiguration =
project.getConfigurations().register(ROOT_CONFIGURATION_NAME, conf -> {
conf.setVisible(false);
});

project.allprojects(subproject -> {
NamedDomainObjectProvider<Configuration> rootConfiguration =
subproject.getConfigurations().register(ROOT_CONFIGURATION_NAME, conf -> {
conf.setVisible(false);
});

subproject.getConfigurations().configureEach(conf ->
setupConfiguration(subproject, extension, rootConfiguration, versionsProps.get(), conf));

// Note: don't add constraints to this, only call `create` / `platform` on it.
DependencyConstraintHandler constraintHandler = project.getDependencies().getConstraints();
rootConfiguration.configure(conf ->
addVersionsPropsConstraints(constraintHandler, conf, versionsProps.get()));

log.info("Configuring rules to assign *-constraints to platforms in {}", subproject);
subproject.getDependencies()
.getComponents()
.all(component -> tryAssignComponentToPlatform(versionsProps.get(), component));

// Gradle 5.1 has a bug whereby a platform dependency whose version comes from a separate constraint end
// up as two separate entries in the resulting POM, making it invalid.
// https://github.com/gradle/gradle/issues/8238
subproject.getPluginManager().withPlugin("publishing", plugin -> {
PublishingExtension publishingExtension =
subproject.getExtensions().getByType(PublishingExtension.class);
publishingExtension.getPublications().withType(MavenPublication.class, publication -> {
log.info("Fixing pom publication for {}: {}", subproject, publication);
publication.getPom().withXml(xmlProvider -> {
GradleWorkarounds.mergeImportsWithVersions(xmlProvider.asElement());
});
project.getConfigurations().configureEach(conf ->
setupConfiguration(project, extension, rootConfiguration, versionsProps, conf));

// Note: don't add constraints to this, only call `create` / `platform` on it.
DependencyConstraintHandler constraintHandler = project.getDependencies().getConstraints();
rootConfiguration.configure(conf ->
addVersionsPropsConstraints(constraintHandler, conf, versionsProps));

log.info("Configuring rules to assign *-constraints to platforms in {}", project);
project.getDependencies()
.getComponents()
.all(component -> tryAssignComponentToPlatform(versionsProps, component));

// Gradle 5.1 has a bug whereby a platform dependency whose version comes from a separate constraint end
// up as two separate entries in the resulting POM, making it invalid.
// https://github.com/gradle/gradle/issues/8238
project.getPluginManager().withPlugin("publishing", plugin -> {
PublishingExtension publishingExtension =
project.getExtensions().getByType(PublishingExtension.class);
publishingExtension.getPublications().withType(MavenPublication.class, publication -> {
log.info("Fixing pom publication for {}: {}", project, publication);
publication.getPom().withXml(xmlProvider -> {
GradleWorkarounds.mergeImportsWithVersions(xmlProvider.asElement());
});
});
});
}

private static void applyToRootProject(Project project) {
project.getExtensions()
.create(VersionRecommendationsExtension.EXTENSION, VersionRecommendationsExtension.class, project);
project.subprojects(subproject -> subproject.getPluginManager().apply(VersionsPropsPlugin.class));
}

private static void setupConfiguration(
Project subproject,
VersionRecommendationsExtension extension,
Expand Down Expand Up @@ -185,19 +188,15 @@ private static void addVersionsPropsConstraints(
constraints.forEach(conf.getDependencyConstraints()::add);
}

private static Optional<VersionsProps> computeVersionsProps(Path versionsPropsFile) {
private static VersionsProps loadVersionsProps(Path versionsPropsFile) {
if (!Files.exists(versionsPropsFile)) {
return Optional.empty();
return VersionsProps.empty();
}
log.info("Configuring constraints from properties file {}", versionsPropsFile);
return Optional.of(new VersionsProps(versionsPropsFile));
return VersionsProps.loadFromFile(versionsPropsFile);
}

private static void checkPreconditions(Project project) {
if (!project.getRootProject().equals(project)) {
throw new GradleException("Must be applied only to root project");
}

private static void checkPreconditions() {
Preconditions.checkState(
GradleVersion.current().compareTo(MINIMUM_GRADLE_VERSION) >= 0,
"This plugin requires gradle >= %s",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,22 @@ class VersionsPropsPluginIntegrationSpec extends IntegrationSpec {
def e = runTasksAndFail()
e.output.contains("Not allowed to resolve")
}
/**

def "creates rootConfiguration even if versions props file missing"() {
buildFile << """
dependencies {
constraints {
rootConfiguration 'org.slf4j:slf4j-api:1.7.25'
}
}
""".stripIndent()
file('versions.props').delete()

expect:
runTasks()
}

/**
* Recursively converts a node's children to a map of <tt>(tag name): (value inside tag)</tt>.
* <p>
* See: https://stackoverflow.com/a/26889997/274699
Expand Down

0 comments on commit a2068b1

Please sign in to comment.