5858 */
5959public abstract class CheckBom extends DefaultTask {
6060
61- private final Provider <ResolvedBom > resolvedBom ;
62-
63- private final ConfigurationContainer configurations ;
64-
65- private final DependencyHandler dependencies ;
66-
6761 private final BomExtension bom ;
6862
69- private final BomResolver bomResolver ;
63+ private final List < LibraryCheck > checks ;
7064
7165 @ Inject
7266 public CheckBom (BomExtension bom ) {
73- this .configurations = getProject ().getConfigurations ();
74- this .dependencies = getProject ().getDependencies ();
67+ ConfigurationContainer configurations = getProject ().getConfigurations ();
68+ DependencyHandler dependencies = getProject ().getDependencies ();
69+ Provider <ResolvedBom > resolvedBom = getResolvedBomFile ().map (RegularFile ::getAsFile ).map (ResolvedBom ::readFrom );
70+ this .checks = List .of (new CheckExclusions (configurations , dependencies ), new CheckProhibitedVersions (),
71+ new CheckVersionAlignment (),
72+ new CheckDependencyManagementAlignment (resolvedBom , configurations , dependencies ));
7573 this .bom = bom ;
76- this .resolvedBom = getResolvedBomFile ().map (RegularFile ::getAsFile ).map (ResolvedBom ::readFrom );
77- this .bomResolver = new BomResolver (this .configurations , this .dependencies );
7874 }
7975
8076 @ InputFile
8177 @ PathSensitive (PathSensitivity .RELATIVE )
82- abstract RegularFileProperty getResolvedBomFile ();
78+ public abstract RegularFileProperty getResolvedBomFile ();
8379
8480 @ TaskAction
8581 void checkBom () {
8682 List <String > errors = new ArrayList <>();
8783 for (Library library : this .bom .getLibraries ()) {
88- checkLibrary (library , errors );
84+ errors . addAll ( checkLibrary (library ) );
8985 }
9086 if (!errors .isEmpty ()) {
9187 System .out .println ();
@@ -95,165 +91,229 @@ void checkBom() {
9591 }
9692 }
9793
98- private void checkLibrary (Library library , List < String > errors ) {
94+ private List < String > checkLibrary (Library library ) {
9995 List <String > libraryErrors = new ArrayList <>();
100- checkExclusions (library , libraryErrors );
101- checkProhibitedVersions (library , libraryErrors );
102- checkVersionAlignment (library , libraryErrors );
103- checkDependencyManagementAlignment (library , libraryErrors );
96+ this .checks .stream ().flatMap ((check ) -> check .check (library ).stream ()).forEach (libraryErrors ::add );
97+ List <String > errors = new ArrayList <>();
10498 if (!libraryErrors .isEmpty ()) {
10599 errors .add (library .getName ());
106100 for (String libraryError : libraryErrors ) {
107101 errors .add (" - " + libraryError );
108102 }
109103 }
104+ return errors ;
105+ }
106+
107+ private interface LibraryCheck {
108+
109+ List <String > check (Library library );
110+
110111 }
111112
112- private void checkExclusions (Library library , List <String > errors ) {
113- for (Group group : library .getGroups ()) {
114- for (Module module : group .getModules ()) {
115- if (!module .getExclusions ().isEmpty ()) {
116- checkExclusions (group .getId (), module , library .getVersion ().getVersion (), errors );
113+ private static final class CheckExclusions implements LibraryCheck {
114+
115+ private final ConfigurationContainer configurations ;
116+
117+ private final DependencyHandler dependencies ;
118+
119+ private CheckExclusions (ConfigurationContainer configurations , DependencyHandler dependencies ) {
120+ this .configurations = configurations ;
121+ this .dependencies = dependencies ;
122+ }
123+
124+ @ Override
125+ public List <String > check (Library library ) {
126+ List <String > errors = new ArrayList <>();
127+ for (Group group : library .getGroups ()) {
128+ for (Module module : group .getModules ()) {
129+ if (!module .getExclusions ().isEmpty ()) {
130+ checkExclusions (group .getId (), module , library .getVersion ().getVersion (), errors );
131+ }
117132 }
118133 }
134+ return errors ;
119135 }
120- }
121136
122- private void checkExclusions (String groupId , Module module , DependencyVersion version , List <String > errors ) {
123- Set <String > resolved = this .configurations
124- .detachedConfiguration (this .dependencies .create (groupId + ":" + module .getName () + ":" + version ))
125- .getResolvedConfiguration ()
126- .getResolvedArtifacts ()
127- .stream ()
128- .map ((artifact ) -> artifact .getModuleVersion ().getId ())
129- .map ((id ) -> id .getGroup () + ":" + id .getModule ().getName ())
130- .collect (Collectors .toSet ());
131- Set <String > exclusions = module .getExclusions ()
132- .stream ()
133- .map ((exclusion ) -> exclusion .getGroupId () + ":" + exclusion .getArtifactId ())
134- .collect (Collectors .toSet ());
135- Set <String > unused = new TreeSet <>();
136- for (String exclusion : exclusions ) {
137- if (!resolved .contains (exclusion )) {
138- if (exclusion .endsWith (":*" )) {
139- String group = exclusion .substring (0 , exclusion .indexOf (':' ) + 1 );
140- if (resolved .stream ().noneMatch ((candidate ) -> candidate .startsWith (group ))) {
137+ private void checkExclusions (String groupId , Module module , DependencyVersion version , List <String > errors ) {
138+ Set <String > resolved = this .configurations
139+ .detachedConfiguration (this .dependencies .create (groupId + ":" + module .getName () + ":" + version ))
140+ .getResolvedConfiguration ()
141+ .getResolvedArtifacts ()
142+ .stream ()
143+ .map ((artifact ) -> artifact .getModuleVersion ().getId ())
144+ .map ((id ) -> id .getGroup () + ":" + id .getModule ().getName ())
145+ .collect (Collectors .toSet ());
146+ Set <String > exclusions = module .getExclusions ()
147+ .stream ()
148+ .map ((exclusion ) -> exclusion .getGroupId () + ":" + exclusion .getArtifactId ())
149+ .collect (Collectors .toSet ());
150+ Set <String > unused = new TreeSet <>();
151+ for (String exclusion : exclusions ) {
152+ if (!resolved .contains (exclusion )) {
153+ if (exclusion .endsWith (":*" )) {
154+ String group = exclusion .substring (0 , exclusion .indexOf (':' ) + 1 );
155+ if (resolved .stream ().noneMatch ((candidate ) -> candidate .startsWith (group ))) {
156+ unused .add (exclusion );
157+ }
158+ }
159+ else {
141160 unused .add (exclusion );
142161 }
143162 }
144- else {
145- unused .add (exclusion );
146- }
163+ }
164+ exclusions .removeAll (resolved );
165+ if (!unused .isEmpty ()) {
166+ errors .add ("Unnecessary exclusions on " + groupId + ":" + module .getName () + ": " + exclusions );
147167 }
148168 }
149- exclusions .removeAll (resolved );
150- if (!unused .isEmpty ()) {
151- errors .add ("Unnecessary exclusions on " + groupId + ":" + module .getName () + ": " + exclusions );
152- }
169+
153170 }
154171
155- private void checkProhibitedVersions (Library library , List <String > errors ) {
156- ArtifactVersion currentVersion = new DefaultArtifactVersion (library .getVersion ().getVersion ().toString ());
157- for (ProhibitedVersion prohibited : library .getProhibitedVersions ()) {
158- if (prohibited .isProhibited (library .getVersion ().getVersion ().toString ())) {
159- errors .add ("Current version " + currentVersion + " is prohibited" );
160- }
161- else {
162- VersionRange versionRange = prohibited .getRange ();
163- if (versionRange != null ) {
164- for (Restriction restriction : versionRange .getRestrictions ()) {
165- ArtifactVersion upperBound = restriction .getUpperBound ();
166- if (upperBound == null ) {
167- return ;
168- }
169- int comparison = currentVersion .compareTo (upperBound );
170- if ((restriction .isUpperBoundInclusive () && comparison <= 0 )
171- || ((!restriction .isUpperBoundInclusive ()) && comparison < 0 )) {
172- return ;
173- }
172+ private static final class CheckProhibitedVersions implements LibraryCheck {
173+
174+ @ Override
175+ public List <String > check (Library library ) {
176+ List <String > errors = new ArrayList <>();
177+ ArtifactVersion currentVersion = new DefaultArtifactVersion (library .getVersion ().getVersion ().toString ());
178+ for (ProhibitedVersion prohibited : library .getProhibitedVersions ()) {
179+ if (prohibited .isProhibited (library .getVersion ().getVersion ().toString ())) {
180+ errors .add ("Current version " + currentVersion + " is prohibited" );
181+ }
182+ else {
183+ VersionRange versionRange = prohibited .getRange ();
184+ if (versionRange != null ) {
185+ check (currentVersion , versionRange , errors );
174186 }
175- errors .add ("Version range " + versionRange + " is ineffective as the current version, "
176- + currentVersion + ", is greater than its upper bound" );
177187 }
178188 }
189+ return errors ;
179190 }
180- }
181191
182- private void checkVersionAlignment (Library library , List <String > errors ) {
183- VersionAlignment versionAlignment = library .getVersionAlignment ();
184- if (versionAlignment == null ) {
185- return ;
192+ private void check (ArtifactVersion currentVersion , VersionRange versionRange , List <String > errors ) {
193+ for (Restriction restriction : versionRange .getRestrictions ()) {
194+ ArtifactVersion upperBound = restriction .getUpperBound ();
195+ if (upperBound == null ) {
196+ return ;
197+ }
198+ int comparison = currentVersion .compareTo (upperBound );
199+ if ((restriction .isUpperBoundInclusive () && comparison <= 0 )
200+ || ((!restriction .isUpperBoundInclusive ()) && comparison < 0 )) {
201+ return ;
202+ }
203+ }
204+ errors .add ("Version range " + versionRange + " is ineffective as the current version, " + currentVersion
205+ + ", is greater than its upper bound" );
186206 }
187- Set <String > alignedVersions = versionAlignment .resolve ();
188- if (alignedVersions .size () == 1 ) {
189- String alignedVersion = alignedVersions .iterator ().next ();
190- if (!alignedVersion .equals (library .getVersion ().getVersion ().toString ())) {
191- errors .add ("Version " + library .getVersion ().getVersion () + " is misaligned. It should be "
192- + alignedVersion + "." );
207+
208+ }
209+
210+ private static final class CheckVersionAlignment implements LibraryCheck {
211+
212+ @ Override
213+ public List <String > check (Library library ) {
214+ List <String > errors = new ArrayList <>();
215+ VersionAlignment versionAlignment = library .getVersionAlignment ();
216+ if (versionAlignment != null ) {
217+ check (versionAlignment , library , errors );
193218 }
219+ return errors ;
194220 }
195- else {
196- if (alignedVersions .isEmpty ()) {
197- errors .add ("Version alignment requires a single version but none were found." );
221+
222+ private void check (VersionAlignment versionAlignment , Library library , List <String > errors ) {
223+ Set <String > alignedVersions = versionAlignment .resolve ();
224+ if (alignedVersions .size () == 1 ) {
225+ String alignedVersion = alignedVersions .iterator ().next ();
226+ if (!alignedVersion .equals (library .getVersion ().getVersion ().toString ())) {
227+ errors .add ("Version " + library .getVersion ().getVersion () + " is misaligned. It should be "
228+ + alignedVersion + "." );
229+ }
198230 }
199231 else {
200- errors .add ("Version alignment requires a single version but " + alignedVersions .size () + " were found: "
201- + alignedVersions + "." );
232+ if (alignedVersions .isEmpty ()) {
233+ errors .add ("Version alignment requires a single version but none were found." );
234+ }
235+ else {
236+ errors .add ("Version alignment requires a single version but " + alignedVersions .size ()
237+ + " were found: " + alignedVersions + "." );
238+ }
202239 }
203240 }
204- }
205241
206- private void checkDependencyManagementAlignment (Library library , List <String > errors ) {
207- String alignsWithBom = library .getAlignsWithBom ();
208- if (alignsWithBom == null ) {
209- return ;
210- }
211- Bom mavenBom = this .bomResolver .resolveMavenBom (alignsWithBom + ":" + library .getVersion ().getVersion ());
212- ResolvedBom resolvedBom = this .resolvedBom .get ();
213- Optional <ResolvedLibrary > resolvedLibrary = resolvedBom .libraries ()
214- .stream ()
215- .filter ((candidate ) -> candidate .name ().equals (library .getName ()))
216- .findFirst ();
217- if (!resolvedLibrary .isPresent ()) {
218- throw new RuntimeException ("Library '%s' not found in resolved bom" .formatted (library .getName ()));
219- }
220- checkDependencyManagementAlignment (resolvedLibrary .get (), mavenBom , errors );
221242 }
222243
223- private void checkDependencyManagementAlignment (ResolvedLibrary library , Bom mavenBom , List <String > errors ) {
224- List <Id > managedByLibrary = library .managedDependencies ();
225- List <Id > managedByBom = managedDependenciesOf (mavenBom );
244+ private static final class CheckDependencyManagementAlignment implements LibraryCheck {
226245
227- List <Id > missing = new ArrayList <>(managedByBom );
228- missing .removeAll (managedByLibrary );
246+ private final Provider <ResolvedBom > resolvedBom ;
229247
230- List <Id > unexpected = new ArrayList <>(managedByLibrary );
231- unexpected .removeAll (managedByBom );
232- if (missing .isEmpty () && unexpected .isEmpty ()) {
233- return ;
248+ private final BomResolver bomResolver ;
249+
250+ private CheckDependencyManagementAlignment (Provider <ResolvedBom > resolvedBom ,
251+ ConfigurationContainer configurations , DependencyHandler dependencies ) {
252+ this .resolvedBom = resolvedBom ;
253+ this .bomResolver = new BomResolver (configurations , dependencies );
234254 }
235- String error = "Dependency management does not align with " + mavenBom .id () + ":" ;
236- if (!missing .isEmpty ()) {
237- error = error + "%n - Missing:%n %s" .formatted (String .join ("\n " ,
238- missing .stream ().map ((dependency ) -> dependency .toString ()).toList ()));
255+
256+ @ Override
257+ public List <String > check (Library library ) {
258+ List <String > errors = new ArrayList <>();
259+ String alignsWithBom = library .getAlignsWithBom ();
260+ if (alignsWithBom != null ) {
261+ Bom mavenBom = this .bomResolver
262+ .resolveMavenBom (alignsWithBom + ":" + library .getVersion ().getVersion ());
263+ ResolvedLibrary resolvedLibrary = getResolvedLibrary (library );
264+ checkDependencyManagementAlignment (resolvedLibrary , mavenBom , errors );
265+ }
266+ return errors ;
239267 }
240- if (!unexpected .isEmpty ()) {
241- error = error + "%n - Unexpected:%n %s" .formatted (String .join ("\n " ,
242- unexpected .stream ().map ((dependency ) -> dependency .toString ()).toList ()));
268+
269+ private ResolvedLibrary getResolvedLibrary (Library library ) {
270+ ResolvedBom resolvedBom = this .resolvedBom .get ();
271+ Optional <ResolvedLibrary > resolvedLibrary = resolvedBom .libraries ()
272+ .stream ()
273+ .filter ((candidate ) -> candidate .name ().equals (library .getName ()))
274+ .findFirst ();
275+ if (!resolvedLibrary .isPresent ()) {
276+ throw new RuntimeException ("Library '%s' not found in resolved bom" .formatted (library .getName ()));
277+ }
278+ return resolvedLibrary .get ();
243279 }
244- errors .add (error );
245- }
246280
247- private List <Id > managedDependenciesOf (Bom mavenBom ) {
248- List <Id > managedDependencies = new ArrayList <>();
249- managedDependencies .addAll (mavenBom .managedDependencies ());
250- if (mavenBom .parent () != null ) {
251- managedDependencies .addAll (managedDependenciesOf (mavenBom .parent ()));
281+ private void checkDependencyManagementAlignment (ResolvedLibrary library , Bom mavenBom , List <String > errors ) {
282+ List <Id > managedByLibrary = library .managedDependencies ();
283+ List <Id > managedByBom = managedDependenciesOf (mavenBom );
284+
285+ List <Id > missing = new ArrayList <>(managedByBom );
286+ missing .removeAll (managedByLibrary );
287+
288+ List <Id > unexpected = new ArrayList <>(managedByLibrary );
289+ unexpected .removeAll (managedByBom );
290+ if (missing .isEmpty () && unexpected .isEmpty ()) {
291+ return ;
292+ }
293+ String error = "Dependency management does not align with " + mavenBom .id () + ":" ;
294+ if (!missing .isEmpty ()) {
295+ error = error + "%n - Missing:%n %s" .formatted (String .join ("\n " ,
296+ missing .stream ().map ((dependency ) -> dependency .toString ()).toList ()));
297+ }
298+ if (!unexpected .isEmpty ()) {
299+ error = error + "%n - Unexpected:%n %s" .formatted (String .join ("\n " ,
300+ unexpected .stream ().map ((dependency ) -> dependency .toString ()).toList ()));
301+ }
302+ errors .add (error );
252303 }
253- for (Bom importedBom : mavenBom .importedBoms ()) {
254- managedDependencies .addAll (managedDependenciesOf (importedBom ));
304+
305+ private List <Id > managedDependenciesOf (Bom mavenBom ) {
306+ List <Id > managedDependencies = new ArrayList <>();
307+ managedDependencies .addAll (mavenBom .managedDependencies ());
308+ if (mavenBom .parent () != null ) {
309+ managedDependencies .addAll (managedDependenciesOf (mavenBom .parent ()));
310+ }
311+ for (Bom importedBom : mavenBom .importedBoms ()) {
312+ managedDependencies .addAll (managedDependenciesOf (importedBom ));
313+ }
314+ return managedDependencies ;
255315 }
256- return managedDependencies ;
316+
257317 }
258318
259319}
0 commit comments