-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
backport of helidon enforcer rules to 2.x
- Loading branch information
Showing
13 changed files
with
1,108 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Helidon Build Common Maven Enforcer Rules | ||
|
||
This module provides common Helidon enforcer rules intended to integrate with the [Maven Enforcer Plugin](https://maven.apache.org/enforcer/maven-enforcer-plugin/). | ||
|
||
## Rules | ||
* [HelidonDependenciesRule](src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java) - Verifies compile and runtime maven dependencies (including transitive dependencies) for compatibility with Helidon. | ||
|
||
### General usage | ||
|
||
Here is an example pom.xml: | ||
|
||
```xml | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-enforcer-plugin</artifactId> | ||
<dependencies> | ||
<dependency> | ||
<groupId>io.helidon.build-tools</groupId> | ||
<artifactId>helidon-build-maven-enforcer-rules</artifactId> | ||
<version>${helidon.build-tools.version}</version> | ||
</dependency> | ||
</dependencies> | ||
<executions> | ||
<execution> | ||
<id>enforce-helidon-dependencies</id> | ||
<goals> | ||
<goal>enforce</goal> | ||
</goals> | ||
<configuration> | ||
<rules> | ||
<helidonDependenciesRule> | ||
<namespace>JAKARTA | JAVAX</namespace> | ||
<!-- list of strings - can be used to exclude a package / group name from validation --> | ||
<excludedGavRegExs> | ||
<!-- for example only - we suggest not including this exclusion in your usage --> | ||
<excludedGavRegEx>javax.servlet.*</excludedGavRegEx> | ||
</excludedGavs> | ||
</helidonJakartaDependenciesRule> | ||
</rules> | ||
</configuration> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
... | ||
``` | ||
|
||
```bash | ||
mvn package | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
Copyright (c) 2023 Oracle and/or its affiliates. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
|
||
<FindBugsFilter | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xmlns="https://github.com/spotbugs/filter/3.0.0" | ||
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd"> | ||
|
||
<Match> | ||
<!-- maven injected value --> | ||
<Class name="io.helidon.build.maven.enforcer.rules.HelidonDependenciesRule"/> | ||
<Bug pattern="NP_UNWRITTEN_FIELD"/> | ||
</Match> | ||
|
||
<Match> | ||
<!-- maven injected value --> | ||
<Class name="io.helidon.build.maven.enforcer.rules.HelidonDependenciesRule"/> | ||
<Bug pattern="UWF_UNWRITTEN_FIELD"/> | ||
</Match> | ||
|
||
</FindBugsFilter> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!-- | ||
Copyright (c) 2021, 2023 Oracle and/or its affiliates. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
--> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>io.helidon.build-tools</groupId> | ||
<artifactId>helidon-build-tools-project</artifactId> | ||
<version>2.0.0-SNAPSHOT</version> | ||
</parent> | ||
<artifactId>helidon-build-maven-enforcer-rules</artifactId> | ||
<name>Helidon Build Tools Maven Enforcer Rules</name> | ||
|
||
<properties> | ||
<!-- Target bytecode version of rule must be 1.8 or 11 - due to MENFORCER-473 | ||
- see https://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html --> | ||
<maven.compiler.source>11</maven.compiler.source> | ||
<maven.compiler.target>11</maven.compiler.target> | ||
|
||
<spotbugs.exclude>etc/spotbugs/exclude.xml</spotbugs.exclude> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.maven</groupId> | ||
<artifactId>maven-artifact</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.maven.enforcer</groupId> | ||
<artifactId>enforcer-api</artifactId> | ||
<optional>true</optional> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.apache.maven</groupId> | ||
<artifactId>maven-core</artifactId> | ||
<optional>true</optional> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter-api</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.hamcrest</groupId> | ||
<artifactId>hamcrest-all</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.codehaus.plexus</groupId> | ||
<artifactId>plexus-component-metadata</artifactId> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>generate-metadata</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
223 changes: 223 additions & 0 deletions
223
...cer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
/* | ||
* Copyright (c) 2023 Oracle and/or its affiliates. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package io.helidon.build.maven.enforcer.rules; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.UncheckedIOException; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Properties; | ||
import java.util.function.Function; | ||
import java.util.regex.Pattern; | ||
import java.util.stream.Collectors; | ||
|
||
import org.apache.maven.artifact.Artifact; | ||
import org.apache.maven.artifact.DefaultArtifact; | ||
import org.apache.maven.artifact.versioning.ArtifactVersion; | ||
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; | ||
import org.apache.maven.artifact.versioning.OverConstrainedVersionException; | ||
import org.apache.maven.artifact.versioning.VersionRange; | ||
|
||
/** | ||
* A function that will return {@code true} if the given maven coordinate is valid. | ||
*/ | ||
class DependencyIsValidCheck implements Function<Artifact, Boolean> { | ||
static final String JAKARTA_RENAMED = "jakarta-renamed.properties"; | ||
static final String JAKARTA_VERSIONS = "jakarta-versions.properties"; | ||
static final String JAKARTA_GROUPS = "jakarta-groups.properties"; | ||
|
||
private static final Map<String, VersionRange> PACKAGE_TO_VERSIONS = loadVersions(); | ||
private static final Map<String, String> PACKAGE_TO_RENAMED = loadRenamed(); | ||
private static final Map<String, String> GROUP_TO_PACKAGE = loadGroups(); | ||
|
||
private final String namespace; | ||
private final List<Pattern> excludedGavRegExs; | ||
|
||
DependencyIsValidCheck(String namespace, | ||
List<Pattern> excludedGavRegExs) { | ||
this.namespace = namespace; | ||
this.excludedGavRegExs = excludedGavRegExs; | ||
} | ||
|
||
@Override | ||
public Boolean apply(Artifact gav) { | ||
if (isExcluded(toSimpleGav(gav))) { | ||
return true; | ||
} | ||
|
||
String groupPackageName = toPackage(gav.getGroupId()); | ||
if (groupPackageName.equals("javax.servlet") | ||
|| groupPackageName.equals("jakarta.servlet")) { | ||
return false; | ||
} | ||
|
||
try { | ||
if (HelidonDependenciesRule.JAKARTA.equalsIgnoreCase(namespace)) { | ||
return applyJakartaRule(groupPackageName, gav.getSelectedVersion()); | ||
} else if (HelidonDependenciesRule.JAVAX.equalsIgnoreCase(namespace)) { | ||
return applyJavaxRule(groupPackageName, gav.getSelectedVersion()); | ||
} else { | ||
throw new IllegalStateException("Invalid namespace: " + namespace); | ||
} | ||
} catch (OverConstrainedVersionException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
|
||
/** | ||
* Checks the given maven GAV. | ||
* | ||
* @param gav the maven GAV | ||
* @return true if the GAV is not in violation | ||
*/ | ||
public boolean apply(String gav) { | ||
return apply(toArtifact(gav)); | ||
} | ||
|
||
/** | ||
* Validates the provided maven GAVs. If any are invalid an exception is thrown. | ||
* | ||
* @param gavs the array of maven GAVs | ||
* @throws ViolationException if a passed GAV is in violation of Helidon's usage policy | ||
*/ | ||
public void validate(String... gavs) throws ViolationException { | ||
validate(Arrays.stream(gavs).map(DependencyIsValidCheck::toArtifact).collect(Collectors.toList())); | ||
} | ||
|
||
/** | ||
* Validates the provided maven GAVs. If any are invalid an exception is thrown. | ||
* | ||
* @param gavs the collection of maven GAVs | ||
* @throws ViolationException if a passed GAV is in violation of Helidon's usage policy | ||
*/ | ||
public void validate(Collection<Artifact> gavs) throws ViolationException { | ||
List<String> violations = new ArrayList<>(); | ||
for (Artifact gav : gavs) { | ||
if (!apply(gav)) { | ||
violations.add(toSimpleGav(gav)); | ||
} | ||
} | ||
|
||
if (!violations.isEmpty()) { | ||
throw new ViolationException("Bad dependencies spotted (review with mvn dependency:tree): " + violations, violations); | ||
} | ||
} | ||
|
||
boolean applyJakartaRule(String groupPackageName, | ||
ArtifactVersion version) { | ||
if (groupPackageName.startsWith("javax.")) { | ||
String renamedPackage = PACKAGE_TO_RENAMED.get(groupPackageName); | ||
return (renamedPackage == null); | ||
} else if (groupPackageName.startsWith("jakarta.")) { | ||
VersionRange versionRange = PACKAGE_TO_VERSIONS.get(groupPackageName); | ||
if (versionRange != null) { | ||
return versionRange.containsVersion(version); | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
boolean applyJavaxRule(String groupPackageName, | ||
ArtifactVersion version) { | ||
if (groupPackageName.startsWith("jakarta.")) { | ||
VersionRange versionRange = PACKAGE_TO_VERSIONS.get(groupPackageName); | ||
if (versionRange != null) { | ||
return !versionRange.containsVersion(version); | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Converts a group name to a package name. | ||
* | ||
* @param group the group name | ||
* @return the associated package name | ||
*/ | ||
String toPackage(String group) { | ||
String packageName = GROUP_TO_PACKAGE.get(group); | ||
return (packageName == null) ? group : packageName; | ||
|
||
} | ||
|
||
boolean isExcluded(String groupPackageName) { | ||
for (Pattern pattern : excludedGavRegExs) { | ||
if (pattern.matcher(groupPackageName).matches()) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
static DefaultArtifact toArtifact(String gav) { | ||
String[] split = gav.split(":"); | ||
DefaultArtifact artifact = new DefaultArtifact(split[0], | ||
split.length > 1 ? split[1] : "", // artifact | ||
split.length > 2 ? split[2] : null, // version | ||
split.length > 3 ? split[3] : "compile", // scope | ||
split.length > 4 ? split[4] : "jar", // type | ||
split.length > 5 ? split[5] : "", // classifier | ||
null); // handler | ||
return artifact; | ||
} | ||
|
||
static Map<String, VersionRange> loadVersions() { | ||
Map<String, VersionRange> result = new LinkedHashMap<>(); | ||
load(JAKARTA_VERSIONS).forEach((k, v) -> { | ||
try { | ||
result.put(k.toString(), VersionRange.createFromVersionSpec(v.toString())); | ||
} catch (InvalidVersionSpecificationException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
}); | ||
return result; | ||
} | ||
|
||
static Map<String, String> loadRenamed() { | ||
Map<String, String> result = new LinkedHashMap<>(); | ||
load(JAKARTA_RENAMED).forEach((k, v) -> result.put(k.toString(), v.toString())); | ||
return result; | ||
} | ||
|
||
static Map<String, String> loadGroups() { | ||
Map<String, String> result = new LinkedHashMap<>(); | ||
load(JAKARTA_GROUPS).forEach((k, v) -> result.put(k.toString(), v.toString())); | ||
return result; | ||
} | ||
|
||
static Properties load(String name) { | ||
Properties props = new Properties(); | ||
try (InputStream is = DependencyIsValidCheck.class.getClassLoader() | ||
.getResourceAsStream(name)) { | ||
props.load(is); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
return props; | ||
} | ||
|
||
static String toSimpleGav(Artifact gav) { | ||
return gav.getGroupId() + ":" + gav.getArtifactId() + ":" + gav.getVersion(); | ||
} | ||
|
||
} |
Oops, something went wrong.