Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
package aws.sdk.kotlin.gradle.dsl

import aws.sdk.kotlin.gradle.util.getOrNull
import aws.sdk.kotlin.gradle.util.verifyRootProject
import io.github.gradlenexus.publishplugin.NexusPublishExtension
import org.gradle.api.Project
Expand Down Expand Up @@ -36,6 +37,7 @@ private object EnvironmentVariables {
const val GPG_PASSPHRASE = "JRELEASER_GPG_PASSPHRASE"
const val GPG_PUBLIC_KEY = "JRELEASER_GPG_PUBLIC_KEY"
const val GPG_SECRET_KEY = "JRELEASER_GPG_SECRET_KEY"
const val GENERIC_TOKEN = "JRELEASER_GENERIC_TOKEN"
}

internal val ALLOWED_PUBLICATION_NAMES = setOf(
Expand All @@ -56,22 +58,27 @@ internal val ALLOWED_PUBLICATION_NAMES = setOf(
"dynamodb-mapper-schema-generatorPluginMarkerMaven",
)

internal val KOTLIN_NATIVE_PUBLICATION_NAMES = setOf(
internal val ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES = setOf(
"iosArm64",
"iosSimulatorArm64",
"iosX64",

"linuxArm64",
"linuxX64",
"macosArm64",
"macosX64",
"mingwX64",
)

// TODO Refactor to support project names _or_ publication group names.
// aws-crt-kotlin is not published with a group name, so we need to check project names instead.
private val KOTLIN_NATIVE_PROJECT_NAMES = setOf(
"aws-crt-kotlin",
// Group names which are allowed to publish K/N artifacts
private val ALLOWED_KOTLIN_NATIVE_GROUP_NAMES = setOf(
"aws.sdk.kotlin.crt",
)

// Optional override to the above set.
// Used to support local development where you want to run publishToMavenLocal in smithy-kotlin, aws-sdk-kotlin.
internal const val OVERRIDE_KOTLIN_NATIVE_GROUP_NAME_VALIDATION = "aws.kotlin.native.allowPublication"

/**
* Mark this project as excluded from publishing
*/
Expand Down Expand Up @@ -237,10 +244,7 @@ fun Project.configurePublishing(repoName: String, githubOrganization: String = "
if (!secretKey.isNullOrBlank() && !passphrase.isNullOrBlank()) {
apply(plugin = "signing")
extensions.configure<SigningExtension> {
useInMemoryPgpKeys(
secretKey,
passphrase,
)
useInMemoryPgpKeys(secretKey, passphrase)
sign(publications)
}

Expand Down Expand Up @@ -306,20 +310,19 @@ fun Project.configureNexus(
fun Project.configureJReleaser() {
verifyRootProject { "JReleaser configuration must be applied to the root project only" }

var missingVariables = false
listOf(
val requiredVariables = listOf(
EnvironmentVariables.MAVEN_CENTRAL_USERNAME,
EnvironmentVariables.MAVEN_CENTRAL_TOKEN,
EnvironmentVariables.GPG_PASSPHRASE,
EnvironmentVariables.GPG_PUBLIC_KEY,
EnvironmentVariables.GPG_SECRET_KEY,
).forEach {
if (System.getenv(it).isNullOrBlank()) {
missingVariables = true
logger.info("Skipping JReleaser configuration, missing required environment variable: $it")
}
EnvironmentVariables.GENERIC_TOKEN,
)

if (!requiredVariables.all { System.getenv(it).isNotBlank() }) {
logger.info("Skipping JReleaser configuration, missing one or more required environment variables: ${requiredVariables.joinToString()}")
Copy link
Contributor

@0marperez 0marperez Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a preference, I liked how we were able to see exactly which env vars were missing before

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may have been useful for debugging while adding JReleaser, but it's very noisy for everyday builds. We would have five lines printed saying there are missing environment variables for every build

return
}
if (missingVariables) return

// Get SDK version from gradle.properties
val sdkVersion: String by project
Expand All @@ -330,23 +333,22 @@ fun Project.configureJReleaser() {
version = sdkVersion
}

// FIXME We're currently signing the artifacts twice. Once using the logic in configurePublishing above,
// and the second time during JReleaser's signing stage.
signing {
active = Active.ALWAYS
armored = true
}

// Used for creating a tagged release, uploading files and generating changelogs.
// In the future we can set this up to push release tags to GitHub, but for now it's
// set up to do nothing.
// https://jreleaser.org/guide/latest/reference/release/index.html
// JReleaser requires a releaser to be configured even though we don't use it.
// https://github.com/jreleaser/jreleaser/discussions/1725#discussioncomment-10674529
release {
generic {
enabled = true
skipRelease = true
}
}

// Used to announce a release to configured announcers.
// We don't announce our releases anywhere
// https://jreleaser.org/guide/latest/reference/announce/index.html
announce {
active = Active.NEVER
Expand All @@ -356,14 +358,14 @@ fun Project.configureJReleaser() {
maven {
mavenCentral {
create("maven-central") {
active = Active.ALWAYS
url = "https://central.sonatype.com/api/v1/publisher"
stagingRepository(rootProject.layout.buildDirectory.dir("m2").get().toString())
artifacts {
verifyPom = false // Sonatype already verifies POMs, and JReleaser's validator is not compatible with TOML or klib types.
artifactOverride {
artifactId = "version-catalog"
jar = false
verifyPom = false // jreleaser doesn't understand toml packaging
jar = false // Version catalogs don't produce a JAR
verifyPom = false // JReleaser fails when processing <packaging>toml</packaging> tags: `Unknown packaging: toml`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to set verifyPom to false in the artifactOverride as well as in the artifacts block?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can leave verifyPom enabled for most artifacts. If it causes issues, then we can look at disabling it

}
}
maxRetries = 100
Expand All @@ -375,23 +377,43 @@ fun Project.configureJReleaser() {
}
}

internal fun Project.configureJReleaserKotlinNativeOverrides() {
val nativePublications = tasks.withType<AbstractPublishToMaven>().filter {
it.publication.name in ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES
}
extensions.configure<JReleaserExtension> {
deploy {
maven {
mavenCentral {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this block for? Will it be filled out later?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that was included prematurely, I just pushed a commit to fix configuration for klib artifacts

}
}
}
}
}

internal fun isAvailableForPublication(project: Project, publication: MavenPublication): Boolean {
var shouldPublish = true

// Check SKIP_PUBLISH_PROP
if (project.extra.has(Properties.SKIP_PUBLISHING)) shouldPublish = false

// Only publish publications with the configured group from JReleaser or everything if JReleaser group is not configured
val publishGroupName = System.getenv(EnvironmentVariables.GROUP_ID)
shouldPublish = shouldPublish && (publishGroupName == null || publication.groupId.startsWith(publishGroupName))

// Validate publication name is allowed to be published
shouldPublish = shouldPublish &&
(
ALLOWED_PUBLICATION_NAMES.any { publication.name.equals(it, ignoreCase = true) } ||
// standard publication
(KOTLIN_NATIVE_PUBLICATION_NAMES.any { publication.name.equals(it, ignoreCase = true) } && KOTLIN_NATIVE_PROJECT_NAMES.any { project.name.equals(it, ignoreCase = true) }) // Kotlin/Native publication
)
// Allow overriding K/N publications for local development
val overrideGroupNameValidation = project.extra.getOrNull<String>(OVERRIDE_KOTLIN_NATIVE_GROUP_NAME_VALIDATION) == "true"

// Validate publication name
if (publication.name in ALLOWED_PUBLICATION_NAMES) {
// Standard publication
} else if (publication.name in ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES) {
// Kotlin/Native publication
if (overrideGroupNameValidation && publication.groupId !in ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES) {
println("Overriding K/N publication, project=${project.name}; publication=${publication.name}; group=${publication.groupId}")
} else {
shouldPublish = shouldPublish && publication.groupId in ALLOWED_KOTLIN_NATIVE_GROUP_NAMES
}
} else {
shouldPublish = false
}

return shouldPublish
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package aws.sdk.kotlin.gradle.dsl
import kotlinx.coroutines.test.runTest
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.internal.extensions.core.extra
import org.gradle.testfixtures.ProjectBuilder
import kotlin.test.Test
import kotlin.test.assertFalse
Expand All @@ -32,7 +33,7 @@ class PublishTest {
assertTrue(isAvailableForPublication(project, jvmRuntimePublication))
}

KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
val nativeRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.sdk.kotlin.crt"
version = "1.2.3"
Expand Down Expand Up @@ -62,7 +63,7 @@ class PublishTest {
assertTrue(isAvailableForPublication(project, jvmRuntimePublication))
}

KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
val nativeRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.sdk.kotlin"
version = "1.2.3"
Expand Down Expand Up @@ -92,7 +93,69 @@ class PublishTest {
assertTrue(isAvailableForPublication(project, jvmRuntimePublication))
}

KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
val nativeRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.smithy.kotlin"
version = "1.2.3"
artifactId = "runtime"
}
assertFalse(isAvailableForPublication(project, nativeRuntimePublication))
}
}
}

@Test
fun `users can override smithy-kotlin publication`() = runTest {
val project = ProjectBuilder.builder().withName("aws-smithy-kotlin").build()
project.group = "aws.smithy.kotlin"
project.version = "1.2.3"
project.extra.set(OVERRIDE_KOTLIN_NATIVE_GROUP_NAME_VALIDATION, "true")

project.configurePublishing("smithy-kotlin", "smithy-lang")

val publishing = project.extensions.getByType(PublishingExtension::class.java)
publishing.publications {
ALLOWED_PUBLICATION_NAMES.forEach {
val jvmRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.smithy.kotlin"
version = "1.2.3"
artifactId = "runtime"
}
assertTrue(isAvailableForPublication(project, jvmRuntimePublication))
}

ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
val nativeRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.smithy.kotlin"
version = "1.2.3"
artifactId = "runtime"
}
assertTrue(isAvailableForPublication(project, nativeRuntimePublication))
}
}
}

@Test
fun `override only works when set to true`() = runTest {
val project = ProjectBuilder.builder().withName("aws-smithy-kotlin").build()
project.group = "aws.smithy.kotlin"
project.version = "1.2.3"
project.extra.set(OVERRIDE_KOTLIN_NATIVE_GROUP_NAME_VALIDATION, "this is not true")

project.configurePublishing("smithy-kotlin", "smithy-lang")

val publishing = project.extensions.getByType(PublishingExtension::class.java)
publishing.publications {
ALLOWED_PUBLICATION_NAMES.forEach {
val jvmRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.smithy.kotlin"
version = "1.2.3"
artifactId = "runtime"
}
assertTrue(isAvailableForPublication(project, jvmRuntimePublication))
}

ALLOWED_KOTLIN_NATIVE_PUBLICATION_NAMES.forEach {
val nativeRuntimePublication = create(it, MavenPublication::class.java).apply {
groupId = "aws.smithy.kotlin"
version = "1.2.3"
Expand Down
Loading