diff --git a/README.md b/README.md index 4307a8d..5e93ab5 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,16 @@ to different kinds of repositories. It supports publishing to: > For Maven Central builds, the plugin takes care of releasing the artifacts using Sonatype REST APIs so you don't have to use their web UI. + +It supports automatic configuration for a certain set of projects: + +- [Android Projects](https://opensource.deepmedia.io/deployer/artifacts#android-projects) +- [Kotlin Projects](https://opensource.deepmedia.io/deployer/artifacts#kotlin-regular-projects) +- [Kotlin Multiplatform Projects](https://opensource.deepmedia.io/deployer/artifacts#kotlin-multiplatform-projects) +- [Gradle Plugin Projects](https://opensource.deepmedia.io/deployer/artifacts#gradle-plugin-projects) + +In addition, you may configure deployments manually based on some existing `SoftwareComponent`, `MavenPublication` or simple file artifacts. + ```kotlin // settings.gradle.kts pluginManagement { @@ -27,7 +37,7 @@ pluginManagement { // build.gradle.kts of deployable modules plugins { - id("io.deepmedia.tools.deployer") version "0.14.0" + id("io.deepmedia.tools.deployer") version "0.15.0-alpha1" } ``` diff --git a/deployer/build.gradle.kts b/deployer/build.gradle.kts index b23517a..9f2898f 100644 --- a/deployer/build.gradle.kts +++ b/deployer/build.gradle.kts @@ -3,13 +3,13 @@ plugins { `kotlin-dsl` `java-gradle-plugin` - id("io.deepmedia.tools.deployer") version "0.14.0-alpha1" + id("io.deepmedia.tools.deployer") version "0.14.0" kotlin("plugin.serialization") version "1.9.23" id("org.jetbrains.dokka") version "1.9.20" } dependencies { - compileOnly("com.android.tools.build:gradle:8.0.2") + compileOnly("com.android.tools.build:gradle:8.1.1") compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23") // api("org.jetbrains.dokka:dokka-gradle-plugin:1.8.20") @@ -33,7 +33,7 @@ gradlePlugin { } group = "io.deepmedia.tools.deployer" -version = "0.14.0" // on change, update README +version = "0.15.0-alpha1" // on change, update README val javadocs = tasks.register("dokkaJavadocJar") { dependsOn(tasks.dokkaJavadoc) diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/central/portal/CentralPortalClient.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/central/portal/CentralPortalClient.kt index 1cdab98..20d0833 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/central/portal/CentralPortalClient.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/central/portal/CentralPortalClient.kt @@ -1,6 +1,5 @@ package io.deepmedia.tools.deployer.central.portal -import com.android.tools.r8.internal.Bo import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.engine.cio.* diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidLibraryInference.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidInference.kt similarity index 52% rename from deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidLibraryInference.kt rename to deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidInference.kt index 69f9a7a..c147af3 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidLibraryInference.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/AndroidInference.kt @@ -1,8 +1,12 @@ package io.deepmedia.tools.deployer.inference +import io.deepmedia.tools.deployer.isKotlinProject import io.deepmedia.tools.deployer.model.Component import io.deepmedia.tools.deployer.model.DeploySpec import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType /** * Android targets can be configured for publishing in two different ways: @@ -11,7 +15,7 @@ import org.gradle.api.Project * with a custom name. The name of the single variant, or the custom name of a multi-variant publiation, will be * the name of the software component. * https://android.googlesource.com/platform/tools/base/+/refs/heads/mirror-goog-studio-main/build-system/gradle-api/src/main/java/com/android/build/api/dsl/LibraryPublishing.kt - * TODO: there doesn't seem to be any way of reading variants from AGP + * NOTE: there doesn't seem to be any way of reading variants from AGP * * 2. Via KGP's configuration block. * In this case, the software components can be retrieved using [KotlinTarget.components] as we do in fromKotlinTarget. @@ -20,11 +24,27 @@ import org.gradle.api.Project * - there may be more than one component, if user chose to do so * - [KotlinTarget.components] *must* be called in a afterEvaluate block, after AGP's afterEvaluate. * - * We can't support 1. until a proper AGP API exist. - * We do already support 2. through KotlinInference, so this is useless for now. */ -internal class AndroidLibraryInference : Inference { +internal class AndroidInference(private val componentNames: List) : Inference { + override fun inferComponents(project: Project, spec: DeploySpec, create: (Component.() -> Unit) -> Component) { - TODO("Not yet implemented") + project.plugins.withId("com.android.library") { + val kotlin = if (project.isKotlinProject) project.kotlinExtension else null + componentNames.forEach { componentName -> + create { + fromSoftwareComponent(componentName) + + packaging.set("aar") + + if (kotlin is KotlinMultiplatformExtension) { + val androidTargets = kotlin.targets.matching { it.platformType == KotlinPlatformType.androidJvm } + artifactId.set { + val target = androidTargets.firstOrNull { it.components.any { it.name == componentName } } ?: androidTargets.first() + "$it-${target.name.lowercase()}" + } + } + } + } + } } } \ No newline at end of file diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/KotlinInference.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/KotlinInference.kt index a953e63..422d8c1 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/KotlinInference.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/inference/KotlinInference.kt @@ -2,7 +2,6 @@ package io.deepmedia.tools.deployer.inference import io.deepmedia.tools.deployer.model.Component import io.deepmedia.tools.deployer.model.DeploySpec -import io.deepmedia.tools.deployer.whenEvaluated import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension @@ -16,11 +15,14 @@ internal class KotlinInference : Inference { private val pluginIds = listOf( "org.jetbrains.kotlin.jvm", "org.jetbrains.kotlin.js", - "org.jetbrains.kotlin.android", "org.jetbrains.kotlin.multiplatform" ) private fun inferComponent(target: KotlinTarget, multiplatform: Boolean, create: (Component.() -> Unit) -> Component) { + if (target is KotlinAndroidTarget) { + // Should use AndroidInference! + return + } if (target is KotlinOnlyTarget<*>) { create { fromKotlinTarget(target) @@ -28,23 +30,6 @@ internal class KotlinInference : Inference { artifactId.set { "$it-${target.name.lowercase()}" } } } - } else if (target is KotlinAndroidTarget) { - // Android's KotlinTarget.components must be called in afterEvaluate, - // possibly nested so we jump after AGP's afterEvaluate blocks. - // There may be 0, 1 or more components depending on how KotlinAndroidTarget was configured. - // NOTE: if multiple components are present, they will have the same artifactId. - target.project.whenEvaluated { - target.project.whenEvaluated { - (target as KotlinTarget).components.forEach { component -> - create { - fromSoftwareComponent(component, tag = target) - if (multiplatform) { - artifactId.set { "$it-${target.name.lowercase()}" } - } - } - } - } - } } else { error("Unexpected kotlin target: $target") } diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Content.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Content.kt index 93e3d64..a85f3cb 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Content.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Content.kt @@ -1,5 +1,6 @@ package io.deepmedia.tools.deployer.model +import io.deepmedia.tools.deployer.inference.AndroidInference import io.deepmedia.tools.deployer.inference.GradlePluginInference import io.deepmedia.tools.deployer.inference.Inference import io.deepmedia.tools.deployer.inference.KotlinInference @@ -25,62 +26,38 @@ open class Content @Inject constructor(private val objects: ObjectFactory) : Com return component } - private val inferenceAction = objects.property>() - private val inference = objects.property() + private data class InferenceData(val inference: Inference, val action: Action) + private val inferenceData = objects.listProperty() fun kotlinComponents(action: Action = Action { }) { - inference = KotlinInference() - inferenceAction = action + inferenceData.add(InferenceData(KotlinInference(), action)) } fun gradlePluginComponents(action: Action = Action { }) { - inference = GradlePluginInference() - inferenceAction = action + inferenceData.add(InferenceData(GradlePluginInference(), action)) } - // private val sourcesInjection = objects.property() - // private val docsInjection = objects.property() + fun androidComponents(componentName: String, vararg otherComponentNames: String, action: Action = Action { }) { + inferenceData.add(InferenceData(AndroidInference(listOf(componentName, *otherComponentNames)), action)) + } internal fun fallback(to: Content) { - // inherit.fallback(to.inherit) // doesn't seem right - // sourcesInjection.fallback(to.sourcesInjection) - // docsInjection.fallback(to.docsInjection) to.inheritedComponents.all { inheritedComponents.add(this) } to.inheritedComponents.whenObjectRemoved { inheritedComponents.remove(this) } to.declaredComponents.all { inheritedComponents.add(this) } to.declaredComponents.whenObjectRemoved { inheritedComponents.remove(this) } } - /* fun emptySources() { sourcesInjection.set("empty") } - fun emtpyDocs() { docsInjection.set("empty") } - @Deprecated("autoDocs is deprecated.", level = DeprecationLevel.WARNING) - fun autoDocs() { docsInjection.set("auto") } - @Deprecated("autoSources is deprecated.", level = DeprecationLevel.WARNING) - fun autoSources() { sourcesInjection.set("auto") } */ - internal fun resolve(project: Project, spec: DeploySpec) { inherit.finalizeValue() // fixes 'components' - inference.finalizeValue() - inferenceAction.finalizeValue() - inference.orNull?.inferComponents(project, spec) { configure -> - component { - configure() - inferenceAction.orNull?.execute(this) + inferenceData.finalizeValue() + inferenceData.get().forEach { + it.inference.inferComponents(project, spec) { configure -> + component { + configure() + it.action.execute(this) + } } } - /* val commonSources = sourcesInjection.orNull - val commonDocs = docsInjection.orNull - allComponents.configureEach { - when { - sources.isPresent -> { /* already provided */ } - commonSources == "empty" -> sources(project.makeSourcesJar(spec, this, true)) - commonSources == "auto" -> sources(project.makeSourcesJar(spec, this, false)) - } - when { - docs.isPresent -> { /* already provided */ } - commonDocs == "empty" -> docs(project.makeDocsJar(spec, this, true)) - commonDocs == "auto" -> docs(project.makeDocsJar(spec, this, false)) - } - } */ } } diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Release.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Release.kt index 43894cd..3fee2a3 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Release.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/model/Release.kt @@ -1,7 +1,5 @@ package io.deepmedia.tools.deployer.model -import com.android.build.gradle.BaseExtension -import io.deepmedia.tools.deployer.isAndroidLibraryProject import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -32,16 +30,7 @@ open class Release @Inject constructor(objects: ObjectFactory) { // Tag defaults to v$version. // Description defaults to target name + tag // Packaging can be set to aar for AARs. - resolvedVersion = version.orElse(target.provider { - when { - target.isAndroidLibraryProject -> { - val android = target.extensions.getByName("android") as BaseExtension - android.defaultConfig.versionName + (android.defaultConfig.versionNameSuffix ?: "") - } - else -> target.version.toString() - } - }) - + resolvedVersion = version.orElse(target.provider { target.version.toString() }) resolvedTag = tag.orElse(resolvedVersion.map { "v$it" }) resolvedDescription = description.orElse(tag.map { "${target.name} $it" }) } diff --git a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/tasks/Workarounds.kt b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/tasks/Workarounds.kt index 2dc17bd..419a67a 100644 --- a/deployer/src/main/kotlin/io/deepmedia/tools/deployer/tasks/Workarounds.kt +++ b/deployer/src/main/kotlin/io/deepmedia/tools/deployer/tasks/Workarounds.kt @@ -79,6 +79,8 @@ internal fun Artifacts.Entry.Resolved.wrapped(project: Project, logger: Logger, else -> when (val data = unwrappedArtifact) { is PublishArtifact -> data.classifier to data.extension is AbstractArchiveTask -> data.archiveClassifier.orNull to data.archiveExtension.get() + // NOTE: logic here is very weak, could at least check if last() seems to be a version number and if so, + // we can infer that this file has no classifier (that is, it's the main POM artifact) else -> resolvedFile.nameWithoutExtension.split("-").last() to resolvedFile.extension } } diff --git a/docs/artifacts.mdx b/docs/artifacts.mdx index 8baceef..094d93e 100644 --- a/docs/artifacts.mdx +++ b/docs/artifacts.mdx @@ -9,16 +9,17 @@ Each spec can support multiple components, each one corresponding to a maven pub ## Built-in components -Inside the `content {}`, a few utilities are available so that: -- You give the deployer some hints about the kind of project that you are working on -- The deployer infers a list of components that you will want to export +Inside the `content {}` block, a few utilities are available so that: +- You give us some hints about the kind of project that you are working on +- We infer a list of components that you will want to export ##### Kotlin Multiplatform projects Use `content.kotlinComponents()`. The spec will export: - One component for Kotlin Metadata (the `common` target) -- One component per declared target -- For Android targets, one component per exported variant +- One component per declared kotlin target + +> If your Kotlin project includes Android, you should also use [`androidComponents()`](#android-projects). ```kotlin content { @@ -30,9 +31,9 @@ content { ##### Kotlin regular projects -Again, use `content.kotlinComponents()`. The spec will export: -- if target is Android, one component per configured variant -- in all other cases (JS, JVM, ...), one component only +Again, use `content.kotlinComponents()`. The spec will export a single component based for the desired platform. + +> If your Kotlin project targets Android, you should use [`androidComponents()`](#android-projects) instead. ```kotlin content { @@ -42,6 +43,28 @@ content { } ``` +##### Android projects + +Use `content.androidComponents("softwareComponentName")` for Android projects. This function accepts a vararg +list of software component names, which you must declare in the regular `android {}` configuration. + +Unfortunately, Android Gradle Plugin offers no API to read the variants that you configured for publishing, so +the exact names must be passed to the deployer, too. + +```kotlin +android { + publishing { + singleVariant("release") + multipleVariants("merged") { includeBuildTypeValues("debug", "release") } + } +} +content { + androidComponents("release", "merged") { + // Optional configuration, invoked on each component. + } +} +``` + ##### Gradle Plugin projects For projects providing gradle plugins, it is expected that the `java-gradle-plugin` is applied diff --git a/docs/index.mdx b/docs/index.mdx index 4850205..21d1e09 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -20,3 +20,12 @@ to different kinds of repositories. It supports publishing to: > For Maven Central builds, the plugin takes care of releasing the artifacts using Sonatype REST APIs so you don't have to use their web UI. +It supports automatic configuration for a certain set of projects: + +- [Android Projects](artifacts#android-projects) +- [Kotlin Projects](artifacts#kotlin-regular-projects) +- [Kotlin Multiplatform Projects](artifacts#kotlin-multiplatform-projects) +- [Gradle Plugin Projects](artifacts#gradle-plugin-projects) + +In addition, you may configure deployments manually based on some existing `SoftwareComponent`, `MavenPublication` or simple file artifacts. +