Skip to content

Commit

Permalink
Signing and Publication (#74)
Browse files Browse the repository at this point in the history
* Updated artifacts publication (except Gradle plugins)
- added signing
- added proper metadata

* Updated Gradle Plugins artifacts publication

* Fixed some teamcity configs
  • Loading branch information
Mr3zee authored May 27, 2024
1 parent 05f03cd commit 96e4879
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 79 deletions.
4 changes: 2 additions & 2 deletions bom/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ plugins {

dependencies {
constraints {
rootProject.allprojects.filter {
it.name.startsWith("kotlinx-rpc") && it != project
rootProject.subprojects.filter {
it.plugins.hasPlugin("maven-publish") && it != project
}.forEach { project ->
api(project)
}
Expand Down
6 changes: 6 additions & 0 deletions gradle-conventions/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ dependencies {
}

gradlePlugin {
plugins {
named("conventions-publishing") {
version = libs.versions.rpc.core.get()
}
}

plugins {
named("conventions-common") {
version = libs.versions.rpc.core.get()
Expand Down
29 changes: 29 additions & 0 deletions gradle-conventions/gradle-publish-stub/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
alias(libs.plugins.gradle.kotlin.dsl)
}

configurations.configureEach {
resolutionStrategy {
force(libs.kotlin.reflect)
force(libs.kotlin.stdlib)
force(libs.kotlin.stdlib.jdk7)
force(libs.kotlin.stdlib.jdk8)
}
}

dependencies {
implementation(libs.kotlin.gradle.plugin)
}

gradlePlugin {
plugins {
named("conventions-gradle-publish") {
id = "conventions-gradle-publish"
version = libs.versions.rpc.core.get()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
29 changes: 29 additions & 0 deletions gradle-conventions/gradle-publish/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
alias(libs.plugins.gradle.kotlin.dsl)
}

configurations.configureEach {
resolutionStrategy {
force(libs.kotlin.reflect)
force(libs.kotlin.stdlib)
force(libs.kotlin.stdlib.jdk7)
force(libs.kotlin.stdlib.jdk8)
}
}

dependencies {
implementation(libs.kotlin.gradle.plugin)
}

gradlePlugin {
plugins {
named("conventions-gradle-publish") {
id = "conventions-gradle-publish"
version = libs.versions.rpc.core.get()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

apply(plugin = "com.gradle.plugin-publish")

@Suppress("UnstableApiUsage")
the<GradlePluginDevelopmentExtension>().apply {
website.set("https://kotlinlang.org")
vcsUrl.set("https://github.com/Kotlin/kotlinx-rpc")

plugins.all {
tags.set(
setOf(
"kotlin",
"kotlinx",
"rpc",
"web",
"services",
"asynchronous",
"network",
)
)
}
}

val setupPluginUploadFromEnvironment = tasks.register("setupPluginUploadFromEnvironment") {
doLast {
val key = getSensitiveProperty("gradle.publish.key")
val secret = getSensitiveProperty("gradle.publish.secret")

if (key == null || secret == null) {
throw GradleException(
"GRADLE_PUBLISH_KEY and/or GRADLE_PUBLISH_SECRET are not defined environment variables"
)
}

// in case properties are env vars
System.setProperty("gradle.publish.key", key)
System.setProperty("gradle.publish.secret", secret)
}
}

tasks.named("publishPlugins") {
dependsOn(setupPluginUploadFromEnvironment)
}

fun getSensitiveProperty(name: String?): String? {
if (name == null) {
error("Expected not null property 'name' for publication repository config")
}

return project.findProperty(name) as? String
?: System.getenv(name)
?: System.getProperty(name)
}
7 changes: 7 additions & 0 deletions gradle-conventions/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ plugins {
include(":compiler-specific-module")

val kotlinVersion: String by extra
val isLatestKotlinVersion: Boolean by extra

if (kotlinVersion >= "1.8.0") {
include(":kover")
} else {
include(":kover-stub")
}

if (isLatestKotlinVersion) {
include(":gradle-publish")
} else {
include(":gradle-publish-stub")
}
23 changes: 1 addition & 22 deletions gradle-conventions/src/main/kotlin/conventions-common.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,16 @@
*/

import io.gitlab.arturbosch.detekt.Detekt
import util.configureJvmPublication
import util.configureKmpPublication
import util.configureLibraryPublication
import util.libs

plugins {
id("io.gitlab.arturbosch.detekt")
id("conventions-publishing")
}

val kotlinVersion: String by extra
val globalRootDir: String by extra

val publishingExtension = project.extensions.findByType<PublishingExtension>()

// for some reason jvm publication will be registered by module itself, for example for gradle plugins
val skipJvmPublication: Boolean? by extra

if (name.startsWith("kotlinx-rpc")) {
if (publishingExtension != null) {
publishingExtension.configureLibraryPublication()
} else {
plugins.withId("org.jetbrains.kotlin.jvm") {
configureJvmPublication(skipJvmPublication == true)
}

plugins.withId("org.jetbrains.kotlin.multiplatform") {
configureKmpPublication()
}
}
}

val globalDetektDir = "$globalRootDir/detekt"

// https://detekt.dev/docs/gettingstarted/gradle#options-for-detekt-configuration-closure
Expand Down
135 changes: 135 additions & 0 deletions gradle-conventions/src/main/kotlin/conventions-publishing.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import util.by
import util.configureRepository
import util.getSensitiveProperty

val publishingExtension = project.extensions.findByType<PublishingExtension>()

if (name.startsWith("kotlinx-rpc")) { // only public modules
if (publishingExtension == null) {
apply(plugin = "maven-publish")
}

apply(plugin = "signing")

the<PublishingExtension>().configurePublication()
logger.info("Configured ${project.name} for publication as it is a public module")
} else {
logger.info("Skipping ${project.name} publication configuration as it is not a public module")
}

fun PublishingExtension.configurePublication() {
repositories {
configureSonatypeRepository()
configureSpaceRepository()
configureLocalDevRepository()
}

val javadocJar = configureEmptyJavadocArtifact()

// separate function is needed for different gradle versions
// in 7.6 `Configuration` argument is `this`, in 8.* it is a first argument (hence `it`)
val onPublication: (MavenPublication) -> Unit = { publication ->
publication.pom.configureMavenCentralMetadata()
publication.signPublicationIfKeyPresent()
publication.artifact(javadocJar)
logger.info("Project ${project.name} -> Publication configured: ${publication.name}")
}

publications.withType(MavenPublication::class).all(onPublication)

tasks.withType<PublishToMavenRepository>().configureEach {
dependsOn(tasks.withType<Sign>())
}
}

fun MavenPom.configureMavenCentralMetadata() {
name by project.name
description by "kotlinx.rpc, a Kotlin library for adding asynchronous RPC services to your applications."
url by "https://github.com/Kotlin/kotlinx-rpc"

licenses {
license {
name by "The Apache Software License, Version 2.0"
url by "https://www.apache.org/licenses/LICENSE-2.0.txt"
distribution by "repo"
}
}

developers {
developer {
id by "JetBrains"
name by "JetBrains Team"
organization by "JetBrains"
organizationUrl by "https://www.jetbrains.com"
}
}

scm {
url by "https://github.com/Kotlin/kotlinx-rpc"
connection by "scm:git:git://github.com/Kotlin/kotlinx-rpc.git"
developerConnection by "scm:git:[email protected]:Kotlin/kotlinx-rpc.git"
}
}

fun RepositoryHandler.configureSpaceRepository() {
configureRepository(project) {
username = "SPACE_USERNAME"
password = "SPACE_PASSWORD"
name = "space"
url = "https://maven.pkg.jetbrains.space/public/p/krpc/maven"
}
}

fun RepositoryHandler.configureLocalDevRepository() {
// Something that's straightforward to "clean" for development, not mavenLocal
// IMPORTANT: for gradle plugins 'rootProject' is 'gradle-plugin', not 'kotlinx-rpc'
val buildDir = rootProject.layout.buildDirectory.get()
maven("$buildDir/repo") {
name = "buildRepo"
}
}

fun RepositoryHandler.configureSonatypeRepository() {
configureRepository(project) {
username = "libs.sonatype.user"
password = "libs.sonatype.password"
name = "sonatype"
url = sonatypeRepositoryUri
}
}

val sonatypeRepositoryUri: String?
get() {
val repositoryId: String = project.getSensitiveProperty("libs.repository.id")
?.takeIf { it.isNotBlank() }
?: return null

return "https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId"
}

fun configureEmptyJavadocArtifact(): org.gradle.jvm.tasks.Jar {
val javadocJar by project.tasks.creating(Jar::class) {
archiveClassifier.set("javadoc")
// contents are deliberately left empty
// https://central.sonatype.org/publish/requirements/#supply-javadoc-and-sources
}
return javadocJar
}

fun MavenPublication.signPublicationIfKeyPresent() {
val keyId = project.getSensitiveProperty("libs.sign.key.id")
val signingKey = project.getSensitiveProperty("libs.sign.key.private")
val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase")

if (!signingKey.isNullOrBlank()) {
the<SigningExtension>().apply {
useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)

sign(this@signPublicationIfKeyPresent)
}
}
}
Loading

0 comments on commit 96e4879

Please sign in to comment.