Skip to content

Commit 61594de

Browse files
authored
Print a warning when there is a duplicate artifact in the artifact list and setup basic integration testing (bazel-contrib#579)
1 parent 921975f commit 61594de

File tree

6 files changed

+176
-8
lines changed

6 files changed

+176
-8
lines changed

.bazelci/presubmit.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ tasks:
99
- bazel run @unpinned_regression_testing//:pin
1010
- bazel run @unpinned_maven_install_in_custom_location//:pin
1111
- bazel run @duplicate_artifacts_test//:pin
12-
- bazel run @regression_testing//:outdated
12+
- tests/bazel_run_tests.sh
1313
test_flags:
1414
- "--//settings:stamp_manifest=True"
1515
test_targets:
@@ -33,7 +33,7 @@ tasks:
3333
shell_commands:
3434
- bazel run @unpinned_regression_testing//:pin
3535
- bazel run @unpinned_maven_install_in_custom_location//:pin
36-
- bazel run @regression_testing//:outdated
36+
- tests/bazel_run_tests.sh
3737
test_targets:
3838
- "--"
3939
- "//..."
@@ -43,7 +43,7 @@ tasks:
4343
shell_commands:
4444
- bazel run @unpinned_regression_testing//:pin
4545
- bazel run @unpinned_maven_install_in_custom_location//:pin
46-
- bazel run @regression_testing//:outdated
46+
- tests/bazel_run_tests.sh
4747
test_targets:
4848
- "--"
4949
- "//..."
@@ -53,7 +53,7 @@ tasks:
5353
shell_commands:
5454
- bazel run @unpinned_regression_testing//:pin
5555
- bazel run @unpinned_maven_install_in_custom_location//:pin
56-
- bazel run @regression_testing//:outdated
56+
- tests/bazel_run_tests.sh
5757
test_targets:
5858
- "--"
5959
- "//..."
@@ -68,7 +68,7 @@ tasks:
6868
- bazel run @unpinned_regression_testing//:pin
6969
- bazel run @unpinned_maven_install_in_custom_location//:pin
7070
- bazel run @duplicate_artifacts_test//:pin
71-
- bazel run @regression_testing//:outdated
71+
- tests/bazel_run_tests.sh
7272
test_targets:
7373
- "--"
7474
- "//..."
@@ -81,7 +81,7 @@ tasks:
8181
- bazel run @unpinned_regression_testing//:pin
8282
- bazel run @unpinned_maven_install_in_custom_location//:pin
8383
- bazel run @duplicate_artifacts_test//:pin
84-
- bazel run @regression_testing//:outdated
84+
- tests/bazel_run_tests.sh
8585
test_flags:
8686
- "--//settings:stamp_manifest=True"
8787
test_targets:
@@ -98,7 +98,7 @@ tasks:
9898
- bazel run @unpinned_regression_testing//:pin
9999
- bazel run @unpinned_maven_install_in_custom_location//:pin
100100
- bazel run @duplicate_artifacts_test//:pin
101-
- bazel run @regression_testing//:outdated
101+
- tests/bazel_run_tests.sh
102102
test_targets:
103103
- "--"
104104
- "//..."

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Table of Contents
3636
* [Hiding transitive dependencies](#hiding-transitive-dependencies)
3737
* [Fetch and resolve timeout](#fetch-and-resolve-timeout)
3838
* [Jetifier](#jetifier)
39+
* [Duplicate artifact warning](#duplicate-artifact-warning)
3940
* [Exporting and consuming artifacts from external repositories](#exporting-and-consuming-artifacts-from-external-repositories)
4041
* [Publishing to external repositories](#publishing-to-external-repositories)
4142
* [Demo](#demo)
@@ -893,6 +894,22 @@ maven_install(
893894
)
894895
```
895896

897+
### Duplicate artifact warning
898+
899+
By default you will be warned if there are duplicate artifacts in your artifact list. The `duplicate_version_warning` setting can be used to change this behavior. Use "none" to disable the warning and "error" to fail the build instead of warn.
900+
901+
```python
902+
maven_install(
903+
artifacts = [
904+
# ...
905+
],
906+
repositories = [
907+
# ...
908+
],
909+
duplicate_version_warning = "error"
910+
)
911+
```
912+
896913
### Provide JVM options for Coursier with `COURSIER_OPTS`
897914

898915
You can set up `COURSIER_OPTS` environment variable to provide some additional JVM options for Coursier.

WORKSPACE

+27
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,33 @@ maven_install(
363363
],
364364
)
365365

366+
maven_install(
367+
name = "duplicate_version_warning",
368+
artifacts = [
369+
"com.fasterxml.jackson.core:jackson-annotations:2.10.1",
370+
"com.fasterxml.jackson.core:jackson-annotations:2.12.1",
371+
"com.fasterxml.jackson.core:jackson-annotations:2.10.1",
372+
"com.fasterxml.jackson.core:jackson-annotations:2.11.2",
373+
"com.github.jnr:jffi:1.3.4",
374+
maven.artifact(
375+
group = "com.github.jnr",
376+
artifact = "jffi",
377+
version = "1.3.3",
378+
classifier = "native",
379+
),
380+
maven.artifact(
381+
group = "com.github.jnr",
382+
artifact = "jffi",
383+
version = "1.3.2",
384+
classifier = "native",
385+
),
386+
],
387+
repositories = [
388+
"https://repo1.maven.org/maven2",
389+
"https://maven.google.com",
390+
],
391+
)
392+
366393
maven_install(
367394
name = "starlark_aar_import_test",
368395
artifacts = [

coursier.bzl

+57
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ def _pinned_coursier_fetch_impl(repository_ctx):
372372
for artifact in repository_ctx.attr.artifacts:
373373
artifacts.append(json_parse(artifact))
374374

375+
_check_artifacts_are_unique(artifacts, repository_ctx.attr.duplicate_version_warning)
376+
375377
# Read Coursier state from maven_install.json.
376378
repository_ctx.symlink(
377379
repository_ctx.path(repository_ctx.attr.maven_install_json),
@@ -606,6 +608,31 @@ def _deduplicate_artifacts(dep_tree):
606608
dep_tree.update({"dependencies": deduped_artifacts.values() + null_artifacts})
607609
return dep_tree
608610

611+
def _check_artifacts_are_unique(artifacts, duplicate_version_warning):
612+
if duplicate_version_warning == "none":
613+
return
614+
seen_artifacts = {}
615+
duplicate_artifacts = {}
616+
for artifact in artifacts:
617+
artifact_coordinate = artifact["group"] + ":" + artifact["artifact"] + (":%s" % artifact["classifier"] if artifact.get("classifier") != None else "")
618+
if artifact_coordinate in seen_artifacts:
619+
if artifact_coordinate in duplicate_artifacts:
620+
duplicate_artifacts[artifact_coordinate].append(artifact["version"])
621+
else:
622+
duplicate_artifacts[artifact_coordinate] = [artifact["version"]]
623+
else:
624+
seen_artifacts[artifact_coordinate] = artifact["version"]
625+
626+
if duplicate_artifacts:
627+
msg_parts = ["Found duplicate artifact versions"]
628+
for duplicate in duplicate_artifacts:
629+
msg_parts.append(" {} has multiple versions {}".format(duplicate, ", ".join([seen_artifacts[duplicate]] + duplicate_artifacts[duplicate])))
630+
msg_parts.append("Please remove duplicate artifacts from the artifact list so you do not get unexpected artifact versions")
631+
if duplicate_version_warning == "error":
632+
fail("\n".join(msg_parts))
633+
else:
634+
print("\n".join(msg_parts))
635+
609636
# Get the path to the cache directory containing Coursier-downloaded artifacts.
610637
#
611638
# This method is public for testing.
@@ -779,6 +806,8 @@ def _coursier_fetch_impl(repository_ctx):
779806
for artifact in repository_ctx.attr.artifacts:
780807
artifacts.append(json_parse(artifact))
781808

809+
_check_artifacts_are_unique(artifacts, repository_ctx.attr.duplicate_version_warning)
810+
782811
excluded_artifacts = []
783812
for artifact in repository_ctx.attr.excluded_artifacts:
784813
excluded_artifacts.append(json_parse(artifact))
@@ -1093,6 +1122,20 @@ pinned_coursier_fetch = repository_rule(
10931122
"fail_if_repin_required": attr.bool(doc = "Whether to fail the build if the maven_artifact inputs have changed but the lock file has not been repinned.", default = False),
10941123
"use_starlark_android_rules": attr.bool(default = False, doc = "Whether to use the native or Starlark version of the Android rules."),
10951124
"aar_import_bzl_label": attr.string(default = DEFAULT_AAR_IMPORT_LABEL, doc = "The label (as a string) to use to import aar_import from"),
1125+
"duplicate_version_warning": attr.string(
1126+
doc = """What to do if there are duplicate artifacts
1127+
1128+
If "error", then print a message and fail the build.
1129+
If "warn", then print a warning and continue.
1130+
If "none", then do nothing.
1131+
""",
1132+
default = "warn",
1133+
values = [
1134+
"error",
1135+
"warn",
1136+
"none",
1137+
],
1138+
),
10961139
},
10971140
implementation = _pinned_coursier_fetch_impl,
10981141
)
@@ -1138,6 +1181,20 @@ coursier_fetch = repository_rule(
11381181
"jetify_include_list": attr.string_list(doc = "List of artifacts that need to be jetified in `groupId:artifactId` format. By default all artifacts are jetified if `jetify` is set to True.", default = JETIFY_INCLUDE_LIST_JETIFY_ALL),
11391182
"use_starlark_android_rules": attr.bool(default = False, doc = "Whether to use the native or Starlark version of the Android rules."),
11401183
"aar_import_bzl_label": attr.string(default = DEFAULT_AAR_IMPORT_LABEL, doc = "The label (as a string) to use to import aar_import from"),
1184+
"duplicate_version_warning": attr.string(
1185+
doc = """What to do if there are duplicate artifacts
1186+
1187+
If "error", then print a message and fail the build.
1188+
If "warn", then print a warning and continue.
1189+
If "none", then do nothing.
1190+
""",
1191+
default = "warn",
1192+
values = [
1193+
"error",
1194+
"warn",
1195+
"none",
1196+
],
1197+
),
11411198
},
11421199
environ = [
11431200
"JAVA_HOME",

defs.bzl

+7-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def maven_install(
4242
additional_netrc_lines = [],
4343
fail_if_repin_required = False,
4444
use_starlark_android_rules = False,
45-
aar_import_bzl_label = DEFAULT_AAR_IMPORT_LABEL):
45+
aar_import_bzl_label = DEFAULT_AAR_IMPORT_LABEL,
46+
duplicate_version_warning = "warn"):
4647
"""Resolves and fetches artifacts transitively from Maven repositories.
4748
4849
This macro runs a repository rule that invokes the Coursier CLI to resolve
@@ -86,6 +87,9 @@ def maven_install(
8687
not use the typical default repository name to import the Android
8788
Starlark rules. Default is
8889
"@build_bazel_rules_android//rules:rules.bzl".
90+
duplicate_version_warning: What to do if an artifact is specified multiple times. If "error" then
91+
fail the build, if "warn" then print a message and continue, if "none" then do nothing. The default
92+
is "warn".
8993
"""
9094
repositories_json_strings = []
9195
for repository in parse.parse_repository_spec_list(repositories):
@@ -136,6 +140,7 @@ def maven_install(
136140
jetify_include_list = jetify_include_list,
137141
use_starlark_android_rules = use_starlark_android_rules,
138142
aar_import_bzl_label = aar_import_bzl_label,
143+
duplicate_version_warning = duplicate_version_warning,
139144
)
140145

141146
if maven_install_json != None:
@@ -154,6 +159,7 @@ def maven_install(
154159
jetify_include_list = jetify_include_list,
155160
additional_netrc_lines = additional_netrc_lines,
156161
fail_if_repin_required = fail_if_repin_required,
162+
duplicate_version_warning = duplicate_version_warning,
157163
)
158164

159165
def artifact(a, repository_name = DEFAULT_REPOSITORY_NAME):

tests/bazel_run_tests.sh

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash -e
2+
3+
# A simple test framework to verify bazel output without setting up an entire WORKSPACE
4+
# in the bazel sandbox as is done in https://github.com/bazelbuild/bazel/blob/master/src/test/shell/unittest.bash
5+
#
6+
# Add a new test to the TESTS array and send all output to TEST_LOG
7+
8+
function test_duplicate_version_warning() {
9+
bazel run @duplicate_version_warning//:pin >> "$TEST_LOG" 2>&1
10+
11+
expect_log "Found duplicate artifact versions"
12+
expect_log "com.fasterxml.jackson.core:jackson-annotations has multiple versions"
13+
expect_log "com.github.jnr:jffi:native has multiple versions"
14+
}
15+
16+
function test_outdated() {
17+
bazel run @regression_testing//:outdated >> "$TEST_LOG" 2>&1
18+
19+
expect_log "Checking for updates of .* artifacts against the following repositories"
20+
expect_log "junit:junit \[4.12"
21+
}
22+
23+
TESTS=("test_duplicate_version_warning" "test_outdated")
24+
25+
function run_tests() {
26+
printf "Running bazel run tests:\n"
27+
for test in ${TESTS[@]}; do
28+
printf " ${test} "
29+
TEST_LOG=/tmp/${test}_test.log
30+
rm -f "$TEST_LOG"
31+
DUMPED_TEST_LOG=0
32+
${test}
33+
printf "PASSED\n"
34+
rm -f "$TEST_LOG"
35+
done
36+
}
37+
38+
function expect_log() {
39+
local pattern=$1
40+
local message=${2:-Expected regexp "$pattern" not found}
41+
grep -sq -- "$pattern" $TEST_LOG && return 0
42+
43+
printf "FAILED\n"
44+
cat $TEST_LOG
45+
DUMPED_TEST_LOG=1
46+
printf "FAILURE: $message\n"
47+
return 1
48+
}
49+
50+
function exit_handler() {
51+
local exit_code=$?
52+
if [ $exit_code != "0" ] && [ $DUMPED_TEST_LOG == "0" ]; then
53+
printf "ERROR\n"
54+
cat $TEST_LOG
55+
fi
56+
return $exit_code
57+
}
58+
59+
trap exit_handler EXIT
60+
61+
run_tests

0 commit comments

Comments
 (0)