fix: honour pinned versions in external resolvers (#1606)
diff --git a/docs/bzlmod-api.md b/docs/bzlmod-api.md
index 399d03d..38e7474 100644
--- a/docs/bzlmod-api.md
+++ b/docs/bzlmod-api.md
@@ -114,7 +114,7 @@
| <a id="maven.install-strict_visibility_value"></a>strict_visibility_value | - | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["@rules_jvm_external//visibility:private"]` |
| <a id="maven.install-use_credentials_from_home_netrc_file"></a>use_credentials_from_home_netrc_file | Whether to pass machine login credentials from the ~/.netrc file to coursier. | Boolean | optional | `False` |
| <a id="maven.install-use_starlark_android_rules"></a>use_starlark_android_rules | Whether to use the native or Starlark version of the Android rules. | Boolean | optional | `False` |
-| <a id="maven.install-version_conflict_policy"></a>version_conflict_policy | Policy for user-defined vs. transitive dependency version conflicts<br><br>If "pinned", choose the user-specified version in maven_install unconditionally. If "default", follow Coursier's default policy. | String | optional | `"default"` |
+| <a id="maven.install-version_conflict_policy"></a>version_conflict_policy | Policy for user-defined vs. transitive dependency version conflicts<br><br>If "pinned", choose the user-specified version in maven_install unconditionally. With the Gradle and Maven resolvers, this only applies to artifacts contributed by the root module. If "default", follow the selected resolver's default policy. | String | optional | `"default"` |
<a id="maven.override"></a>
diff --git a/private/extensions/maven.bzl b/private/extensions/maven.bzl
index a9a148b..0aa500d 100644
--- a/private/extensions/maven.bzl
+++ b/private/extensions/maven.bzl
@@ -110,7 +110,8 @@
doc = """Policy for user-defined vs. transitive dependency version conflicts
If "pinned", choose the user-specified version in maven_install unconditionally.
- If "default", follow Coursier's default policy.
+ With the Gradle and Maven resolvers, this only applies to artifacts contributed by the root module.
+ If "default", follow the selected resolver's default policy.
""",
default = "default",
values = [
@@ -597,6 +598,16 @@
if k != "to_json" and k != "to_proto" and getattr(s, k, None)
} | {"version": getattr(s, "version", "")}
+def apply_root_version_conflict_policy(artifacts, resolver, version_conflict_policy):
+ """Applies the install-level conflict policy to root module artifacts."""
+ if resolver not in ["gradle", "maven"] or version_conflict_policy != "pinned":
+ return artifacts
+
+ return [
+ struct(**(remove_fields(artifact) | {"force_version": True})) if getattr(artifact, "version", None) else artifact
+ for artifact in artifacts
+ ]
+
def maven_impl(mctx):
repos = {}
overrides = {}
@@ -652,7 +663,11 @@
merged_repo.update(root_repo)
# Special handling for artifacts and boms - deduplicate with root priority
- root_artifacts = root_repo.get("artifacts", [])
+ root_artifacts = apply_root_version_conflict_policy(
+ root_repo.get("artifacts", []),
+ root_repo.get("resolver", _DEFAULT_RESOLVER),
+ root_repo.get("version_conflict_policy", "default"),
+ )
bazel_dep_to_non_root_artifacts = non_root_repo.get("bazel_dep_to_artifacts", {})
root_boms = root_repo.get("boms", [])
bazel_dep_to_non_root_boms = non_root_repo.get("bazel_dep_to_boms", {})
diff --git a/tests/unit/BUILD b/tests/unit/BUILD
index 7ca05ab..f50a066 100644
--- a/tests/unit/BUILD
+++ b/tests/unit/BUILD
@@ -11,6 +11,7 @@
load(":specs_test.bzl", "artifact_specs_test_suite")
load(":v3_lock_file_test.bzl", "v3_lock_file_test_suite")
load(":version_catalogs_test.bzl", "version_catalogs_test_suite")
+load(":version_conflict_policy_test.bzl", "version_conflict_policy_test_suite")
amend_artifact_test_suite()
@@ -38,3 +39,4 @@
version_catalogs_test_suite()
+version_conflict_policy_test_suite()
diff --git a/tests/unit/version_conflict_policy_test.bzl b/tests/unit/version_conflict_policy_test.bzl
new file mode 100644
index 0000000..26b7100
--- /dev/null
+++ b/tests/unit/version_conflict_policy_test.bzl
@@ -0,0 +1,48 @@
+"""Tests for resolver-specific version conflict policies."""
+
+load("@bazel_skylib//lib:partial.bzl", "partial")
+load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")
+load("//private/extensions:maven.bzl", "apply_root_version_conflict_policy")
+load("//private/lib:coordinates.bzl", "unpack_coordinates")
+
+def _pinned_policy_forces_versioned_root_artifacts_impl(ctx):
+ env = unittest.begin(ctx)
+
+ for resolver in ["gradle", "maven"]:
+ versioned = unpack_coordinates("com.example:root:1.0")
+ versionless = unpack_coordinates("com.example:managed-by-bom")
+
+ artifacts = apply_root_version_conflict_policy(
+ [versioned, versionless],
+ resolver,
+ "pinned",
+ )
+
+ asserts.true(env, artifacts[0].force_version)
+ asserts.false(env, hasattr(artifacts[1], "force_version"))
+
+ return unittest.end(env)
+
+pinned_policy_forces_versioned_root_artifacts_test = unittest.make(_pinned_policy_forces_versioned_root_artifacts_impl)
+
+def _other_policies_leave_root_artifacts_unchanged_impl(ctx):
+ env = unittest.begin(ctx)
+
+ artifact = unpack_coordinates("com.example:root:1.0")
+
+ default_artifacts = apply_root_version_conflict_policy([artifact], "gradle", "default")
+ coursier_artifacts = apply_root_version_conflict_policy([artifact], "coursier", "pinned")
+
+ asserts.false(env, hasattr(default_artifacts[0], "force_version"))
+ asserts.false(env, hasattr(coursier_artifacts[0], "force_version"))
+
+ return unittest.end(env)
+
+other_policies_leave_root_artifacts_unchanged_test = unittest.make(_other_policies_leave_root_artifacts_unchanged_impl)
+
+def version_conflict_policy_test_suite():
+ unittest.suite(
+ "version_conflict_policy_tests",
+ partial.make(pinned_policy_forces_versioned_root_artifacts_test, size = "small"),
+ partial.make(other_policies_leave_root_artifacts_unchanged_test, size = "small"),
+ )