diff --git a/.gitignore b/.gitignore index 116162cd..c7ab86eb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ build/ .gradle/ buildSrc/.kotlin/ +# Ignore Gradle wrapper jar file +gradle/wrapper/gradle-wrapper.jar +gradle/wrapper/gradle-wrapper-*.sha256 + # Maven **/.mvn/wrapper/maven-wrapper.jar target/ @@ -13,3 +17,5 @@ target/ out/ conformance/conformance-server.pid + +/.env diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index 1c660122..6bb4ce70 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -18,7 +18,7 @@ plugins { `java-platform` `maven-publish` signing - `cel-conventions` + id("cel-conventions") } dependencies { diff --git a/buildSrc/build.gradle.kts b/build-logic/build.gradle.kts similarity index 100% rename from buildSrc/build.gradle.kts rename to build-logic/build.gradle.kts diff --git a/buildSrc/settings.gradle.kts b/build-logic/settings.gradle.kts similarity index 100% rename from buildSrc/settings.gradle.kts rename to build-logic/settings.gradle.kts diff --git a/buildSrc/src/main/kotlin/CodeCoverage.kt b/build-logic/src/main/kotlin/CodeCoverage.kt similarity index 100% rename from buildSrc/src/main/kotlin/CodeCoverage.kt rename to build-logic/src/main/kotlin/CodeCoverage.kt diff --git a/buildSrc/src/main/kotlin/Ide.kt b/build-logic/src/main/kotlin/Ide.kt similarity index 76% rename from buildSrc/src/main/kotlin/Ide.kt rename to build-logic/src/main/kotlin/Ide.kt index 05f0bb52..4b8ebb5b 100644 --- a/buildSrc/src/main/kotlin/Ide.kt +++ b/build-logic/src/main/kotlin/Ide.kt @@ -31,11 +31,10 @@ import org.jetbrains.gradle.ext.settings fun Project.nessieIde() { apply() - if (this == rootProject) { + if (path == ":") { - val projectName = rootProject.file("ide-name.txt").readText().trim() - val ideName = - "$projectName ${rootProject.version.toString().replace(Regex("^([0-9.]+).*"), "$1")}" + val projectName = layout.settingsDirectory.file("ide-name.txt").asFile.readText().trim() + val ideName = "$projectName ${version.toString().replace(Regex("^([0-9.]+).*"), "$1")}" apply() configure { @@ -51,7 +50,11 @@ fun Project.nessieIde() { profiles.create("Nessie-ASF") { // strip trailing LF val copyrightText = - rootProject.file("codestyle/copyright-header.txt").readLines().joinToString("\n") + layout.settingsDirectory + .file("codestyle/copyright-header.txt") + .asFile + .readLines() + .joinToString("\n") notice = copyrightText } } @@ -63,8 +66,9 @@ fun Project.nessieIde() { defaults = true jvmArgs = - rootProject.projectDir - .resolve("gradle.properties") + layout.settingsDirectory + .file("gradle.properties") + .asFile .reader() .use { val rules = java.util.Properties() @@ -81,10 +85,13 @@ fun Project.nessieIde() { // There's no proper way to set the name of the IDEA project (when "just importing" or syncing // the Gradle project) - val ideaDir = projectDir.resolve(".idea") + val ideaDir = layout.projectDirectory.dir(".idea").asFile - if (ideaDir.isDirectory) { - ideaDir.resolve(".name").writeText(ideName) + if (java.lang.Boolean.getBoolean("idea.sync.active") && ideaDir.isDirectory) { + val ideaNameFile = ideaDir.resolve(".name") + if (!ideaNameFile.isFile || ideaNameFile.readText() != ideName) { + ideaNameFile.writeText(ideName) + } } configure { project { name = ideName } } diff --git a/buildSrc/src/main/kotlin/Java.kt b/build-logic/src/main/kotlin/Java.kt similarity index 98% rename from buildSrc/src/main/kotlin/Java.kt rename to build-logic/src/main/kotlin/Java.kt index d9da69bb..9a0e1196 100644 --- a/buildSrc/src/main/kotlin/Java.kt +++ b/build-logic/src/main/kotlin/Java.kt @@ -65,7 +65,7 @@ fun Project.nessieConfigureJava() { } } - if (project != rootProject) { + if (path != ":") { tasks.withType().configureEach { duplicatesStrategy = DuplicatesStrategy.WARN } } } diff --git a/buildSrc/src/main/kotlin/ProtobufHelperPlugin.kt b/build-logic/src/main/kotlin/ProtobufHelperPlugin.kt similarity index 100% rename from buildSrc/src/main/kotlin/ProtobufHelperPlugin.kt rename to build-logic/src/main/kotlin/ProtobufHelperPlugin.kt diff --git a/buildSrc/src/main/kotlin/PublishingHelperExtension.kt b/build-logic/src/main/kotlin/PublishingHelperExtension.kt similarity index 100% rename from buildSrc/src/main/kotlin/PublishingHelperExtension.kt rename to build-logic/src/main/kotlin/PublishingHelperExtension.kt diff --git a/build-logic/src/main/kotlin/PublishingHelperPlugin.kt b/build-logic/src/main/kotlin/PublishingHelperPlugin.kt new file mode 100644 index 00000000..e67351a2 --- /dev/null +++ b/build-logic/src/main/kotlin/PublishingHelperPlugin.kt @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2022 The Authors of CEL-Java + * + * 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. + */ + +import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin +import groovy.util.Node +import groovy.util.NodeList +import javax.inject.Inject +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationVariant +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage +import org.gradle.api.component.AdhocComponentWithVariants +import org.gradle.api.component.SoftwareComponentFactory +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.tasks.GenerateMavenPom +import org.gradle.api.publish.tasks.GenerateModuleMetadata +import org.gradle.api.tasks.PathSensitivity +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.withType +import org.gradle.plugins.signing.SigningExtension +import org.gradle.plugins.signing.SigningPlugin + +/** Applies common configurations to all Nessie projects. */ +@Suppress("unused") +class PublishingHelperPlugin +@Inject +constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Plugin { + override fun apply(project: Project): Unit = project.run { + extensions.create("publishingHelper", PublishingHelperExtension::class.java, this) + + apply(plugin = "maven-publish") + apply(plugin = "signing") + + plugins.withId("maven-publish") { + val publication = + extensions.getByType().publications.register( + "maven" + ) { + val parentGroupId = project.group.toString() + val parentVersion = project.version.toString() + + groupId = "$group" + version = parentVersion + + configurePom(project) + + if (path != ":") { + pom.withXml { + val projectNode = asNode() + + val parentNode = projectNode.appendNode("parent") + parentNode.appendNode("groupId", parentGroupId) + parentNode.appendNode("artifactId", "cel-parent") + parentNode.appendNode("version", parentVersion) + } + } + + suppressPomMetadataWarningsFor("testApiElements") + suppressPomMetadataWarningsFor("testJavadocElements") + suppressPomMetadataWarningsFor("testRuntimeElements") + suppressPomMetadataWarningsFor("testSourcesElements") + suppressPomMetadataWarningsFor("testFixturesApiElements") + suppressPomMetadataWarningsFor("testFixturesRuntimeElements") + } + + if (project.plugins.hasPlugin(ShadowPlugin::class.java)) { + publication.configure { configureShadowPublishing(project, this, softwareComponentFactory) } + } else { + plugins.withId("java-platform") { + publication.configure { from(components.getByName("javaPlatform")) } + } + plugins.withId("java") { publication.configure { project.configureJavaPublishing(this) } } + } + + configureSigning(publication.get()) + } + + tasks.named("generatePomFileForMavenPublication", GenerateMavenPom::class.java) { + if (path == ":") { + inputs + .file(layout.settingsDirectory.file("gradle/developers.csv")) + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs + .file(layout.settingsDirectory.file("gradle/contributors.csv")) + .withPathSensitivity(PathSensitivity.RELATIVE) + } + } + + // Gradle complains when a Gradle module metadata ("pom on steroids") is generated with an + // enforcedPlatform() dependency - but Quarkus requires enforcedPlatform(), so we have to + // allow it. + tasks.withType().configureEach { + suppressedValidationErrors.add("enforced-platform") + } + } + + private fun Project.configureJavaPublishing(mavenPublication: MavenPublication) { + val component = components.findByName("java") ?: return + if (component is AdhocComponentWithVariants) { + listOf("testFixturesApiElements", "testFixturesRuntimeElements").forEach { cfg -> + configurations.findByName(cfg)?.apply { + component.addVariantsFromConfiguration(this) { skip() } + } + } + } + mavenPublication.from(component) + } + + private fun MavenPublication.configurePom(project: Project) = project.run { + val e = extensions.getByType() + val pomName = + if (extra.has("maven.name")) { + extra["maven.name"].toString() + } else { + project.name + } + val pomDescription = project.description + val developersFile = layout.settingsDirectory.file("gradle/developers.csv") + val contributorsFile = layout.settingsDirectory.file("gradle/contributors.csv") + + pom { + name.set(pomName) + description.set(pomDescription) + + if (path == ":") { + val repoUrl = e.nessieRepoName.map { "https://github.com/projectnessie/$it" } + val scmUrl = repoUrl.map { "scm:git:$it" } + val developers = + parsePeopleFile( + providers.fileContents(developersFile).asText.get(), + "gradle/developers.csv", + minColumns = 3, + maxColumns = 3, + ) + val contributors = + parsePeopleFile( + providers.fileContents(contributorsFile).asText.get(), + "gradle/contributors.csv", + minColumns = 2, + maxColumns = 2, + ) + + inceptionYear.set(e.inceptionYear) + url.set(repoUrl) + organization { + name.set("Project Nessie") + url.set("https://projectnessie.org") + } + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + mailingLists { + mailingList { + name.set("Project Nessie List") + subscribe.set("projectnessie-subscribe@googlegroups.com") + unsubscribe.set("projectnessie-unsubscribe@googlegroups.com") + post.set("projectnessie@googlegroups.com") + archive.set("https://groups.google.com/g/projectnessie") + } + } + scm { + connection.set(scmUrl) + developerConnection.set(scmUrl) + url.set(repoUrl.map { "$it/tree/main" }) + tag.set("main") + } + issueManagement { + system.set("Github") + url.set(repoUrl.map { "$it/issues" }) + } + developers { + developers.forEach { person -> + developer { + id.set(person[0]) + name.set(person[1]) + url.set(person[2]) + } + } + } + contributors { + contributors.forEach { person -> + contributor { + name.set(person[0]) + url.set(person[1]) + } + } + } + } + } + } + + private fun Project.configureSigning(mavenPublication: MavenPublication) { + if (providers.gradleProperty("release").isPresent) { + plugins.withType().configureEach { + configure { + useInMemoryPgpKeys( + providers.gradleProperty("signingKey").orNull, + providers.gradleProperty("signingPassword").orNull, + ) + sign(mavenPublication) + + if (providers.gradleProperty("useGpgAgent").isPresent) { + useGpgCmd() + } + } + } + } + } + + private fun parsePeopleFile( + text: String, + path: String, + minColumns: Int, + maxColumns: Int, + ): List> = + text + .lineSequence() + .map { line -> line.trim() } + .filter { line -> line.isNotEmpty() && !line.startsWith("#") } + .map { line -> + val args = line.split(",") + if (args.size < minColumns || args.size > maxColumns) { + throw GradleException("$path contains invalid line '${line}'") + } + args + } + .toList() + + private fun xmlNode(node: Node?, child: String): Node? { + val found = node?.get(child) + if (found is NodeList) { + if (found.isNotEmpty()) { + return found[0] as Node + } + } + return null + } +} + +internal fun configureShadowPublishing( + project: Project, + mavenPublication: MavenPublication, + softwareComponentFactory: SoftwareComponentFactory, +) = project.run { + fun isPublishable(element: ConfigurationVariant): Boolean { + for (artifact in element.artifacts) { + if (JavaBasePlugin.UNPUBLISHABLE_VARIANT_ARTIFACTS.contains(artifact.type)) { + return false + } + } + return true + } + + val shadowJar = project.tasks.named("shadowJar") + val shadowProjectDependencies = + project.configurations + .getByName("shadow") + .allDependencies + .withType(ProjectDependency::class.java) + .map { + PomDependency(it.group, it.name, it.version) + } + + val shadowApiElements = + project.configurations.create("shadowApiElements") { + isCanBeConsumed = true + isCanBeResolved = false + attributes { + attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_API)) + attribute( + Category.CATEGORY_ATTRIBUTE, + project.objects.named(Category::class.java, Category.LIBRARY), + ) + attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + project.objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + attribute( + Bundling.BUNDLING_ATTRIBUTE, + project.objects.named(Bundling::class.java, Bundling.SHADOWED), + ) + } + outgoing.artifact(shadowJar) + } + + val component = softwareComponentFactory.adhoc("shadow") + component.addVariantsFromConfiguration(shadowApiElements) { + if (isPublishable(configurationVariant)) { + mapToMavenScope("compile") + } else { + skip() + } + } + // component.addVariantsFromConfiguration(configurations.getByName("runtimeElements")) { + component.addVariantsFromConfiguration( + project.configurations.getByName("shadowRuntimeElements") + ) { + if (isPublishable(configurationVariant)) { + mapToMavenScope("runtime") + } else { + skip() + } + } + // Sonatype requires the javadoc and sources jar to be present, but the + // Shadow extension does not publish those. + component.addVariantsFromConfiguration(project.configurations.getByName("javadocElements")) {} + component.addVariantsFromConfiguration(project.configurations.getByName("sourcesElements")) {} + mavenPublication.from(component) + + // This a replacement to add dependencies to the pom, if necessary. Equivalent to + // 'shadowExtension.component(mavenPublication)', which we cannot use. + + mavenPublication.pom { + withXml { + val node = asNode() + val depNode = node.get("dependencies") + val dependenciesNode = + if ((depNode as NodeList).isNotEmpty()) depNode[0] as Node + else node.appendNode("dependencies") + shadowProjectDependencies.forEach { + val dependencyNode = dependenciesNode.appendNode("dependency") + dependencyNode.appendNode("groupId", it.groupId) + dependencyNode.appendNode("artifactId", it.artifactId) + dependencyNode.appendNode("version", it.version) + dependencyNode.appendNode("scope", "runtime") + } + } + } +} + +private data class PomDependency(val groupId: String?, val artifactId: String, val version: String?) diff --git a/buildSrc/src/main/kotlin/Spotless.kt b/build-logic/src/main/kotlin/Spotless.kt similarity index 70% rename from buildSrc/src/main/kotlin/Spotless.kt rename to build-logic/src/main/kotlin/Spotless.kt index bf945d80..c476bb22 100644 --- a/buildSrc/src/main/kotlin/Spotless.kt +++ b/build-logic/src/main/kotlin/Spotless.kt @@ -16,39 +16,46 @@ import com.diffplug.gradle.spotless.SpotlessExtension import com.diffplug.gradle.spotless.SpotlessPlugin +import com.diffplug.spotless.LineEnding import org.gradle.api.Project import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.withType fun Project.nessieConfigureSpotless() { - apply() if (!java.lang.Boolean.getBoolean("idea.sync.active")) { + val copyrightHeader = layout.settingsDirectory.file("codestyle/copyright-header-java.txt") + val rootProjectOnly = path == ":" + plugins.withType().configureEach { configure { + lineEndings = LineEnding.UNIX + format("xml") { target("src/**/*.xml", "src/**/*.xsd") eclipseWtp(com.diffplug.spotless.extra.wtp.EclipseWtpFormatterStep.XML) - .configFile(rootProject.projectDir.resolve("codestyle/org.eclipse.wst.xml.core.prefs")) + .configFile( + layout.settingsDirectory.file("codestyle/org.eclipse.wst.xml.core.prefs").asFile + ) } kotlinGradle { ktfmt().googleStyle() - licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt"), "$") - if (project == rootProject) { - target("*.gradle.kts", "buildSrc/*.gradle.kts") + licenseHeaderFile(copyrightHeader.asFile, "$") + if (rootProjectOnly) { + target("*.gradle.kts", "build-logic/*.gradle.kts") } } - if (project == rootProject) { + if (rootProjectOnly) { kotlin { ktfmt().googleStyle() - licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt"), "$") - target("buildSrc/src/**/kotlin/**") - targetExclude("buildSrc/build/**") + licenseHeaderFile(copyrightHeader.asFile, "$") + target("build-logic/src/**/kotlin/**") + targetExclude("build-logic/build/**") } } - val dirsInSrc = projectDir.resolve("src").listFiles() + val dirsInSrc = layout.projectDirectory.dir("src").asFile.listFiles() val sourceLangs = if (dirsInSrc != null) dirsInSrc @@ -62,7 +69,7 @@ fun Project.nessieConfigureSpotless() { if (sourceLangs.contains("antlr4")) { antlr4 { - licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt")) + licenseHeaderFile(copyrightHeader.asFile) target("src/**/antlr4/**") targetExclude("build/**") } @@ -70,8 +77,8 @@ fun Project.nessieConfigureSpotless() { if (sourceLangs.contains("java")) { java { googleJavaFormat(libsRequiredVersion("googleJavaFormat")) - licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt")) - target("src/**/java/**") + licenseHeaderFile(copyrightHeader.asFile) + target("src/**/*.java") targetExclude("build/**") } } @@ -79,17 +86,17 @@ fun Project.nessieConfigureSpotless() { scala { scalafmt() licenseHeaderFile( - rootProject.file("codestyle/copyright-header-java.txt"), + copyrightHeader.asFile, "^(package|import) .*$", ) target("src/**/scala/**") - targetExclude("buildSrc/build/**") + targetExclude("build-logic/build/**") } } if (sourceLangs.contains("kotlin")) { kotlin { ktfmt().googleStyle() - licenseHeaderFile(rootProject.file("codestyle/copyright-header-java.txt"), "$") + licenseHeaderFile(copyrightHeader.asFile, "$") target("src/**/kotlin/**") targetExclude("build/**") } diff --git a/buildSrc/src/main/kotlin/Testing.kt b/build-logic/src/main/kotlin/Testing.kt similarity index 100% rename from buildSrc/src/main/kotlin/Testing.kt rename to build-logic/src/main/kotlin/Testing.kt diff --git a/buildSrc/src/main/kotlin/Utilities.kt b/build-logic/src/main/kotlin/Utilities.kt similarity index 100% rename from buildSrc/src/main/kotlin/Utilities.kt rename to build-logic/src/main/kotlin/Utilities.kt diff --git a/buildSrc/src/main/kotlin/cel-conventions.gradle.kts b/build-logic/src/main/kotlin/cel-conventions.gradle.kts similarity index 93% rename from buildSrc/src/main/kotlin/cel-conventions.gradle.kts rename to build-logic/src/main/kotlin/cel-conventions.gradle.kts index 97e3465a..a2324019 100644 --- a/buildSrc/src/main/kotlin/cel-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/cel-conventions.gradle.kts @@ -14,19 +14,19 @@ * limitations under the License. */ -if (project.name != "conformance" && project.name != "jacoco") { - apply() -} - nessieConfigureSpotless() nessieConfigureJava() +if (project.name != "conformance" && project.name != "jacoco") { + apply() +} + nessieIde() apply() -if (projectDir.resolve("src/test/java").exists()) { +if (layout.projectDirectory.dir("src/test/java").asFile.exists()) { nessieConfigureTestTasks() } diff --git a/build.gradle.kts b/build.gradle.kts index 3b350254..a7c18240 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,16 +14,12 @@ * limitations under the License. */ -import java.time.Duration import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers plugins { signing - alias(libs.plugins.testsummary) - alias(libs.plugins.testrerun) - alias(libs.plugins.nmcp) - `cel-conventions` + id("cel-conventions") } mapOf("versionJacoco" to libs.versions.jacoco.get(), "versionJandex" to libs.versions.jandex.get()) @@ -31,29 +27,8 @@ mapOf("versionJacoco" to libs.versions.jacoco.get(), "versionJandex" to libs.ver tasks.named("wrapper") { distributionType = Wrapper.DistributionType.ALL } -// Pass environment variables: -// ORG_GRADLE_PROJECT_sonatypeUsername -// ORG_GRADLE_PROJECT_sonatypePassword -// Gradle targets: -// publishAggregationToCentralPortal -// publishAggregationToCentralPortalSnapshots -// (zipAggregateMavenCentralDeployment to just generate the single, aggregated deployment zip) -// Ref: Maven Central Publisher API: -// https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle -nmcpAggregation { - centralPortal { - username.value(provider { System.getenv("ORG_GRADLE_PROJECT_sonatypeUsername") }) - password.value(provider { System.getenv("ORG_GRADLE_PROJECT_sonatypePassword") }) - publishingType = if (System.getenv("CI") != null) "AUTOMATIC" else "USER_MANAGED" - publishingTimeout = Duration.ofMinutes(120) - validationTimeout = Duration.ofMinutes(120) - publicationName = "${project.name}-$version" - } - publishAllProjectsProbablyBreakingProjectIsolation() -} - -val buildToolIntegrationGradle by - tasks.registering(Exec::class) { +val buildToolIntegrationGradle = + tasks.register("buildToolIntegrationGradle") { group = "Verification" description = "Checks whether bom works fine with Gradle, requires preceding publishToMavenLocal in a separate Gradle invocation" @@ -62,8 +37,8 @@ val buildToolIntegrationGradle by commandLine("./gradlew", "jar", "-Dcel.version=${project.version}") } -val buildToolIntegrationMaven by - tasks.registering(Exec::class) { +val buildToolIntegrationMaven = + tasks.register("buildToolIntegrationMaven") { group = "Verification" description = "Checks whether bom works fine with Maven, requires preceding publishToMavenLocal in a separate Gradle invocation" @@ -72,14 +47,15 @@ val buildToolIntegrationMaven by commandLine("./mvnw", "clean", "package", "-Dcel.version=${project.version}") } -val buildToolIntegrations by tasks.registering { - group = "Verification" - description = - "Checks whether bom works fine with build tools, requires preceding publishToMavenLocal in a separate Gradle invocation" +val buildToolIntegrations = + tasks.register("buildToolIntegrations") { + group = "Verification" + description = + "Checks whether bom works fine with build tools, requires preceding publishToMavenLocal in a separate Gradle invocation" - dependsOn(buildToolIntegrationGradle) - dependsOn(buildToolIntegrationMaven) -} + dependsOn(buildToolIntegrationGradle) + dependsOn(buildToolIntegrationMaven) + } publishingHelper { nessieRepoName.set("cel-java") @@ -104,6 +80,7 @@ tasks.named("wrapper") { val insertAtLine = scriptLines.indexOf("# Use the maximum available, or set MAX_FD != -1 to use that value.") scriptLines.add(insertAtLine, "") + scriptLines.add(insertAtLine, $$"[ -f \"${APP_HOME}/.env\" ] && . \"${APP_HOME}/.env\"") scriptLines.add(insertAtLine, $$". \"${APP_HOME}/gradle/gradlew-include.sh\"") scriptFile.writeText(scriptLines.joinToString("\n")) diff --git a/buildSrc/src/main/kotlin/PublishingHelperPlugin.kt b/buildSrc/src/main/kotlin/PublishingHelperPlugin.kt deleted file mode 100644 index 141549d6..00000000 --- a/buildSrc/src/main/kotlin/PublishingHelperPlugin.kt +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (C) 2022 The Authors of CEL-Java - * - * 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. - */ - -import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin -import groovy.util.Node -import groovy.util.NodeList -import javax.inject.Inject -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ConfigurationVariant -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.component.ModuleComponentSelector -import org.gradle.api.artifacts.result.DependencyResult -import org.gradle.api.attributes.Bundling -import org.gradle.api.attributes.Category -import org.gradle.api.attributes.LibraryElements -import org.gradle.api.attributes.Usage -import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.component.SoftwareComponentFactory -import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.api.publish.PublishingExtension -import org.gradle.api.publish.maven.MavenPublication -import org.gradle.api.publish.tasks.GenerateModuleMetadata -import org.gradle.api.tasks.PathSensitivity -import org.gradle.kotlin.dsl.apply -import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.extra -import org.gradle.kotlin.dsl.provideDelegate -import org.gradle.kotlin.dsl.register -import org.gradle.kotlin.dsl.withType -import org.gradle.plugins.signing.SigningExtension -import org.gradle.plugins.signing.SigningPlugin - -/** Applies common configurations to all Nessie projects. */ -@Suppress("unused") -class PublishingHelperPlugin -@Inject -constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Plugin { - override fun apply(project: Project): Unit = project.run { - extensions.create("publishingHelper", PublishingHelperExtension::class.java, this) - - apply(plugin = "maven-publish") - apply(plugin = "signing") - - plugins.withId("publishing") { - configure { - publications { - register("maven") { - val mavenPublication = this - afterEvaluate { - // This MUST happen in an 'afterEvaluate' to ensure that the Shadow*Plugin has - // been applied. - if (project.plugins.hasPlugin(ShadowPlugin::class.java)) { - configureShadowPublishing(project, mavenPublication, softwareComponentFactory) - } else { - val component = components.firstOrNull { c -> - c.name == "javaPlatform" || c.name == "java" - } - if (component is AdhocComponentWithVariants) { - listOf("testFixturesApiElements", "testFixturesRuntimeElements").forEach { cfg -> - configurations.findByName(cfg)?.apply { - component.addVariantsFromConfiguration(this) { skip() } - } - } - } - from(component) - } - suppressPomMetadataWarningsFor("testApiElements") - suppressPomMetadataWarningsFor("testJavadocElements") - suppressPomMetadataWarningsFor("testRuntimeElements") - suppressPomMetadataWarningsFor("testSourcesElements") - suppressPomMetadataWarningsFor("testFixturesApiElements") - suppressPomMetadataWarningsFor("testFixturesRuntimeElements") - } - - groupId = "$group" - version = project.version.toString() - - tasks.named("generatePomFileForMavenPublication") { - val e = project.extensions.getByType(PublishingHelperExtension::class.java) - - pom { - name.set( - project.provider { - if (project.extra.has("maven.name")) { - project.extra["maven.name"].toString() - } else { - project.name - } - } - ) - description.set(project.description) - if (project != rootProject) { - withXml { - val projectNode = asNode() - - val parentNode = projectNode.appendNode("parent") - parentNode.appendNode("groupId", parent!!.group) - parentNode.appendNode("artifactId", parent!!.name) - parentNode.appendNode("version", parent!!.version) - - addMissingMandatoryDependencyVersions(projectNode) - } - } else { - val nessieRepoName = e.nessieRepoName.get() - - inputs - .file(rootProject.file("gradle/developers.csv")) - .withPathSensitivity(PathSensitivity.RELATIVE) - inputs - .file(rootProject.file("gradle/contributors.csv")) - .withPathSensitivity(PathSensitivity.RELATIVE) - doFirst { - inceptionYear.set(e.inceptionYear.get()) - url.set("https://github.com/projectnessie/$nessieRepoName") - organization { - name.set("Project Nessie") - url.set("https://projectnessie.org") - } - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - mailingLists { - mailingList { - name.set("Project Nessie List") - subscribe.set("projectnessie-subscribe@googlegroups.com") - unsubscribe.set("projectnessie-unsubscribe@googlegroups.com") - post.set("projectnessie@googlegroups.com") - archive.set("https://groups.google.com/g/projectnessie") - } - } - scm { - connection.set("scm:git:https://github.com/projectnessie/$nessieRepoName") - developerConnection.set( - "scm:git:https://github.com/projectnessie/$nessieRepoName" - ) - url.set("https://github.com/projectnessie/$nessieRepoName/tree/main") - tag.set("main") - } - issueManagement { - system.set("Github") - url.set("https://github.com/projectnessie/$nessieRepoName/issues") - } - developers { - file(rootProject.file("gradle/developers.csv")) - .readLines() - .map { line -> line.trim() } - .filter { line -> line.isNotEmpty() && !line.startsWith("#") } - .forEach { line -> - val args = line.split(",") - if (args.size < 3) { - throw GradleException( - "gradle/developers.csv contains invalid line '${line}'" - ) - } - developer { - id.set(args[0]) - name.set(args[1]) - url.set(args[2]) - } - } - } - contributors { - file(rootProject.file("gradle/contributors.csv")) - .readLines() - .map { line -> line.trim() } - .filter { line -> line.isNotEmpty() && !line.startsWith("#") } - .forEach { line -> - val args = line.split(",") - if (args.size > 2) { - throw GradleException( - "gradle/contributors.csv contains invalid line '${line}'" - ) - } - contributor { - name.set(args[0]) - url.set(args[1]) - } - } - } - } - } - } - } - } - } - } - } - - // Gradle complains when a Gradle module metadata ("pom on steroids") is generated with an - // enforcedPlatform() dependency - but Quarkus requires enforcedPlatform(), so we have to - // allow it. - tasks.withType().configureEach { - suppressedValidationErrors.add("enforced-platform") - } - - if (project.hasProperty("release")) { - plugins.withType().configureEach { - configure { - val signingKey: String? by project - val signingPassword: String? by project - useInMemoryPgpKeys(signingKey, signingPassword) - val publishing = project.extensions.getByType(PublishingExtension::class.java) - afterEvaluate { sign(publishing.publications.getByName("maven")) } - - if (project.hasProperty("useGpgAgent")) { - useGpgCmd() - } - } - } - } - } - - /** - * Scans the generated pom.xml for `` in `` that do not have a - * `` and adds one, if possible. Maven kinda requires `` tags there, even if the - * `` without a `` is a bom and that bom's version is available transitively. - */ - private fun Project.addMissingMandatoryDependencyVersions(projectNode: Node) { - xmlNode(xmlNode(projectNode, "dependencyManagement"), "dependencies")?.children()?.forEach { - val dependency = it as Node - if (xmlNode(dependency, "version") == null) { - val depGroup = xmlNode(dependency, "groupId")!!.text() - val depName = xmlNode(dependency, "artifactId")!!.text() - - var depResult = - findDependency(configurations.findByName("runtimeClasspath"), depGroup, depName) - if (depResult == null) { - depResult = - findDependency(configurations.findByName("testRuntimeClasspath"), depGroup, depName) - } - - if (depResult != null) { - val req = depResult.requested as ModuleComponentSelector - dependency.appendNode("version", req.version) - } - } - } - } - - private fun findDependency( - config: Configuration?, - depGroup: String, - depName: String, - ): DependencyResult? { - if (config != null) { - val depResult = - config.incoming.resolutionResult.allDependencies.find { depResult -> - val req = depResult.requested - if (req is ModuleComponentSelector) req.group == depGroup && req.module == depName - else false - } - return depResult - } - return null - } - - private fun xmlNode(node: Node?, child: String): Node? { - val found = node?.get(child) - if (found is NodeList) { - if (found.isNotEmpty()) { - return found[0] as Node - } - } - return null - } -} - -internal fun configureShadowPublishing( - project: Project, - mavenPublication: MavenPublication, - softwareComponentFactory: SoftwareComponentFactory, -) = project.run { - fun isPublishable(element: ConfigurationVariant): Boolean { - for (artifact in element.artifacts) { - if (JavaBasePlugin.UNPUBLISHABLE_VARIANT_ARTIFACTS.contains(artifact.type)) { - return false - } - } - return true - } - - val shadowJar = project.tasks.named("shadowJar") - - val shadowApiElements = - project.configurations.create("shadowApiElements") { - isCanBeConsumed = true - isCanBeResolved = false - attributes { - attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_API)) - attribute( - Category.CATEGORY_ATTRIBUTE, - project.objects.named(Category::class.java, Category.LIBRARY), - ) - attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - project.objects.named(LibraryElements::class.java, LibraryElements.JAR), - ) - attribute( - Bundling.BUNDLING_ATTRIBUTE, - project.objects.named(Bundling::class.java, Bundling.SHADOWED), - ) - } - outgoing.artifact(shadowJar) - } - - val component = softwareComponentFactory.adhoc("shadow") - component.addVariantsFromConfiguration(shadowApiElements) { - if (isPublishable(configurationVariant)) { - mapToMavenScope("compile") - } else { - skip() - } - } - // component.addVariantsFromConfiguration(configurations.getByName("runtimeElements")) { - component.addVariantsFromConfiguration( - project.configurations.getByName("shadowRuntimeElements") - ) { - if (isPublishable(configurationVariant)) { - mapToMavenScope("runtime") - } else { - skip() - } - } - // Sonatype requires the javadoc and sources jar to be present, but the - // Shadow extension does not publish those. - component.addVariantsFromConfiguration(project.configurations.getByName("javadocElements")) {} - component.addVariantsFromConfiguration(project.configurations.getByName("sourcesElements")) {} - mavenPublication.from(component) - - // This a replacement to add dependencies to the pom, if necessary. Equivalent to - // 'shadowExtension.component(mavenPublication)', which we cannot use. - - mavenPublication.pom { - withXml { - val node = asNode() - val depNode = node.get("dependencies") - val dependenciesNode = - if ((depNode as NodeList).isNotEmpty()) depNode[0] as Node - else node.appendNode("dependencies") - project.configurations.getByName("shadow").allDependencies.forEach { - if (it is ProjectDependency) { - val dependencyNode = dependenciesNode.appendNode("dependency") - dependencyNode.appendNode("groupId", it.group) - dependencyNode.appendNode("artifactId", it.name) - dependencyNode.appendNode("version", it.version) - dependencyNode.appendNode("scope", "runtime") - } - } - } - } -} diff --git a/conformance/build.gradle.kts b/conformance/build.gradle.kts index 6aea2891..0b3978cd 100644 --- a/conformance/build.gradle.kts +++ b/conformance/build.gradle.kts @@ -16,20 +16,51 @@ import com.google.protobuf.gradle.ProtobufExtension import com.google.protobuf.gradle.ProtobufPlugin +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.compile.JavaCompile plugins { `java-library` id("com.gradleup.shadow") - id("org.caffinitas.gradle.testsummary") - id("org.caffinitas.gradle.testrerun") - `cel-conventions` + id("cel-conventions") } apply() +val syncedMainProtoDir = layout.buildDirectory.dir("pb-src/proto") +val mainProtoResourcesDir = layout.buildDirectory.dir("generated/proto-resources/main") +val syncMainProtoSources = + tasks.register("syncMainProtoSources") { + into(syncedMainProtoDir) + from(layout.settingsDirectory.dir("submodules/googleapis/google/rpc")) { into("google/rpc") } + from(layout.settingsDirectory.dir("submodules/googleapis/google/api/expr/conformance")) { + into("google/api/expr/conformance") + } + } + +val emptyTestProtoDir = layout.buildDirectory.dir("pb-src/test/proto") + sourceSets.main { - java.srcDir(layout.buildDirectory.dir("generated/source/proto/main/java")) - java.srcDir(layout.buildDirectory.dir("generated/source/proto/main/grpc")) + java.setSrcDirs( + listOf( + layout.projectDirectory.dir("src/main/java"), + layout.buildDirectory.dir("generated/sources/proto/main/java"), + layout.buildDirectory.dir("generated/sources/proto/main/grpc"), + ) + ) + resources.setSrcDirs(listOf(mainProtoResourcesDir)) + extensions.configure("proto") { + setSrcDirs(listOf(syncedMainProtoDir)) + } +} + +sourceSets.test { + java.setSrcDirs(listOf(layout.projectDirectory.dir("src/test/java"))) + resources.setSrcDirs(emptyList()) + extensions.configure("proto") { + setSrcDirs(listOf(emptyTestProtoDir)) + } } configurations.all { exclude(group = "org.projectnessie.cel", module = "cel-generated-pb") } @@ -69,5 +100,20 @@ configure { generateProtoTasks { all().configureEach { this.plugins.create("grpc") {} } } } +tasks.named("generateProto") { dependsOn(syncMainProtoSources) } + +tasks.named("compileJava") { dependsOn(tasks.named("generateProto")) } + +tasks.named("processProtoResources") { dependsOn(syncMainProtoSources) } + +tasks.named("processResources") { dependsOn(tasks.named("processProtoResources")) } + +tasks.named("generateTestProto") { enabled = false } + +tasks.named("processTestProtoResources") { enabled = false } + // The protobuf-plugin should ideally do this -tasks.named("sourcesJar") { dependsOn(tasks.named("generateProto")) } +tasks.named("sourcesJar") { + dependsOn(tasks.named("generateProto")) + dependsOn(tasks.named("processProtoResources")) +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 021a83ad..3fc666f2 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -20,9 +20,7 @@ plugins { `maven-publish` id("com.diffplug.spotless") alias(libs.plugins.jmh) - alias(libs.plugins.testsummary) - alias(libs.plugins.testrerun) - `cel-conventions` + id("cel-conventions") `java-test-fixtures` } diff --git a/generated-antlr/build.gradle.kts b/generated-antlr/build.gradle.kts index ce86a457..ceafa33e 100644 --- a/generated-antlr/build.gradle.kts +++ b/generated-antlr/build.gradle.kts @@ -22,7 +22,7 @@ plugins { `maven-publish` signing id("com.gradleup.shadow") - `cel-conventions` + id("cel-conventions") } dependencies { diff --git a/generated-pb/build.gradle.kts b/generated-pb/build.gradle.kts index 1cf5e341..bd743f3b 100644 --- a/generated-pb/build.gradle.kts +++ b/generated-pb/build.gradle.kts @@ -16,25 +16,71 @@ import com.google.protobuf.gradle.ProtobufExtension import com.google.protobuf.gradle.ProtobufPlugin +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.compile.JavaCompile plugins { `java-library` `maven-publish` signing - `cel-conventions` + id("cel-conventions") `java-test-fixtures` } apply() +val syncedMainProtoDir = layout.buildDirectory.dir("pb-src/proto") +val mainProtoResourcesDir = layout.buildDirectory.dir("generated/proto-resources/main") +val syncMainProtoSources = + tasks.register("syncMainProtoSources") { + into(syncedMainProtoDir) + from(layout.settingsDirectory.dir("submodules/googleapis/google/rpc")) { into("google/rpc") } + from(layout.settingsDirectory.dir("submodules/googleapis/google/api/expr/v1alpha1")) { + into("google/api/expr/v1alpha1") + } + } + +val emptyTestProtoDir = layout.buildDirectory.dir("pb-src/test/proto") +val syncedTestFixturesProtoDir = layout.buildDirectory.dir("pb-src/testFixtures/proto") +val testFixturesProtoResourcesDir = + layout.buildDirectory.dir("generated/proto-resources/testFixtures") +val syncTestFixturesProtoSources = + tasks.register("syncTestFixturesProtoSources") { + into(syncedTestFixturesProtoDir) + from(layout.projectDirectory.dir("src/testFixtures/proto")) { include("*.proto") } + from(layout.settingsDirectory.dir("submodules/cel-spec/proto/test/v1/proto2")) { + into("proto/test/v1/proto2") + } + from(layout.settingsDirectory.dir("submodules/cel-spec/proto/test/v1/proto3")) { + into("proto/test/v1/proto3") + } + } + sourceSets.main { - java.srcDir(layout.buildDirectory.dir("generated/source/proto/main/java")) + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/main/java"))) java.destinationDirectory.set(layout.buildDirectory.dir("classes/java/generated")) + resources.setSrcDirs(listOf(mainProtoResourcesDir)) + extensions.configure("proto") { + setSrcDirs(listOf(syncedMainProtoDir)) + } } sourceSets.test { - java.srcDir(layout.buildDirectory.dir("generated/source/proto/test/java")) + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/test/java"))) java.destinationDirectory.set(layout.buildDirectory.dir("classes/java/generatedTest")) + resources.setSrcDirs(emptyList()) + extensions.configure("proto") { + setSrcDirs(listOf(emptyTestProtoDir)) + } +} + +sourceSets.testFixtures { + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/testFixtures/java"))) + resources.setSrcDirs(listOf(testFixturesProtoResourcesDir)) + extensions.configure("proto") { + setSrcDirs(listOf(syncedTestFixturesProtoDir)) + } } dependencies { @@ -56,3 +102,28 @@ configure { artifact = "com.google.protobuf:protoc:${libs.versions.protobuf.get()}" } } + +tasks.named("generateProto") { dependsOn(syncMainProtoSources) } + +tasks.named("compileJava") { dependsOn(tasks.named("generateProto")) } + +tasks.named("processProtoResources") { dependsOn(syncMainProtoSources) } + +tasks.named("processResources") { dependsOn(tasks.named("processProtoResources")) } + +tasks.named("sourcesJar") { + dependsOn(tasks.named("generateProto")) + dependsOn(tasks.named("processProtoResources")) +} + +tasks.named("generateTestFixturesProto") { dependsOn(syncTestFixturesProtoSources) } + +tasks.named("compileTestFixturesJava") { + dependsOn(tasks.named("generateTestFixturesProto")) +} + +tasks.named("processTestFixturesProtoResources") { dependsOn(syncTestFixturesProtoSources) } + +tasks.named("processTestFixturesResources") { + dependsOn(tasks.named("processTestFixturesProtoResources")) +} diff --git a/generated-pb/src/main/proto/google/api/expr/v1alpha1 b/generated-pb/src/main/proto/google/api/expr/v1alpha1 deleted file mode 120000 index 3ca742d0..00000000 --- a/generated-pb/src/main/proto/google/api/expr/v1alpha1 +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../submodules/googleapis/google/api/expr/v1alpha1 \ No newline at end of file diff --git a/generated-pb/src/main/proto/google/rpc b/generated-pb/src/main/proto/google/rpc deleted file mode 120000 index b915345f..00000000 --- a/generated-pb/src/main/proto/google/rpc +++ /dev/null @@ -1 +0,0 @@ -../../../../../submodules/googleapis/google/rpc \ No newline at end of file diff --git a/generated-pb/src/testFixtures/proto/proto/test/v1/proto2 b/generated-pb/src/testFixtures/proto/proto/test/v1/proto2 deleted file mode 120000 index c7628e00..00000000 --- a/generated-pb/src/testFixtures/proto/proto/test/v1/proto2 +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../submodules/cel-spec/proto/test/v1/proto2 \ No newline at end of file diff --git a/generated-pb/src/testFixtures/proto/proto/test/v1/proto3 b/generated-pb/src/testFixtures/proto/proto/test/v1/proto3 deleted file mode 120000 index a6b27aac..00000000 --- a/generated-pb/src/testFixtures/proto/proto/test/v1/proto3 +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../submodules/cel-spec/proto/test/v1/proto3 \ No newline at end of file diff --git a/generated-pb3/build.gradle.kts b/generated-pb3/build.gradle.kts index de2c819e..54ecee51 100644 --- a/generated-pb3/build.gradle.kts +++ b/generated-pb3/build.gradle.kts @@ -16,25 +16,71 @@ import com.google.protobuf.gradle.ProtobufExtension import com.google.protobuf.gradle.ProtobufPlugin +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.compile.JavaCompile plugins { `java-library` `maven-publish` signing - `cel-conventions` + id("cel-conventions") `java-test-fixtures` } apply() +val syncedMainProtoDir = layout.buildDirectory.dir("pb-src/proto") +val mainProtoResourcesDir = layout.buildDirectory.dir("generated/proto-resources/main") +val syncMainProtoSources = + tasks.register("syncMainProtoSources") { + into(syncedMainProtoDir) + from(layout.settingsDirectory.dir("submodules/googleapis/google/rpc")) { into("google/rpc") } + from(layout.settingsDirectory.dir("submodules/googleapis/google/api/expr/v1alpha1")) { + into("google/api/expr/v1alpha1") + } + } + +val emptyTestProtoDir = layout.buildDirectory.dir("pb-src/test/proto") +val syncedTestFixturesProtoDir = layout.buildDirectory.dir("pb-src/testFixtures/proto") +val testFixturesProtoResourcesDir = + layout.buildDirectory.dir("generated/proto-resources/testFixtures") +val syncTestFixturesProtoSources = + tasks.register("syncTestFixturesProtoSources") { + into(syncedTestFixturesProtoDir) + from(layout.settingsDirectory.dir("generated-pb/src/testFixtures/proto")) { include("*.proto") } + from(layout.settingsDirectory.dir("submodules/cel-spec/proto/test/v1/proto2")) { + into("proto/test/v1/proto2") + } + from(layout.settingsDirectory.dir("submodules/cel-spec/proto/test/v1/proto3")) { + into("proto/test/v1/proto3") + } + } + sourceSets.main { - java.srcDir(layout.buildDirectory.dir("generated/source/proto/main/java")) + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/main/java"))) java.destinationDirectory.set(layout.buildDirectory.dir("classes/java/generated")) + resources.setSrcDirs(listOf(mainProtoResourcesDir)) + extensions.configure("proto") { + setSrcDirs(listOf(syncedMainProtoDir)) + } } sourceSets.test { - java.srcDir(layout.buildDirectory.dir("generated/source/proto/test/java")) + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/test/java"))) java.destinationDirectory.set(layout.buildDirectory.dir("classes/java/generatedTest")) + resources.setSrcDirs(emptyList()) + extensions.configure("proto") { + setSrcDirs(listOf(emptyTestProtoDir)) + } +} + +sourceSets.testFixtures { + java.setSrcDirs(listOf(layout.buildDirectory.dir("generated/sources/proto/testFixtures/java"))) + resources.setSrcDirs(listOf(testFixturesProtoResourcesDir)) + extensions.configure("proto") { + setSrcDirs(listOf(syncedTestFixturesProtoDir)) + } } dependencies { @@ -56,3 +102,28 @@ configure { artifact = "com.google.protobuf:protoc:${libs.versions.protobuf3.get()}" } } + +tasks.named("generateProto") { dependsOn(syncMainProtoSources) } + +tasks.named("compileJava") { dependsOn(tasks.named("generateProto")) } + +tasks.named("processProtoResources") { dependsOn(syncMainProtoSources) } + +tasks.named("processResources") { dependsOn(tasks.named("processProtoResources")) } + +tasks.named("sourcesJar") { + dependsOn(tasks.named("generateProto")) + dependsOn(tasks.named("processProtoResources")) +} + +tasks.named("generateTestFixturesProto") { dependsOn(syncTestFixturesProtoSources) } + +tasks.named("compileTestFixturesJava") { + dependsOn(tasks.named("generateTestFixturesProto")) +} + +tasks.named("processTestFixturesProtoResources") { dependsOn(syncTestFixturesProtoSources) } + +tasks.named("processTestFixturesResources") { + dependsOn(tasks.named("processTestFixturesProtoResources")) +} diff --git a/generated-pb3/src b/generated-pb3/src deleted file mode 120000 index 454c0e97..00000000 --- a/generated-pb3/src +++ /dev/null @@ -1 +0,0 @@ -../generated-pb/src \ No newline at end of file diff --git a/gradle/gradlew-include.sh b/gradle/gradlew-include.sh new file mode 100644 index 00000000..8ec9a2f6 --- /dev/null +++ b/gradle/gradlew-include.sh @@ -0,0 +1,64 @@ +# +# Copyright (C) 2024 Dremio +# +# 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. +# + +# Downloads the gradle-wrapper.jar if necessary and verifies its integrity. +# Included from /.gradlew + +# Extract the Gradle version from gradle-wrapper.properties. +GRADLE_DIST_VERSION="$(grep distributionUrl= "$APP_HOME/gradle/wrapper/gradle-wrapper.properties" | sed 's/^.*gradle-\([0-9.]*\)-[a-z]*.zip$/\1/')" +GRADLE_WRAPPER_SHA256="$APP_HOME/gradle/wrapper/gradle-wrapper-${GRADLE_DIST_VERSION}.jar.sha256" +GRADLE_WRAPPER_JAR="$APP_HOME/gradle/wrapper/gradle-wrapper.jar" +if [ -x "$(command -v sha256sum)" ] ; then + SHASUM="sha256sum" +else + if [ -x "$(command -v shasum)" ] ; then + SHASUM="shasum -a 256" + else + echo "Neither sha256sum nor shasum are available, install either." > /dev/stderr + exit 1 + fi +fi +if [ ! -e "${GRADLE_WRAPPER_SHA256}" ]; then + # Delete the wrapper jar, if the checksum file does not exist. + rm -f "${GRADLE_WRAPPER_JAR}" +fi +if [ -e "${GRADLE_WRAPPER_JAR}" ]; then + # Verify the wrapper jar, if it exists, delete wrapper jar and checksum file, if the checksums + # do not match. + JAR_CHECKSUM="$(${SHASUM} "${GRADLE_WRAPPER_JAR}" | cut -d\ -f1)" + EXPECTED="$(cat "${GRADLE_WRAPPER_SHA256}")" + if [ "${JAR_CHECKSUM}" != "${EXPECTED}" ]; then + rm -f "${GRADLE_WRAPPER_JAR}" "${GRADLE_WRAPPER_SHA256}" + fi +fi +if [ ! -e "${GRADLE_WRAPPER_SHA256}" ]; then + curl --location --output "${GRADLE_WRAPPER_SHA256}" https://services.gradle.org/distributions/gradle-${GRADLE_DIST_VERSION}-wrapper.jar.sha256 || exit 1 +fi +if [ ! -e "${GRADLE_WRAPPER_JAR}" ]; then + # The Gradle version extracted from the `distributionUrl` property does not contain ".0" patch + # versions. Need to append a ".0" in that case to download the wrapper jar. + GRADLE_VERSION="$(echo "$GRADLE_DIST_VERSION" | sed 's/^\([0-9]*[.][0-9]*\)$/\1.0/')" + curl --location --output "${GRADLE_WRAPPER_JAR}" https://raw.githubusercontent.com/gradle/gradle/v${GRADLE_VERSION}/gradle/wrapper/gradle-wrapper.jar || exit 1 + JAR_CHECKSUM="$(${SHASUM} "${GRADLE_WRAPPER_JAR}" | cut -d\ -f1)" + EXPECTED="$(cat "${GRADLE_WRAPPER_SHA256}")" + if [ "${JAR_CHECKSUM}" != "${EXPECTED}" ]; then + # If the (just downloaded) checksum and the downloaded wrapper jar do not match, something + # really bad is going on. + echo "Expected sha256 of the downloaded gradle-wrapper.jar does not match the downloaded sha256!" > /dev/stderr + exit 1 + fi +fi + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c7662b3a..b84b0406 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -68,9 +68,6 @@ tomcat-annotations-api = { module = "org.apache.tomcat:annotations-api", version idea-ext = { id = "org.jetbrains.gradle.plugin.idea-ext", version = "1.4.1" } jandex = { id = "com.github.vlsi.jandex", version.ref = "jandexPlugin" } jmh = { id = "me.champeau.jmh", version = "0.7.3" } -nmcp = { id = "com.gradleup.nmcp.aggregation", version = "1.6.1" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } shadow = { id = "com.gradleup.shadow", version.ref = "shadowPlugin" } spotless = { id = "com.diffplug.spotless", version.ref = "spotlessPlugin" } -testrerun = { id = "org.caffinitas.gradle.testrerun", version = "0.1" } -testsummary = { id = "org.caffinitas.gradle.testsummary", version = "0.1.1" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 61285a65..00000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradlew b/gradlew index adff685a..10fe486d 100755 --- a/gradlew +++ b/gradlew @@ -88,6 +88,9 @@ APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit +[ -f "${APP_HOME}/.env" ] && . "${APP_HOME}/.env" +. "${APP_HOME}/gradle/gradlew-include.sh" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/jackson/build.gradle.kts b/jackson/build.gradle.kts index 03289299..69ef1224 100644 --- a/jackson/build.gradle.kts +++ b/jackson/build.gradle.kts @@ -18,9 +18,7 @@ plugins { `java-library` `maven-publish` signing - id("org.caffinitas.gradle.testsummary") - id("org.caffinitas.gradle.testrerun") - `cel-conventions` + id("cel-conventions") } description = "CEL Jackson 2 support" diff --git a/jackson3/build.gradle.kts b/jackson3/build.gradle.kts index d49d0907..3338184e 100644 --- a/jackson3/build.gradle.kts +++ b/jackson3/build.gradle.kts @@ -18,9 +18,7 @@ plugins { `java-library` `maven-publish` signing - id("org.caffinitas.gradle.testsummary") - id("org.caffinitas.gradle.testrerun") - `cel-conventions` + id("cel-conventions") } description = "CEL Jackson 3 support" diff --git a/settings.gradle.kts b/settings.gradle.kts index 8a3a9b6f..0d114d64 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,29 +14,51 @@ * limitations under the License. */ +import com.gradle.develocity.agent.gradle.scan.BuildScanPublishingConfiguration +import java.time.Duration +import org.gradle.api.specs.Spec +import org.gradle.kotlin.dsl.support.serviceOf + if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) { throw GradleException("Build requires Java 17") } -val baseVersion = file("version.txt").readText().trim() +val baseVersion = + providers.fileContents(layout.settingsDirectory.file("version.txt")).asText.map { it.trim() } +val isCI = providers.environmentVariable("CI").isPresent +val isBuildScanRequested = gradle.startParameter.isBuildScan +val configurationCacheRequested = + gradle.serviceOf().configurationCache.requested.getOrElse(false) + +if (isCI && configurationCacheRequested) { + throw GradleException( + "Gradle configuration cache must not be enabled in CI because it can persist build configuration state to disk." + ) +} pluginManagement { + includeBuild("build-logic") { name = "cel-java-build-logic" } + repositories { mavenCentral() // prefer Maven Central, in case Gradle's repo has issues gradlePluginPortal() - if (System.getProperty("withMavenLocal").toBoolean()) { + if (providers.systemProperty("withMavenLocal").map(String::toBoolean).getOrElse(false)) { mavenLocal() } } } -plugins { id("com.gradle.develocity") version ("4.5.0") } +plugins { + id("com.gradle.develocity") version ("4.5.0") + id("com.gradleup.nmcp.settings") version ("1.6.1") +} develocity { - if (System.getenv("CI") != null) { + if (isCI) { buildScan { termsOfUseUrl = "https://gradle.com/terms-of-service" termsOfUseAgree = "yes" + publishing.onlyIf(RequestedBuildScanPublishingSpec(true)) // Add some potentially interesting information from the environment listOf( "GITHUB_ACTION_REPOSITORY", @@ -52,29 +74,55 @@ develocity { "GITHUB_WORKFLOW", ) .forEach { e -> - val v = System.getenv(e) + val v = providers.environmentVariable(e).orNull if (v != null) { value(e, v) } } - val ghUrl = System.getenv("GITHUB_SERVER_URL") + val ghUrl = providers.environmentVariable("GITHUB_SERVER_URL").orNull if (ghUrl != null) { - val ghRepo = System.getenv("GITHUB_REPOSITORY") - val ghRunId = System.getenv("GITHUB_RUN_ID") + val ghRepo = providers.environmentVariable("GITHUB_REPOSITORY").orNull + val ghRunId = providers.environmentVariable("GITHUB_RUN_ID").orNull link("Summary", "$ghUrl/$ghRepo/actions/runs/$ghRunId") link("PRs", "$ghUrl/$ghRepo/pulls") } } } else { - buildScan { publishing { onlyIf { gradle.startParameter.isBuildScan } } } + buildScan { publishing.onlyIf(RequestedBuildScanPublishingSpec(isBuildScanRequested)) } } } +class RequestedBuildScanPublishingSpec(private val enabled: Boolean) : + Spec { + override fun isSatisfiedBy(context: BuildScanPublishingConfiguration.PublishingContext): Boolean = + enabled +} + rootProject.name = "cel-parent" +// Pass environment variables: +// ORG_GRADLE_PROJECT_sonatypeUsername +// ORG_GRADLE_PROJECT_sonatypePassword +// Gradle targets: +// publishAggregationToCentralPortal +// publishAggregationToCentralPortalSnapshots +// (nmcpZipAggregation to just generate the single, aggregated deployment zip) +// Ref: Maven Central Publisher API: +// https://central.sonatype.org/publish/publish-portal-api/#uploading-a-deployment-bundle +nmcpSettings { + centralPortal { + providers.environmentVariable("ORG_GRADLE_PROJECT_sonatypeUsername").orNull?.let(username::set) + providers.environmentVariable("ORG_GRADLE_PROJECT_sonatypePassword").orNull?.let(password::set) + publishingType.set(if (isCI) "AUTOMATIC" else "USER_MANAGED") + publishingTimeout.set(Duration.ofMinutes(120)) + validationTimeout.set(Duration.ofMinutes(120)) + publicationName.set("cel-parent-${baseVersion.get()}") + } +} + gradle.beforeProject { group = "org.projectnessie.cel" - version = baseVersion + version = baseVersion.get() description = when (name) { "cel" -> "Common-Expression-Language - Java implementation" diff --git a/standalone/build.gradle.kts b/standalone/build.gradle.kts index 25e490e1..4def8ece 100644 --- a/standalone/build.gradle.kts +++ b/standalone/build.gradle.kts @@ -21,7 +21,7 @@ plugins { `maven-publish` signing id("com.gradleup.shadow") - `cel-conventions` + id("cel-conventions") } dependencies { diff --git a/tools/build.gradle.kts b/tools/build.gradle.kts index 1bac71e5..fd12d9e9 100644 --- a/tools/build.gradle.kts +++ b/tools/build.gradle.kts @@ -21,9 +21,7 @@ plugins { `java-library` `maven-publish` signing - id("org.caffinitas.gradle.testsummary") - id("org.caffinitas.gradle.testrerun") - `cel-conventions` + id("cel-conventions") } apply() @@ -43,4 +41,5 @@ configure { // Download from repositories artifact = "com.google.protobuf:protoc:${libs.versions.protobuf.get()}" } + generateProtoTasks { ofSourceSet("main").configureEach { enabled = false } } }