|
5 | 5 | # ============================================================================ |
6 | 6 | # Tests all components of a project affected by changes in its dependencies. |
7 | 7 | # |
8 | | -# First, an anecdote illustrating the problem this script solves: |
| 8 | +# In particular, this script detects problems caused by diamond dependency |
| 9 | +# structures: |
9 | 10 | # |
10 | | -# Suppose you have a large application, org:app:1.0.0, with many dependencies: |
11 | | -# org:foo:1.2.3, org:bar:3.4.5, and many others. |
| 11 | +# https://jlbp.dev/what-is-a-diamond-dependency-conflict |
12 | 12 | # |
13 | | -# Now suppose you make some changes to foo, and want to know whether deploying |
14 | | -# them (i.e., releasing a new foo and updating app to depend on that release) |
15 | | -# will break the app. So you manually update your local copy of app to depend |
16 | | -# on org:foo:1.3.0-SNAPSHOT, and run the build (including tests, of course). |
| 13 | +# This "melting pot" build rebuilds every dependency of a project, but at |
| 14 | +# unified dependency versions matching those of the toplevel project. |
17 | 15 | # |
18 | | -# The build passes, but this alone is insufficient: org:bar:3.4.5 also depends |
19 | | -# on org:foo:1.2.3, so you manually update bar to use org:foo:1.3.0-SNAPSHOT, |
20 | | -# then build bar to verify that it also is not broken by the update. |
| 16 | +# For example, net.imagej:imagej:2.5.0 depends on many components including |
| 17 | +# org.scijava:scijava-common:2.88.1 and net.imagej:imagej-ops:0.46.1, |
| 18 | +# both of which depend on org.scijava:parsington. But: |
21 | 19 | # |
22 | | -# This process quickly becomes very tedious when there are dozens of |
23 | | -# components of app which all depend on foo. |
| 20 | +# - org.scijava:scijava-common:2.88.1 depends on org.scijava:parsington:3.0.0 |
| 21 | +# - net.imagej:imagej-ops:0.46.1 depends on org.scijava:parsington:2.0.0 |
24 | 22 | # |
25 | | -# And more importantly, testing each component individually in this manner is |
26 | | -# still insufficient to determine whether all of them will truly work together |
27 | | -# at runtime, where only a single version of each component is deployed. |
| 23 | +# ImageJ2 can only depend on one of these versions at runtime. The newer one, |
| 24 | +# ideally. SciJava projects use the pom-scijava parent POM as a Bill of |
| 25 | +# Materials (BOM) to declare these winning versions, which works great... |
| 26 | +# EXCEPT for when newer versions break backwards compatibility, as happened |
| 27 | +# here: it's a SemVer-versioned project at different major version numbers. |
28 | 28 | # |
29 | | -# For example: suppose org:bar:3.4.5 depends on org:lib:8.0.0, while |
30 | | -# org:foo:1.2.3 depends on org:lib:7.0.0. The relevant facts are: |
| 29 | +# Enter this melting-pot script. It rebuilds each project dependency from |
| 30 | +# source and runs the unit tests, but with all dependency versions pinned to |
| 31 | +# match those of the toplevel project. |
31 | 32 | # |
32 | | -# * Your new foo (org:foo:1.3.0-SNAPSHOT) builds against lib 7, and portions |
33 | | -# of it rely on lib-7-specific API. |
| 33 | +# So in the example above, this script: |
34 | 34 | # |
35 | | -# * The bar component pinned to foo 1.3.0-SNAPSHOT builds against lib 8; it |
36 | | -# compiles with passing tests because bar only invokes portions of the foo |
37 | | -# API which do not require lib-7-specific API. |
| 35 | +# 1. gathers the dependencies of net.imagej:imagej:2.5.0; |
| 36 | +# 2. clones each dependency from SCM at the correct release tag; |
| 37 | +# 3. rebuilds each dependency, but with dependency versions overridden to |
| 38 | +# those of net.imagej:imagej:2.5.0 rather than those originally used for |
| 39 | +# that dependency at that release. |
38 | 40 | # |
39 | | -# In this scenario, it is lib 8 that is actually deployed at runtime with the |
40 | | -# app, so parts of foo will be broken, even though both foo and bar build with |
41 | | -# passing tests individually. |
42 | | -# |
43 | | -# This "melting pot" build seeks to overcome many of these issues by unifying |
44 | | -# all components of the app into a single multi-module build, with all |
45 | | -# versions uniformly pinned to the ones that will actually be deployed at |
46 | | -# runtime. |
47 | | -# |
48 | | -# This goal is achieved by synthesizing a multi-module build including all |
49 | | -# affected components (or optionally, all components period) of the specific |
50 | | -# project, and then executing a Maven build with uniformly overridden versions |
51 | | -# of all components to the ones resolved for the project itself. |
| 41 | +# So e.g. in the above scenario, net.imagej:imagej-ops:0.46.1 will be rebuilt |
| 42 | +# against org.scijava:parsington:3.0.0, and we will discover whether any of |
| 43 | +# parsington's breaking API changes from 2.0.0 to 3.0.0 actually impact the |
| 44 | +# compilation or (tested) runtime behavior of imagej-ops. |
52 | 45 | # |
53 | 46 | # IMPORTANT IMPLEMENTATION DETAIL! The override works by setting a version |
54 | 47 | # property for each component of the form "artifactId.version"; it is assumed |
|
0 commit comments