Skip to content

Commit

Permalink
Add Maven publication (#190)
Browse files Browse the repository at this point in the history
Adds publishing for Maven artifacts, including signing. Includes most prerequisites to publish Maven artifacts to Maven Central.

The build-code parts have been taken from Nessie, including the necessary special treatment of shadow-jars and support to publish a bom.
`./gradlew publishToMavenLocal` works out of the box.

On top of the Nessie parts, this change can also build a source tarball from using `git archive`, plus some Apache project specific adoptions.

Fully signed invocation example, assuming GPG agent (there are alternative ways to provide the GPG key+passphrase):
```bash
./ gradlew \
   publishToMavenLocal \
   sourceTarball \
   -Prelease \
   -PuseGpgAgent
```

This change also introduces a `version.txt`, which contains `999-SNAPSHOT`. Using `999-SNAPSHOT` on the `main` branch makes it easier later on to enable multiple major/minor version branches and releases from those. `version.txt` on a major/minor version branch would contain something like `2.1-SNAPSHOT`. With `999-SNAPSHOT` on the `main` branch eliminates the need to update `version.txt` on the `main` branch when a new major version branch is created, eliminating the need to commit and push to multiple branches from a mostly automated release workflow infrastructure.
  • Loading branch information
snazy authored Sep 10, 2024
1 parent d3bfa05 commit 99d8eb7
Show file tree
Hide file tree
Showing 21 changed files with 1,158 additions and 65 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ jobs:

- name: Check formatting
run: ./gradlew check


- name: Check Maven publication
run: ./gradlew publishToMavenLocal sourceTarball

- name: Build with Gradle Wrapper
run: ./gradlew test

Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ WORKDIR /app
RUN rm -rf build

# Build the rest catalog
RUN ./gradlew --no-daemon --info -PeclipseLink=$ECLIPSELINK clean shadowJar startScripts
RUN ./gradlew --no-daemon --info -PeclipseLink=$ECLIPSELINK clean prepareDockerDist

FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.20-2.1721752928
WORKDIR /app
COPY --from=build /app/polaris-service/build/libs/polaris-service-all.jar /app/lib/polaris-service-all.jar
COPY --from=build /app/polaris-service/build/docker-dist/bin /app/bin
COPY --from=build /app/polaris-service/build/docker-dist/lib /app/lib
COPY --from=build /app/polaris-server.yml /app
COPY --from=build /app/polaris-service/build/scripts/polaris-service /app/bin/polaris-service

EXPOSE 8181

Expand Down
2 changes: 2 additions & 0 deletions build-logic/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ dependencies {
implementation(baselibs.errorprone)
implementation(baselibs.idea.ext)
implementation(baselibs.license.report)
implementation(baselibs.nexus.publish)
implementation(baselibs.shadow)
implementation(baselibs.spotless)
}
8 changes: 8 additions & 0 deletions build-logic/src/main/kotlin/polaris-java.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import net.ltgt.gradle.errorprone.errorprone
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.named
import publishing.PublishingHelperPlugin

plugins {
id("jacoco")
Expand All @@ -30,6 +31,8 @@ plugins {
id("net.ltgt.errorprone")
}

apply<PublishingHelperPlugin>()

tasks.withType(JavaCompile::class.java).configureEach {
options.compilerArgs.addAll(listOf("-Xlint:unchecked", "-Xlint:deprecation"))
options.errorprone.disableAllWarnings = true
Expand Down Expand Up @@ -84,6 +87,11 @@ spotless {

dependencies { errorprone(versionCatalogs.named("libs").findLibrary("errorprone").get()) }

java {
withJavadocJar()
withSourcesJar()
}

tasks.withType<Javadoc>().configureEach {
val opt = options as CoreJavadocOptions
// don't spam log w/ "warning: no @param/@return"
Expand Down
23 changes: 23 additions & 0 deletions build-logic/src/main/kotlin/polaris-root.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@
import org.jetbrains.gradle.ext.copyright
import org.jetbrains.gradle.ext.encodings
import org.jetbrains.gradle.ext.settings
import publishing.PublishingHelperExtension
import publishing.PublishingHelperPlugin

plugins {
id("com.diffplug.spotless")
id("org.jetbrains.gradle.plugin.idea-ext")
}

apply<PublishingHelperPlugin>()

spotless {
kotlinGradle {
ktfmt().googleStyle()
Expand Down Expand Up @@ -57,3 +61,22 @@ if (System.getProperty("idea.sync.active").toBoolean()) {
}
}
}

extensions.getByType<PublishingHelperExtension>().apply {
asfProjectName = "polaris"

mailingLists.addAll("dev", "issues", "commits")

podlingPpmcAsfIds.addAll(
"anoop",
"ashvin",
"jackye",
"jbonofre",
"russellspitzer",
"snazy",
"takidau",
"vvcephei"
)
podlingMentorsAsfIds.addAll("bdelacretaz", "blue", "holden", "jbonofre", "yao")
podlingCommitterAsfIds.addAll()
}
40 changes: 40 additions & 0 deletions build-logic/src/main/kotlin/polaris-shadow-jar.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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.
*/

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar

plugins { id("com.gradleup.shadow") }

val shadowJar = tasks.named<ShadowJar>("shadowJar")

shadowJar.configure {
outputs.cacheIf { false } // do not cache uber/shaded jars
archiveClassifier = ""
mergeServiceFiles()
}

tasks.named<Jar>("jar").configure {
dependsOn(shadowJar)
archiveClassifier = "raw"
}

tasks.withType<ShadowJar>().configureEach {
exclude("META-INF/jandex.idx")
isZip64 = true
}
85 changes: 85 additions & 0 deletions build-logic/src/main/kotlin/publishing/MemoizedGitInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 publishing

import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.java.archives.Attributes
import org.gradle.kotlin.dsl.extra
import java.io.ByteArrayOutputStream
import java.nio.charset.StandardCharsets

/**
* Container to memoize Git information retrieved via `git` command executions across all Gradle projects.
* Jar release artifacts get some attributes added to the jar manifest, which can be quite useful for released jars.
*/
internal class MemoizedGitInfo {
companion object {
private fun execProc(rootProject: Project, cmd: String, vararg args: Any): String {
val buf = ByteArrayOutputStream()
rootProject.exec {
executable = cmd
args(args.toList())
standardOutput = buf
}
return buf.toString(StandardCharsets.UTF_8).trim()
}

fun gitInfo(rootProject: Project, attribs: Attributes) {
val props = gitInfo(rootProject)
attribs.putAll(props)
}

fun gitInfo(rootProject: Project): Map<String, String> {
return if (rootProject.extra.has("gitReleaseInfo")) {
@Suppress("UNCHECKED_CAST")
rootProject.extra["gitReleaseInfo"] as Map<String, String>
} else {
val isRelease = rootProject.hasProperty("release")
val gitHead = execProc(rootProject, "git", "rev-parse", "HEAD")
val gitDescribe = if (isRelease) {
try {
execProc(rootProject, "git", "describe", "--tags")
} catch (e: Exception) {
throw GradleException("'git describe --tags' failed - no Git tag?", e)
}
} else {
execProc(rootProject, "git", "describe", "--always", "--dirty")
}
val timestamp = execProc(rootProject, "date", "+%Y-%m-%d-%H:%M:%S%:z")
val system = execProc(rootProject, "uname", "-a")
val javaVersion = System.getProperty("java.version")

val info =
mapOf(
"Apache-Polaris-Version" to rootProject.version.toString(),
"Apache-Polaris-Is-Release" to isRelease.toString(),
"Apache-Polaris-Build-Git-Head" to gitHead,
"Apache-Polaris-Build-Git-Describe" to gitDescribe,
"Apache-Polaris-Build-Timestamp" to timestamp,
"Apache-Polaris-Build-System" to system,
"Apache-Polaris-Build-Java-Version" to javaVersion
)
rootProject.extra["gitReleaseInfo"] = info
return info
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 publishing

import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.property
import java.io.File
import javax.inject.Inject

/**
* Gradle plugin extension object for the `PublishingHelperPlugin. Most attributes are likely never changed from the
* default values.
*
* Apache podlings need to specify the PPMC members and committers manually, Apache TLPs don't populate these
* properties.
*/
abstract class PublishingHelperExtension
@Inject
constructor(objectFactory: ObjectFactory, project: Project)
{
// optional customization of the pom.xml <name> element
val mavenName = objectFactory.property<String>().convention(project.provider { project.name })

val licenseUrl = objectFactory.property<String>().convention("https://www.apache.org/licenses/LICENSE-2.0.txt")

// the following are only relevant on the root project
val asfProjectName = objectFactory.property<String>()
val baseName = objectFactory.property<String>().convention(project.provider { "apache-${asfProjectName.get()}-${project.version}" })
val distributionDir = objectFactory.directoryProperty().convention(project.layout.buildDirectory.dir("distributions"))
val sourceTarball = objectFactory.fileProperty().convention(project.provider { distributionDir.get().file("${baseName.get()}.tar.gz") })
val sourceTarballDigest = objectFactory.fileProperty().convention(project.provider { distributionDir.get().file("${baseName.get()}.sha512") })

val mailingLists = objectFactory.listProperty(String::class.java).convention(emptyList())

// override the list of developers (P)PMC members + committers, necessary for podlings
val podlingPpmcAsfIds = objectFactory.setProperty(String::class.java).convention(emptySet())
val podlingMentorsAsfIds = objectFactory.setProperty(String::class.java).convention(emptySet())
val podlingCommitterAsfIds = objectFactory.setProperty(String::class.java).convention(emptySet())

fun distributionFile(ext: String): File =
distributionDir.get().file("${baseName.get()}.$ext").asFile
}
Loading

0 comments on commit 99d8eb7

Please sign in to comment.