Add runtime JAR overlap check helper for plugins Introduce reusable tooling to ensure Gerrit plugins do not bundle libraries that are already shipped by Gerrit. - diff_overlap.sh: fails with the intersection of two normalized jar-ID lists - runtime_jars_overlap_test: generates a plugin runtime jar manifest and checks it for overlap with a provided manifest, e.g. //:release.war.jars.txt The overlap check is intended for Gerrit-tree builds where the Gerrit runtime manifest is available; plugins can gate it via target_compatible_with so standalone builds report it as SKIPPED rather than failing. Change-Id: If77e8a4026f48afd304e9f486ebc35e37f9925bf
diff --git a/README.md b/README.md index 92fee68..7261f9c 100644 --- a/README.md +++ b/README.md
@@ -5,6 +5,7 @@ <ul> <li><a href="#gerrit_plugin">gerrit_plugin</a></li> <li><a href="#runtime_jars_allowlist_test">runtime_jars_allowlist_test</a></li> + <li><a href="#runtime_jars_overlap_test">runtime_jars_overlap_test</a></li> </ul> </div> @@ -271,3 +272,47 @@ - normalize (default: True) — strip version suffixes from jar basenames. - exclude_self (default: True) — omit the target's own output jar(s). - size (default: "small") — Bazel test size classification. + +<a name="runtime_jars_overlap_test"></a> +## runtime_jars_overlap_test + +This macro helps plugins detect accidental bundling of third-party runtime JARs +that are already shipped by Gerrit. It compares the plugin's packaged runtime +JAR list against a provided manifest (for example Gerrit's +`//:release.war.jars.txt`) and fails on overlap. + +This check is typically meaningful only when the plugin is built inside the +Gerrit source tree where Gerrit's runtime manifest exists. Standalone plugin +workspaces should gate the test via `target_compatible_with` so it is reported +as **SKIPPED** rather than failing. + +Example usage in a plugin BUILD file: + +```python +load( + "@com_googlesource_gerrit_bazlets//tools:runtime_jars_overlap.bzl", + "runtime_jars_overlap_test", +) +load( + "@com_googlesource_gerrit_bazlets//tools:in_gerrit_tree.bzl", + "in_gerrit_tree_enabled", +) + +runtime_jars_overlap_test( + name = "no_overlap_with_gerrit", + target = ":my_plugin__plugin", + against = "//:release.war.jars.txt", + hint = "Exclude overlaps via maven.install(excluded_artifacts=[...]) and re-run this test.", + target_compatible_with = in_gerrit_tree_enabled(), +) +``` + +Optional arguments: + +- normalize (default: True) — strip version suffixes from jar basenames. +- exclude_self (default: True) — omit the target's own output jar(s). +- size (default: "small") — Bazel test size classification. +- hint (default: "") — optional guidance printed on failure. + +On failure, the test prints the overlapping normalized jar IDs and exits +non-zero.
diff --git a/tools/BUILD b/tools/BUILD index 068e8b0..617d077 100644 --- a/tools/BUILD +++ b/tools/BUILD
@@ -1,5 +1,7 @@ exports_files([ "diff_allowlist.sh", + "diff_overlap.sh", "java_runtime_jars_manifest.bzl", "runtime_jars_allowlist.bzl", + "runtime_jars_overlap.bzl", ])
diff --git a/tools/diff_overlap.sh b/tools/diff_overlap.sh new file mode 100755 index 0000000..1476b2d --- /dev/null +++ b/tools/diff_overlap.sh
@@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +LEFT="${1:?missing left list}" +RIGHT="${2:?missing right list}" +# Bazel sh_test args are not guaranteed to preserve spaces as a single argv element +# across all wrappers/platforms. The hint is free-form text, so treat it as "all +# remaining args" (argv[3..]) to avoid truncating it to the first word. +HINT="${*:3}" + +tmpdir="$(mktemp -d)" +trap 'rm -rf "${tmpdir}"' EXIT + +# Normalize: drop comments/blank lines, sort unique +grep -vE '^\s*(#|$)' "${LEFT}" | sort -u > "${tmpdir}/left.norm" +grep -vE '^\s*(#|$)' "${RIGHT}" | sort -u > "${tmpdir}/right.norm" + +# Intersection (overlap) +comm -12 "${tmpdir}/left.norm" "${tmpdir}/right.norm" > "${tmpdir}/overlap" + +if [[ -s "${tmpdir}/overlap" ]]; then + echo "" >&2 + echo "FAIL: Plugin bundles third-party JARs that are also provided by Gerrit:" >&2 + cat "${tmpdir}/overlap" >&2 + echo "" >&2 + echo "Exclude these artifacts from the plugin to avoid duplicate classes and version skew at runtime." >&2 + echo "" >&2 + + if [[ -n "${HINT}" ]]; then + echo "Fix:" >&2 + echo " ${HINT}" >&2 + else + echo "Fix:" >&2 + echo " Exclude the overlapping artifacts from maven.install(excluded_artifacts = [...])" >&2 + echo " (or treat them as provided_deps if they come from Gerrit)." >&2 + fi + + echo "" >&2 + exit 1 +fi
diff --git a/tools/runtime_jars_overlap.bzl b/tools/runtime_jars_overlap.bzl new file mode 100644 index 0000000..3c4d8b7 --- /dev/null +++ b/tools/runtime_jars_overlap.bzl
@@ -0,0 +1,48 @@ +load("//tools:java_runtime_jars_manifest.bzl", "java_runtime_jars_manifest") + +def runtime_jars_overlap_test( + name, + target, + against, + normalize = True, + exclude_self = True, + size = "small", + hint = "", + **kwargs): + """Fail if `target`'s runtime jar IDs overlap with `against` list. + + Args: + name: sh_test name. + target: Java target providing JavaInfo (e.g. ":myplugin__plugin"). + against: label of a generated text manifest (e.g. "//:release.war.jars.txt"). + normalize/exclude_self: controls jar ID normalization and self-jar exclusion. + hint: optional help printed on failure. + **kwargs: forwarded to native.sh_test (e.g. tags, target_compatible_with). + """ + plugin_manifest = name + "_manifest" + + java_runtime_jars_manifest( + name = plugin_manifest, + target = target, + normalize = normalize, + exclude_self = exclude_self, + ) + + args = [ + "$(location :%s.txt)" % plugin_manifest, + "$(location %s)" % against, + ] + if hint: + args.append(hint) + + native.sh_test( + name = name, + size = size, + srcs = ["@com_googlesource_gerrit_bazlets//tools:diff_overlap.sh"], + args = args, + data = [ + ":%s.txt" % plugin_manifest, + against, + ], + **kwargs + )