From 00d3a0e90901e0a6c7e336a7b4131f5f263f022f Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Fri, 22 Mar 2024 20:41:04 +0100 Subject: [PATCH] [MENFORCER-500] New rule to enforce that Maven coordinates match given patterns Optionally allows to enforce the module directory name being equal to the module's artifactId. --- .../rules/RequireMatchingCoordinates.java | 110 ++++++++++++++++++ enforcer-rules/src/site/apt/index.apt | 2 + .../apt/requireMatchingCoordinates.apt.vm | 75 ++++++++++++ .../require-dependency-scope/verify.groovy | 5 +- .../require-matching-coordinates/mod2/pom.xml | 43 +++++++ .../require-matching-coordinates/pom.xml | 75 ++++++++++++ .../test-multimodule-mod1/pom.xml | 37 ++++++ .../verify.groovy | 23 ++++ 8 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/RequireMatchingCoordinates.java create mode 100644 enforcer-rules/src/site/apt/requireMatchingCoordinates.apt.vm create mode 100644 maven-enforcer-plugin/src/it/projects/require-matching-coordinates/mod2/pom.xml create mode 100644 maven-enforcer-plugin/src/it/projects/require-matching-coordinates/pom.xml create mode 100644 maven-enforcer-plugin/src/it/projects/require-matching-coordinates/test-multimodule-mod1/pom.xml create mode 100644 maven-enforcer-plugin/src/it/projects/require-matching-coordinates/verify.groovy diff --git a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/RequireMatchingCoordinates.java b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/RequireMatchingCoordinates.java new file mode 100644 index 00000000..f5aff822 --- /dev/null +++ b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/RequireMatchingCoordinates.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.maven.enforcer.rules; + +import javax.inject.Inject; +import javax.inject.Named; + +import java.util.Objects; +import java.util.regex.Pattern; + +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.project.MavenProject; + +/** + * This rule checks that the Maven coordinates (i.e. the project's {@code groupId} and {@code artifactId}) each match a given pattern. + * @since 3.5.0 + */ +@Named("requireMatchingCoordinates") +public final class RequireMatchingCoordinates extends AbstractStandardEnforcerRule { + + private Pattern groupIdPattern; + + private Pattern artifactIdPattern; + + private boolean moduleNameMustMatchArtifactId; + + private final MavenProject project; + + @Inject + public RequireMatchingCoordinates(MavenProject project) { + this.project = Objects.requireNonNull(project); + } + + @Override + public void execute() throws EnforcerRuleException { + StringBuilder msgBuilder = new StringBuilder(); + if (groupIdPattern != null + && !groupIdPattern.matcher(project.getGroupId()).matches()) { + msgBuilder + .append("Group ID must match pattern \"") + .append(groupIdPattern) + .append("\" but is \"") + .append(project.getGroupId()) + .append("\""); + } + if (artifactIdPattern != null + && !artifactIdPattern.matcher(project.getArtifactId()).matches()) { + if (msgBuilder.length() > 0) { + msgBuilder.append(System.lineSeparator()); + } + msgBuilder + .append("Artifact ID must match pattern \"") + .append(artifactIdPattern) + .append("\" but is \"") + .append(project.getArtifactId()) + .append("\""); + } + if (moduleNameMustMatchArtifactId + && !project.isExecutionRoot() + && !project.getBasedir().getName().equals(project.getArtifactId())) { + if (msgBuilder.length() > 0) { + msgBuilder.append(System.lineSeparator()); + } + msgBuilder + .append("Module directory name must be equal to its artifact ID \"") + .append(project.getArtifactId()) + .append("\" but is \"") + .append(project.getBasedir().getName()) + .append("\""); + } + if (msgBuilder.length() > 0) { + throw new EnforcerRuleException(msgBuilder.toString()); + } + } + + public void setGroupIdPattern(String groupIdPattern) { + this.groupIdPattern = Pattern.compile(groupIdPattern); + } + + public void setArtifactIdPattern(String artifactIdPattern) { + this.artifactIdPattern = Pattern.compile(artifactIdPattern); + } + + public void setModuleNameMustMatchArtifactId(boolean moduleNameMustMatchArtifactId) { + this.moduleNameMustMatchArtifactId = moduleNameMustMatchArtifactId; + } + + @Override + public String toString() { + return String.format( + "requireMatchingCoordinates[groupIdPattern=%s, artifactIdPattern=%s, moduleNameMustMatchArtifactId=%b]", + groupIdPattern, artifactIdPattern, moduleNameMustMatchArtifactId); + } +} diff --git a/enforcer-rules/src/site/apt/index.apt b/enforcer-rules/src/site/apt/index.apt index ac384bac..bdf21ae7 100644 --- a/enforcer-rules/src/site/apt/index.apt +++ b/enforcer-rules/src/site/apt/index.apt @@ -73,6 +73,8 @@ Built-In Rules * {{{./requireJavaVersion.html}requireJavaVersion}} - enforces the JDK version. + * {{{./requireMatchingCoordinates.html}requireMatchingCoordinates}} - enforces specific group ID and/or artifact ID patterns. + * {{{./requireMavenVersion.html}requireMavenVersion}} - enforces the Maven version. * {{{./requireNoRepositories.html}requireNoRepositories}} - enforces to not include repositories. diff --git a/enforcer-rules/src/site/apt/requireMatchingCoordinates.apt.vm b/enforcer-rules/src/site/apt/requireMatchingCoordinates.apt.vm new file mode 100644 index 00000000..906d61fe --- /dev/null +++ b/enforcer-rules/src/site/apt/requireMatchingCoordinates.apt.vm @@ -0,0 +1,75 @@ +~~ Licensed to the Apache Software Foundation (ASF) under one +~~ or more contributor license agreements. See the NOTICE file +~~ distributed with this work for additional information +~~ regarding copyright ownership. The ASF licenses this file +~~ to you 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. + + ------ + Require Matching Coordinates + ------ + Konrad Windszus + ------ + 2024-03-22 + ------ + +Require Matching Coordinates + + This rule checks that the Maven coordinates (i.e. the project's <<>> and <<>>) each match a given pattern. + Optionally one can also enforce that in a multi-module build the module directory name is always equal to the module's <<>>. + + The following parameters are supported by this rule: + + * <> - an optional message to the user if the rule fails. If not set a default message will be used. + + * <> - an optional {{{https://docs.oracle.com/javase/tutorial/essential/regex/}regular expression}}, which must match the project's <<>>. If not set there is no check on the <<>>. + + * <> - an optional {{{https://docs.oracle.com/javase/tutorial/essential/regex/}regular expression}}, which must match the project's <<>>. If not set there is no check on the <<>>. + + * <> - boolean flag to enforce that the the module's directory name is always equal to the module's <<>>. By default <<>>. + + [] + + + Sample Plugin Configuration: + ++---+ + + [...] + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${project.version} + + + enforce-group-id + + enforce + + + + + com\.example.\namespace\..* + + + + + + + + + [...] + ++---+ diff --git a/maven-enforcer-plugin/src/it/projects/require-dependency-scope/verify.groovy b/maven-enforcer-plugin/src/it/projects/require-dependency-scope/verify.groovy index f4871fd6..159f4dbd 100644 --- a/maven-enforcer-plugin/src/it/projects/require-dependency-scope/verify.groovy +++ b/maven-enforcer-plugin/src/it/projects/require-dependency-scope/verify.groovy @@ -17,5 +17,6 @@ * under the License. */ File buildLog = new File(basedir, 'build.log') -assert buildLog.text.contains('[ERROR] Dependency org.apache.jackrabbit.vault:vault-cli:jar @ line 65, column 21 does not have an explicit scope defined!') -assert buildLog.text.contains('Found 1 missing dependency scope. Look at the errors emitted above for the details.') +def log = buildLog.text.normalize() +assert log.contains('[ERROR] Dependency org.apache.jackrabbit.vault:vault-cli:jar @ line 65, column 21 does not have an explicit scope defined!') +assert log.contains('Found 1 missing dependency scope. Look at the errors emitted above for the details.') diff --git a/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/mod2/pom.xml b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/mod2/pom.xml new file mode 100644 index 00000000..baa58568 --- /dev/null +++ b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/mod2/pom.xml @@ -0,0 +1,43 @@ + + + + + + 4.0.0 + + + org.apache.maven.its.enforcer + test-multimodule-project + 1.0 + + + invalid-test-multimodule-mod2 + + + + + + + ${project.groupId} + test-multimodule-mod1 + + + + diff --git a/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/pom.xml b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/pom.xml new file mode 100644 index 00000000..0797df8e --- /dev/null +++ b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/pom.xml @@ -0,0 +1,75 @@ + + + + + + 4.0.0 + + org.apache.maven.its.enforcer + test-multimodule-project + 1.0 + pom + + + + + + test-multimodule-mod1 + mod2 + + + + + + ${project.groupId} + test-multimodule-mod1 + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + @project.version@ + + + test + + enforce + + + + + org\.apache\.maven\.its\.enforcer\.somepackage + test-.* + true + + + false + + + + + + + diff --git a/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/test-multimodule-mod1/pom.xml b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/test-multimodule-mod1/pom.xml new file mode 100644 index 00000000..4165af77 --- /dev/null +++ b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/test-multimodule-mod1/pom.xml @@ -0,0 +1,37 @@ + + + + + + 4.0.0 + + + org.apache.maven.its.enforcer + test-multimodule-project + 1.0 + + + org.apache.maven.its.enforcer.somepackage + test-multimodule-mod1 + + + + + diff --git a/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/verify.groovy b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/verify.groovy new file mode 100644 index 00000000..4d39372f --- /dev/null +++ b/maven-enforcer-plugin/src/it/projects/require-matching-coordinates/verify.groovy @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +File buildLog = new File(basedir, 'build.log') + +assert buildLog.text.contains('[WARNING] Rule 0: org.apache.maven.enforcer.rules.RequireMatchingCoordinates failed with message:\nGroup ID must match pattern "org\\.apache\\.maven\\.its\\.enforcer\\.somepackage" but is "org.apache.maven.its.enforcer"\n') +assert buildLog.text.contains('[WARNING] Rule 0: org.apache.maven.enforcer.rules.RequireMatchingCoordinates failed with message:\nGroup ID must match pattern "org\\.apache\\.maven\\.its\\.enforcer\\.somepackage" but is "org.apache.maven.its.enforcer"\nArtifact ID must match pattern "test-.*" but is "invalid-test-multimodule-mod2"\nModule directory name must be equal to its artifact ID "invalid-test-multimodule-mod2" but is "mod2"\n')