From a1f299a967e81537d09f1257fc7408f33644111a Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 16 Nov 2021 20:32:38 -0800 Subject: [PATCH] Refactor Spring Build Conventions Gradle Plugins with missing features and logic required by SSDG. Format source code. --- .../AbstractSpringJavaPlugin.groovy | 63 ++++++++++--------- .../gradle/convention/DeployDocsPlugin.groovy | 11 ++-- .../gradle/convention/DocsPlugin.groovy | 20 +++++- .../convention/IntegrationTestPlugin.groovy | 10 +-- .../RepositoryConventionPlugin.groovy | 12 +++- .../convention/RootProjectPlugin.groovy | 6 +- ...ependencyManagementConventionPlugin.groovy | 6 +- .../convention/SpringModulePlugin.groovy | 13 ++-- .../convention/SpringSampleBootPlugin.groovy | 6 +- .../convention/SpringSamplePlugin.groovy | 5 +- .../convention/SpringSampleWarPlugin.groovy | 16 ++--- .../gradle/convention/SpringTestPlugin.groovy | 5 +- .../TestsConfigurationPlugin.groovy | 7 ++- .../io/spring/gradle/convention/Utils.groovy | 22 ++++--- 14 files changed, 119 insertions(+), 83 deletions(-) diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy index 629bd47..9d09a31 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/AbstractSpringJavaPlugin.groovy @@ -13,61 +13,68 @@ * License for the specific language governing permissions and limitations under * the License. */ +package io.spring.gradle.convention -package io.spring.gradle.convention; - -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.plugins.GroovyPlugin; +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.GroovyPlugin import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.PluginManager; -import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin; -import org.gradle.plugins.ide.idea.IdeaPlugin; +import org.gradle.api.plugins.PluginManager +import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin +import org.gradle.plugins.ide.idea.IdeaPlugin import org.springframework.gradle.CopyPropertiesPlugin import org.springframework.gradle.propdeps.PropDepsEclipsePlugin import org.springframework.gradle.propdeps.PropDepsIdeaPlugin -import org.springframework.gradle.propdeps.PropDepsPlugin; +import org.springframework.gradle.propdeps.PropDepsPlugin /** * @author Rob Winch */ -public abstract class AbstractSpringJavaPlugin implements Plugin { +abstract class AbstractSpringJavaPlugin implements Plugin { @Override - public final void apply(Project project) { - PluginManager pluginManager = project.getPluginManager(); - pluginManager.apply(JavaPlugin.class); + final void apply(Project project) { + + PluginManager pluginManager = project.getPluginManager() + + pluginManager.apply(JavaPlugin.class) pluginManager.apply(ManagementConfigurationPlugin.class) + if (project.file("src/main/groovy").exists() || project.file("src/test/groovy").exists() || project.file("src/integration-test/groovy").exists()) { - pluginManager.apply(GroovyPlugin.class); + + pluginManager.apply(GroovyPlugin.class) } - pluginManager.apply("io.spring.convention.repository"); - pluginManager.apply(EclipseWtpPlugin); - pluginManager.apply(IdeaPlugin); - pluginManager.apply(PropDepsPlugin); - pluginManager.apply(PropDepsEclipsePlugin); - pluginManager.apply(PropDepsIdeaPlugin); - pluginManager.apply("io.spring.convention.tests-configuration"); - pluginManager.apply("io.spring.convention.integration-test"); - pluginManager.apply("io.spring.convention.springdependencymangement"); - pluginManager.apply("io.spring.convention.javadoc-options"); - pluginManager.apply("io.spring.convention.checkstyle"); - pluginManager.apply(CopyPropertiesPlugin); + + pluginManager.apply("io.spring.convention.repository") + pluginManager.apply(EclipseWtpPlugin) + pluginManager.apply(IdeaPlugin) + pluginManager.apply(PropDepsPlugin) + pluginManager.apply(PropDepsEclipsePlugin) + pluginManager.apply(PropDepsIdeaPlugin) + pluginManager.apply("io.spring.convention.tests-configuration") + pluginManager.apply("io.spring.convention.integration-test") + pluginManager.apply("io.spring.convention.springdependencymangement") + pluginManager.apply("io.spring.convention.dependency-set") + pluginManager.apply("io.spring.convention.javadoc-options") + pluginManager.apply("io.spring.convention.checkstyle") + pluginManager.apply(CopyPropertiesPlugin) project.jar { - manifest.attributes["Created-By"] = - "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" + manifest.attributes["Created-By"] = "${System.getProperty("java.version")} (${System.getProperty("java.specification.vendor")})" manifest.attributes["Implementation-Title"] = project.name manifest.attributes["Implementation-Version"] = project.version manifest.attributes["Automatic-Module-Name"] = project.name.replace('-', '.') } + project.test { useJUnitPlatform() } + additionalPlugins(project); } protected abstract void additionalPlugins(Project project); + } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy index bea5c25..37d9da4 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DeployDocsPlugin.groovy @@ -13,23 +13,22 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.tasks.bundling.Zip import org.gradle.api.Plugin import org.gradle.api.Project -public class DeployDocsPlugin implements Plugin { +class DeployDocsPlugin implements Plugin { @Override - public void apply(Project project) { + void apply(Project project) { + project.getPluginManager().apply('org.hidetake.ssh') project.ssh.settings { knownHosts = allowAnyHosts } + project.remotes { docs { role 'docs' @@ -80,4 +79,4 @@ public class DeployDocsPlugin implements Plugin { } } } -} \ No newline at end of file +} diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy index d0a64ab..9834f10 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/DocsPlugin.groovy @@ -1,5 +1,6 @@ package io.spring.gradle.convention +import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.Task @@ -10,18 +11,31 @@ import org.gradle.api.tasks.bundling.Zip /** * Aggregates asciidoc, javadoc, and deploying of the docs into a single plugin */ -public class DocsPlugin implements Plugin { +class DocsPlugin implements Plugin { @Override - public void apply(Project project) { + void apply(Project project) { PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(BasePlugin); + pluginManager.apply("org.asciidoctor.jvm.convert"); + pluginManager.apply("org.asciidoctor.jvm.pdf"); + pluginManager.apply(AsciidoctorConventionPlugin); pluginManager.apply(DeployDocsPlugin); pluginManager.apply(JavadocApiPlugin); + project.tasks.withType(AbstractAsciidoctorTask) { t -> + project.configure(t) { + sources { + include "**/*.adoc" + exclude '_*/**' + } + } + } + Task docsZip = project.tasks.create('docsZip', Zip) { - dependsOn 'api' + dependsOn 'api', 'asciidoctor' group = 'Distribution' archiveBaseName = project.rootProject.name archiveClassifier = 'docs' diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy index 5afd960..6b190d5 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/IntegrationTestPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import org.gradle.api.Plugin @@ -39,20 +38,22 @@ import org.springframework.gradle.propdeps.PropDepsPlugin * * @author Rob Winch */ -public class IntegrationTestPlugin implements Plugin { +class IntegrationTestPlugin implements Plugin { @Override - public void apply(Project project) { + void apply(Project project) { project.plugins.withType(JavaPlugin.class) { applyJava(project) } } private applyJava(Project project) { - if(!project.file('src/integration-test/').exists()) { + + if (!project.file('src/integration-test/').exists()) { // ensure we don't add if no tests to avoid adding Gretty return } + project.configurations { integrationTestCompile { extendsFrom testImplementation @@ -80,6 +81,7 @@ public class IntegrationTestPlugin implements Plugin { shouldRunAfter project.tasks.test useJUnitPlatform() } + project.tasks.check.dependsOn integrationTestTask project.plugins.withType(IdeaPlugin) { diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy index c242081..03601a3 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RepositoryConventionPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Plugin @@ -23,23 +22,28 @@ class RepositoryConventionPlugin implements Plugin { @Override void apply(Project project) { + String[] forceMavenRepositories = ((String) project.findProperty("forceMavenRepositories"))?.split(',') + boolean isImplicitSnapshotRepository = forceMavenRepositories == null && Utils.isSnapshot(project) boolean isImplicitMilestoneRepository = forceMavenRepositories == null && Utils.isMilestone(project) - boolean isSnapshot = isImplicitSnapshotRepository || forceMavenRepositories?.contains('snapshot') boolean isMilestone = isImplicitMilestoneRepository || forceMavenRepositories?.contains('milestone') project.repositories { + if (forceMavenRepositories?.contains('local')) { mavenLocal() } + mavenCentral() + jcenter() { content { includeGroup "org.gretty" } } + if (isSnapshot) { maven { name = 'artifactory-snapshot' @@ -52,6 +56,7 @@ class RepositoryConventionPlugin implements Plugin { url = 'https://repo.spring.io/snapshot/' } } + if (isSnapshot || isMilestone) { maven { name = 'artifactory-milestone' @@ -64,6 +69,7 @@ class RepositoryConventionPlugin implements Plugin { url = 'https://repo.spring.io/milestone/' } } + maven { name = 'artifactory-release' if (project.hasProperty('artifactoryUsername')) { @@ -74,11 +80,11 @@ class RepositoryConventionPlugin implements Plugin { } url = 'https://repo.spring.io/release/' } + maven { name = 'shibboleth' url = 'https://build.shibboleth.net/nexus/content/repositories/releases/' } } } - } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy index 3f7bada..01212bc 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/RootProjectPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import io.spring.nohttp.gradle.NoHttpPlugin @@ -27,7 +26,9 @@ class RootProjectPlugin implements Plugin { @Override void apply(Project project) { + PluginManager pluginManager = project.getPluginManager() + pluginManager.apply(BasePlugin) pluginManager.apply(SchemaPlugin) pluginManager.apply(NoHttpPlugin) @@ -46,6 +47,7 @@ class RootProjectPlugin implements Plugin { } String projectName = Utils.getProjectName(project) + project.sonarqube { properties { property "sonar.java.coveragePlugin", "jacoco" @@ -62,9 +64,9 @@ class RootProjectPlugin implements Plugin { project.tasks.create("dependencyManagementExport", DependencyManagementExportTask) def finalizeDeployArtifacts = project.task("finalizeDeployArtifacts") + if (Utils.isRelease(project) && project.hasProperty("ossrhUsername")) { finalizeDeployArtifacts.dependsOn project.tasks.closeAndReleaseOssrhStagingRepository } } - } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy index 9030617..20ded1a 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringDependencyManagementConventionPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import io.spring.gradle.dependencymanagement.DependencyManagementPlugin @@ -32,18 +31,23 @@ class SpringDependencyManagementConventionPlugin implements Plugin { @Override void apply(Project project) { + project.getPluginManager().apply(ManagementConfigurationPlugin) project.getPluginManager().apply(DependencyManagementPlugin) + project.dependencyManagement { resolutionStrategy { cacheChangingModulesFor 0, "seconds" } } + File rootDir = project.rootDir List dependencyManagementFiles = [project.rootProject.file(DEPENDENCY_MANAGEMENT_RESOURCE)] + for (File dir = project.projectDir; dir != rootDir; dir = dir.parentFile) { dependencyManagementFiles.add(new File(dir, DEPENDENCY_MANAGEMENT_RESOURCE)) } + dependencyManagementFiles.each { f -> if (f.exists()) { project.apply from: f.absolutePath diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy index 36a7013..cba3a01 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringModulePlugin.groovy @@ -13,14 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ - -package io.spring.gradle.convention; +package io.spring.gradle.convention import org.gradle.api.Project -import org.gradle.api.plugins.JavaLibraryPlugin; -import org.gradle.api.plugins.MavenPlugin; +import org.gradle.api.plugins.JavaLibraryPlugin import org.gradle.api.plugins.PluginManager -import org.springframework.gradle.maven.SpringMavenPlugin; +import org.springframework.gradle.maven.SpringMavenPlugin /** * @author Rob Winch @@ -29,17 +27,20 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin { @Override void additionalPlugins(Project project) { + PluginManager pluginManager = project.getPluginManager(); + pluginManager.apply(JavaLibraryPlugin.class) pluginManager.apply(SpringMavenPlugin.class); pluginManager.apply("io.spring.convention.jacoco"); def deployArtifacts = project.task("deployArtifacts") + deployArtifacts.group = 'Deploy tasks' deployArtifacts.description = "Deploys the artifacts to either Artifactory or Maven Central" + if (!Utils.isRelease(project)) { deployArtifacts.dependsOn project.tasks.artifactoryPublish } } - } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy index c79e6b4..e947e0a 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleBootPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import org.gradle.api.Project @@ -22,10 +21,11 @@ import org.gradle.api.plugins.PluginManager /** * @author Rob Winch */ -public class SpringSampleBootPlugin extends SpringSamplePlugin { +class SpringSampleBootPlugin extends SpringSamplePlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { + super.additionalPlugins(project); PluginManager pluginManager = project.getPluginManager(); diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy index f41a0ca..3714c06 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSamplePlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Project @@ -22,10 +21,10 @@ import org.sonarqube.gradle.SonarQubePlugin; /** * @author Rob Winch */ -public class SpringSamplePlugin extends AbstractSpringJavaPlugin { +class SpringSamplePlugin extends AbstractSpringJavaPlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { project.plugins.withType(SonarQubePlugin) { project.sonarqube.skipProject = true } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy index 6128f9c..7eed436 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringSampleWarPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention import org.gradle.api.Project @@ -24,10 +23,11 @@ import org.gradle.api.tasks.testing.Test /** * @author Rob Winch */ -public class SpringSampleWarPlugin extends SpringSamplePlugin { +class SpringSampleWarPlugin extends SpringSamplePlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { + super.additionalPlugins(project); PluginManager pluginManager = project.getPluginManager(); @@ -36,7 +36,7 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { pluginManager.apply("org.gretty"); project.gretty { - servletContainer = 'tomcat85' + servletContainer = 'tomcat10' contextPath = '/' fileLogEnabled = false } @@ -64,6 +64,7 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { } def applyForIntegrationTest(Project project, Task integrationTest) { + project.gretty.integrationTestTask = integrationTest.name integrationTest.doFirst { @@ -83,16 +84,15 @@ public class SpringSampleWarPlugin extends SpringSamplePlugin { integrationTest.systemProperty 'app.baseURI', baseUrl integrationTest.systemProperty 'app.httpBaseURI', httpBaseUrl integrationTest.systemProperty 'app.httpsBaseURI', httpsBaseUrl - integrationTest.systemProperty 'geb.build.baseUrl', baseUrl integrationTest.systemProperty 'geb.build.reportsDir', 'build/geb-reports' } } def getRandomPort() { - ServerSocket ss = new ServerSocket(0) - int port = ss.localPort - ss.close() + ServerSocket serverSocket = new ServerSocket(0) + int port = serverSocket.localPort + serverSocket.close() return port } } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy index 55807dc..42466af 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/SpringTestPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Project; @@ -21,10 +20,10 @@ import org.gradle.api.Project; /** * @author Rob Winch */ -public class SpringTestPlugin extends AbstractSpringJavaPlugin { +class SpringTestPlugin extends AbstractSpringJavaPlugin { @Override - public void additionalPlugins(Project project) { + void additionalPlugins(Project project) { project.sonarqube.skipProject = true } } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy index f8c2ddd..7b89f0d 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/TestsConfigurationPlugin.groovy @@ -13,7 +13,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package io.spring.gradle.convention; import org.gradle.api.Plugin @@ -30,15 +29,17 @@ import org.gradle.jvm.tasks.Jar * * @author Rob Winch */ -public class TestsConfigurationPlugin implements Plugin { +class TestsConfigurationPlugin implements Plugin { + @Override - public void apply(Project project) { + void apply(Project project) { project.plugins.withType(JavaPlugin) { applyJavaProject(project) } } private void applyJavaProject(Project project) { + project.configurations { tests.extendsFrom testRuntime, testRuntimeClasspath } diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy index 8f5a6a9..830986c 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/Utils.groovy @@ -2,33 +2,35 @@ package io.spring.gradle.convention; import org.gradle.api.Project; -public class Utils { +class Utils { + + private Utils() {} static String getProjectName(Project project) { + String projectName = project.getRootProject().getName(); + if(projectName.endsWith("-build")) { projectName = projectName.substring(0, projectName.length() - "-build".length()); } + return projectName; } - static boolean isSnapshot(Project project) { - String projectVersion = projectVersion(project) - return projectVersion.matches('^.*([.-]BUILD)?-SNAPSHOT$') - } - static boolean isMilestone(Project project) { - String projectVersion = projectVersion(project) - return projectVersion.matches('^.*[.-]M\\d+$') || projectVersion.matches('^.*[.-]RC\\d+$') + return projectVersion(project).matches('^.*[.-]M\\d+$') + || projectVersion(project).matches('^.*[.-]RC\\d+$') } static boolean isRelease(Project project) { return !(isSnapshot(project) || isMilestone(project)) } + static boolean isSnapshot(Project project) { + return projectVersion(project).matches('^.*([.-]BUILD)?-SNAPSHOT$') + } + private static String projectVersion(Project project) { return String.valueOf(project.getVersion()); } - - private Utils() {} }