Skip to content

Conversation

@FinlayRJW
Copy link
Contributor

@FinlayRJW FinlayRJW commented Oct 7, 2025

Before this PR

  • Problem:
    Our builds do not currently guarantee that all modules within the same group (e.g., com.palantir:*) are resolved to the same version when used together—especially when multiple versions are present due to transitive dependencies or downgrades.
    Previous attempts (#1423) to add constraints via Gradle Module Metadata (GMM) improved this but did not fully enforce alignment, particularly during version downgrades.

After this PR

  • Solution:
    Introduces a new "virtual platform" mechanism for strict version alignment of all Java modules within the same group, using a pair of new plugins:

    • ConstraintProducerPlugin

      • Applied automatically to the root project by the new settings plugin.
      • For every group with multiple Java modules, injects a synthetic virtual platform dependency constraint (e.g., consistent-versions.external-virtual-platform.com.palantir:_) into all Java modules in the group.
      • This constraint is published in the POM and Gradle Module Metadata, making alignment information available to consumers.
    • ConstraintConsumerSettingsPlugin (published as the com.palantir.externally-consistent-versions settings plugin)

      • Applied in the consumer's settings.gradle.
      • During dependency resolution, automatically inspects POMs and metadata for the virtual platform constraint.
      • Uses a component metadata rule to assign all modules with the constraint to a virtual platform, ensuring Gradle always aligns them to the same version—even if a consumer requests a lower version with force or strictly.
      • Works for both buildscript and runtime classpaths.
    • Tests:

      • Comprehensive integration tests verify that version alignment works as expected in both buildscript and runtime configurations, including cases with forced downgrades, transitive dependencies, and malformed POMs.

Usage

Add to settings.gradle:

plugins {
    id 'com.palantir.externally-consistent-versions' version '<current version>'
}

Why Not Use BOMs?

  • No separate BOM artifact needed: Alignment metadata is published alongside each module.
  • No explicit imports required: Alignment is enforced automatically for any consumer using the settings plugin.
  • Works even on downgrades: Strict alignment is enforced even when versions are forced or strictly constrained, which BOMs cannot guarantee.

==COMMIT_MSG==
Add virtual platform constraint plugins to enable automatic version alignment

  • Adds ConstraintProducerPlugin to inject a virtual platform constraint into all Java modules in a group.
  • Adds ConstraintConsumerSettingsPlugin to enforce version alignment at dependency resolution time based on published POM.
  • Ensures consumers always resolve all modules in a group to the same version, even when downgrades or transitive mismatches occur.
  • Includes comprehensive tests and dependency updates to support the new functionality.
    ==COMMIT_MSG==

Possible Downsides

  • Added Complexity: Introduces new plugins and mechanisms that contributors and consumers should be aware of.
  • Settings Plugin Required: Consumers must explicitly add the settings plugin to benefit from alignment.

@changelog-app
Copy link

changelog-app bot commented Oct 7, 2025

Generate changelog in changelog/@unreleased

Type (Select exactly one)

  • Feature (Adding new functionality)
  • Improvement (Improving existing functionality)
  • Fix (Fixing an issue with existing functionality)
  • Break (Creating a new major version by breaking public APIs)
  • Deprecation (Removing functionality in a non-breaking way)
  • Migration (Automatically moving data/functionality to a new system)

Description

Add virtual platform constraint plugins to enable automatic version alignment

  • Adds ConstraintProducerPlugin to inject a virtual platform constraint into all Java modules in a group.
  • Adds ConstraintConsumerSettingsPlugin to enforce version alignment at dependency resolution time based on published POM.
  • Ensures consumers always resolve all modules in a group to the same version, even when downgrades or transitive mismatches occur.
  • Includes comprehensive tests and dependency updates to support the new functionality.

Check the box to generate changelog(s)

  • Generate changelog entry

@FinlayRJW FinlayRJW changed the title Finlayw/vp Add virtual platform constraint plugins for automatic version alignment across group modules Oct 7, 2025
@changelog-app
Copy link

changelog-app bot commented Oct 7, 2025

Successfully generated changelog entry!

Need to regenerate?

Simply interact with the changelog bot comment again to regenerate these entries.

🔄 Changelog entries were re-generated at Wed, 29 Oct 2025 14:23:57 UTC!


📋Changelog Preview

💡 Improvements

  • Add virtual platform constraint plugins to enable automatic version alignment

    • Adds ConstraintProducerPlugin to inject a virtual platform constraint into all Java modules in a group.
    • Adds ConstraintConsumerSettingsPlugin to enforce version alignment at dependency resolution time based on published POM.
    • Ensures consumers always resolve all modules in a group to the same version, even when downgrades or transitive mismatches occur.
    • Includes comprehensive tests and dependency updates to support the new functionality. (#1436)

@FinlayRJW FinlayRJW marked this pull request as ready for review October 7, 2025 10:11
@FinlayRJW FinlayRJW requested a review from Copilot October 7, 2025 10:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces virtual platform constraint plugins to enforce strict version alignment for all Java modules within the same group, addressing issues where modules might resolve to different versions due to transitive dependencies or downgrades.

Key changes:

  • Adds AddVirtualPlatformPlugin to inject virtual platform constraints into published metadata
  • Adds VirtualPlatformSettingsPlugin to enforce version alignment during dependency resolution
  • Integrates the plugins into the existing consistent versions framework

Reviewed Changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
AddVirtualPlatformPlugin.java New plugin that injects virtual platform constraints when enabled via property
VirtualPlatformSettingsPlugin.java New settings plugin that enforces alignment by reading metadata and assigning modules to virtual platforms
ConsistentVersionsPlugin.java Integrates the new AddVirtualPlatformPlugin into the main plugin
VersionsLockPlugin.java Makes isJavaLibrary method package-private for reuse
AddVirtualPlatformPluginIntegrationSpec.groovy Integration tests for the AddVirtualPlatformPlugin
VirtualPlatformSettingsPluginIntegrationSpec.groovy Comprehensive integration tests for the VirtualPlatformSettingsPlugin

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

}

private void applyVirtualPlatformPerGroup(String groupName, Set<Project> projectGroup) {
String platformCoordinates = groupName + ":" + VIRTUAL_PLATFORM_NAME;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the future we may want to have multiple external virtual platforms per repo. The clearest example of this could be a different platform for -api jars and non--api jars - I don't think they necessarily need to have the same version. We may never do this, but we should at least keep our options open with how we define this format.

Perhaps we could have the format consistent-versions.external-virtual-platform-com.maven.group:_ which gets converted to the virtual platform com.maven.group:_?

This means if we want to add multiple virtual platforms in the future, we could do this com.palantir.foo:_ and com.palantir.foo:_-api. This would replicate/be the same virtual platform as when people do this in their versions.props:

com.palantir.foo:* = 1.1.1
com.palantir.foo:*-api = 1.2.0

We could always make a new "version" of the fake constraint (eg prefixed with consistent-versions.external-virtual-platform-v2 instead) but this makes us "forwards compatible" with all the power we currently have in versions.props.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool I've switched over. I assume we will want to add some kind of extension that lets users create the platforms they want, then some logic in the producer plugin to only add the most specific platform constraint. Or can we add all the platform constraints that match the group and let gradle figure it out.

Say a user adds:

externalVirtualPlatforms {
    platform("com.palantir.foo:*") {
        version = "1.1.1"
    }
    platform("com.palantir.foo:*-api")
}

say we are com.palantir.foo:bar-api would we add both a VP on com.palantir.foo:_:1.1.1 and com.palantir.foo:_-api:<latest-version> or just on the most specific?

throw new IllegalStateException("AddVirtualPlatformPlugin must be applied to the root project");
}

rootProject.afterEvaluate(project -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this still configuration cacheable ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so - I've added a basic test

    def "check does not break configuration cache"() {
        expect:
        runTasksWithConfigurationCache('--write-locks')
        runTasksWithConfigurationCache('build')
    }

Which I think would blow up if there where any configuration cache issues

@FinlayRJW FinlayRJW requested a review from CRogers October 9, 2025 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants