├── .github ├── workflows │ ├── .java-version │ ├── publish-snapshot.yml │ ├── build.yml │ ├── publish-release.yml │ └── publish-docs.yml ├── pull_request_template.md └── renovate.json5 ├── .gitattributes ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── rc.versions.toml ├── beta.versions.toml ├── alpha.versions.toml └── libs.versions.toml ├── plugin ├── src │ ├── integrationTest │ │ ├── fixtures2 │ │ │ ├── test-secring.gpg │ │ │ └── src │ │ │ │ ├── testFixtures │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── vanniktech │ │ │ │ │ │ └── maven │ │ │ │ │ │ └── publish │ │ │ │ │ │ └── test │ │ │ │ │ │ └── TestFixtureClass.java │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── TestFixtureKotlinClass.kt │ │ │ │ ├── commonMain │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── jvmMain │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── linuxX64Main │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── nodeJsMain │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── androidMain │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ ├── AndroidTestClass.kt │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── androidDebug │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ ├── androidRelease │ │ │ │ └── kotlin │ │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── ExpectedTestClass.kt │ │ │ │ └── main │ │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── vanniktech │ │ │ │ │ └── maven │ │ │ │ │ └── publish │ │ │ │ │ └── test │ │ │ │ │ └── KotlinTestClass.kt │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── vanniktech │ │ │ │ └── maven │ │ │ │ └── publish │ │ │ │ └── test │ │ │ │ └── JavaTestClass.java │ │ └── kotlin │ │ │ └── com │ │ │ └── vanniktech │ │ │ └── maven │ │ │ └── publish │ │ │ ├── BasePluginTest.kt │ │ │ ├── ProjectSpec.kt │ │ │ ├── TestOptionsProvider.kt │ │ │ ├── DokkaPluginTest.kt │ │ │ ├── Pom.kt │ │ │ ├── TestOptions.kt │ │ │ ├── AndroidPluginTest.kt │ │ │ ├── SpecialCasePluginTest.kt │ │ │ ├── JavaPluginTest.kt │ │ │ ├── Subjects.kt │ │ │ └── ProjectSpecs.kt │ └── main │ │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── maven │ │ └── publish │ │ ├── DeploymentValidation.kt │ │ ├── central │ │ ├── MavenCentralCoordinates.kt │ │ ├── MavenCentralProject.kt │ │ ├── EndOfBuildAction.kt │ │ ├── DropMavenCentralDeploymentTask.kt │ │ ├── EnableAutomaticMavenCentralPublishingTask.kt │ │ ├── PrepareMavenCentralPublishingTask.kt │ │ └── MavenCentralBuildService.kt │ │ ├── MavenPublishPlugin.kt │ │ ├── workaround │ │ ├── DirectorySignatureType.kt │ │ └── TestFixtures.kt │ │ ├── ProjectExtensions.kt │ │ ├── MavenPublishBasePlugin.kt │ │ ├── tasks │ │ └── JavadocJar.kt │ │ └── Properties.kt ├── lint-baseline.xml ├── gradle.properties ├── build.gradle.kts └── api │ └── plugin.api ├── central-portal ├── gradle.properties ├── build.gradle.kts ├── src │ └── main │ │ └── kotlin │ │ └── com │ │ └── vanniktech │ │ └── maven │ │ └── publish │ │ └── portal │ │ ├── SonatypeCentralPortalOkHttpInterceptor.kt │ │ ├── SonatypeCentralPortalService.kt │ │ ├── DeploymentStatus.kt │ │ └── SonatypeCentralPortal.kt └── api │ └── central-portal.api ├── ktlint.main.kts ├── .gitignore ├── .editorconfig ├── RELEASING.md ├── gradle.properties ├── kotlinw ├── settings.gradle.kts ├── mkdocs.yml ├── docs ├── base.md ├── other.md └── what.md ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /.github/workflows/.java-version: -------------------------------------------------------------------------------- 1 | 25 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | *.bat text eol=crlf 4 | *.jar binary 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/gradle-maven-publish-plugin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/test-secring.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vanniktech/gradle-maven-publish-plugin/HEAD/plugin/src/integrationTest/fixtures2/test-secring.gpg -------------------------------------------------------------------------------- /central-portal/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=central-portal 2 | POM_NAME=Sonatype related APIs and tasks 3 | POM_DESCRIPTION=APIs and tasks to help working with the Sonatype Central Portal 4 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - [ ] [CHANGELOG](https://github.com/vanniktech/gradle-maven-publish-plugin/blob/main/CHANGELOG.md)'s "Unreleased" section has been updated, if applicable. 4 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/DeploymentValidation.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | public enum class DeploymentValidation { 4 | NONE, 5 | VALIDATE, 6 | PUBLISH, 7 | } 8 | -------------------------------------------------------------------------------- /plugin/lint-baseline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ktlint.main.kts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env kotlin 2 | 3 | @file:DependsOn("com.freeletics.gradle:scripts-formatting-jvm:0.36.1") 4 | 5 | import com.freeletics.gradle.scripts.KtLintCli 6 | import com.github.ajalt.clikt.core.main 7 | 8 | KtLintCli().main(args) 9 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/testFixtures/java/com/vanniktech/maven/publish/test/TestFixtureClass.java: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test; 2 | 3 | /** 4 | * Just a test fixture class. 5 | */ 6 | public class TestFixtureClass { 7 | } 8 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/testFixtures/kotlin/com/vanniktech/maven/publish/test/TestFixtureKotlinClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just a test fixture class in Kotlin. 5 | */ 6 | public class TestFixtureKotlinClass 7 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/MavenCentralCoordinates.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | internal data class MavenCentralCoordinates( 4 | val group: String, 5 | val artifactId: String, 6 | val version: String, 7 | ) 8 | -------------------------------------------------------------------------------- /plugin/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=gradle-maven-publish-plugin 2 | POM_NAME=Gradle Maven Publish Plugin 3 | POM_DESCRIPTION=Gradle plugin that configures publish tasks to automatically upload all of your Java, Kotlin, Gradle, or Android libraries to any Maven instance. 4 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/MavenCentralProject.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | import java.io.File 4 | 5 | internal data class MavenCentralProject( 6 | val coordinates: MavenCentralCoordinates, 7 | val localRepository: File, 8 | ) 9 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/commonMain/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just an expected test class with Javadoc. 5 | */ 6 | expect class ExpectedTestClass { 7 | /** 8 | * An expected test funtion that does something. 9 | */ 10 | fun test() 11 | } 12 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/jvmMain/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just a jvm actual test class with Javadoc. 5 | */ 6 | actual class ExpectedTestClass { 7 | /** 8 | * An actual test funtion that does something. 9 | */ 10 | actual fun test() { } 11 | } 12 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/linuxX64Main/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just a linux actual test class with Javadoc. 5 | */ 6 | actual class ExpectedTestClass { 7 | /** 8 | * An actual test funtion that does something. 9 | */ 10 | actual fun test() { } 11 | } 12 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/nodeJsMain/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just a nodejs actual test class with Javadoc. 5 | */ 6 | actual class ExpectedTestClass { 7 | /** 8 | * An actual test funtion that does something. 9 | */ 10 | actual fun test() { } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | out/ 4 | .kotlin/ 5 | 6 | # Ignore Gradle GUI config 7 | gradle-app.setting 8 | 9 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 10 | !gradle-wrapper.jar 11 | 12 | local.properties 13 | 14 | .idea/ 15 | *.iml 16 | 17 | *.DS_Store 18 | 19 | ### MkDocs ### 20 | docs/api 21 | docs/changelog.md 22 | docs/index.md 23 | site 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionSha256Sum=72f44c9f8ebcb1af43838f45ee5c4aa9c5444898b3468ab3f4af7b6076c5bc3f 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 5 | networkTimeout=10000 6 | validateDistributionUrl=true 7 | zipStoreBase=GRADLE_USER_HOME 8 | zipStorePath=wrapper/dists 9 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/androidMain/kotlin/com/vanniktech/maven/publish/test/AndroidTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * Just an Android test class with Javadoc. 7 | */ 8 | class AndroidTestClass { 9 | /** 10 | * An actual test funtion that does something. 11 | */ 12 | fun test() { 13 | Log.i("test", "test main") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gradle/rc.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | gradle = "9.3.0-rc-1" 3 | 4 | kotlin = "2.3.0-RC3" 5 | 6 | android-gradle = "8.13.2" 7 | 8 | gradle-plugin-publish = "2.0.0" 9 | 10 | [plugins] 11 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 12 | android-library = { id = "com.android.library", version.ref = "android-gradle" } 13 | gradle-plugin-publish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" } 14 | -------------------------------------------------------------------------------- /gradle/beta.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | gradle = "9.3.0-rc-1" 3 | 4 | kotlin = "2.3.0-RC3" 5 | 6 | android-gradle = "9.0.0-rc01" 7 | 8 | gradle-plugin-publish = "2.0.0" 9 | 10 | [plugins] 11 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 12 | android-library = { id = "com.android.library", version.ref = "android-gradle" } 13 | gradle-plugin-publish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" } 14 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/androidDebug/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * Just a Android actual test class with Javadoc. 7 | */ 8 | actual class ExpectedTestClass { 9 | /** 10 | * An actual test funtion that does something. 11 | */ 12 | actual fun test() { 13 | Log.i("test", "test debug") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/androidMain/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * Just a Android actual test class with Javadoc. 7 | */ 8 | actual class ExpectedTestClass { 9 | /** 10 | * An actual test funtion that does something. 11 | */ 12 | actual fun test() { 13 | Log.i("test", "test debug") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/androidRelease/kotlin/com/vanniktech/maven/publish/test/ExpectedTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | import android.util.Log 4 | 5 | /** 6 | * Just a Android actual test class with Javadoc. 7 | */ 8 | actual class ExpectedTestClass { 9 | /** 10 | * An actual test funtion that does something. 11 | */ 12 | actual fun test() { 13 | Log.i("test", "test release") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /gradle/alpha.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | gradle = "9.4.0-milestone-3" 3 | 4 | kotlin = "2.3.0-RC3" 5 | 6 | android-gradle = "9.0.0-rc01" 7 | 8 | gradle-plugin-publish = "2.0.0" 9 | 10 | [plugins] 11 | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 12 | android-library = { id = "com.android.library", version.ref = "android-gradle" } 13 | gradle-plugin-publish = { id = "com.gradle.plugin-publish", version.ref = "gradle-plugin-publish" } 14 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/main/kotlin/com/vanniktech/maven/publish/test/KotlinTestClass.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test 2 | 3 | /** 4 | * Just a test class with Javadoc. 5 | */ 6 | object KotlinTestClass { 7 | /** 8 | * Main method which does something. 9 | * 10 | * @param args Command-line arguments passed to the program. 11 | */ 12 | fun main(args: Array?) { 13 | println("Hello World!") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/fixtures2/src/main/java/com/vanniktech/maven/publish/test/JavaTestClass.java: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.test; 2 | 3 | /** 4 | * Just a test class with Javadoc. 5 | */ 6 | public class JavaTestClass { 7 | /** 8 | * Main method which does something. 9 | * 10 | * @param args Command-line arguments passed to the program. 11 | */ 12 | public static void main(String[] args) { 13 | System.out.println("Hello World!"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/EndOfBuildAction.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | internal sealed interface EndOfBuildAction { 4 | val runAfterFailure: Boolean 5 | 6 | data object Upload : EndOfBuildAction { 7 | override val runAfterFailure: Boolean = false 8 | } 9 | 10 | data object Publish : EndOfBuildAction { 11 | override val runAfterFailure: Boolean = false 12 | } 13 | 14 | data class Validate( 15 | val waitForPublishing: Boolean, 16 | ) : EndOfBuildAction { 17 | override val runAfterFailure: Boolean = false 18 | } 19 | 20 | data class Drop( 21 | override val runAfterFailure: Boolean, 22 | ) : EndOfBuildAction 23 | } 24 | -------------------------------------------------------------------------------- /central-portal/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("shared") 3 | } 4 | 5 | dependencies { 6 | implementation(libs.okhttp.client) 7 | implementation(libs.retrofit) 8 | implementation(libs.retrofit.converter.scalars) 9 | implementation(libs.retrofit.converter.moshi) 10 | implementation(libs.slf4j.api) 11 | implementation(libs.moshi) 12 | implementation(libs.moshi.kotlin) 13 | 14 | testImplementation(libs.junit.jupiter) 15 | testImplementation(libs.junit.engine) 16 | testImplementation(libs.junit.launcher) 17 | testImplementation(libs.truth) 18 | testImplementation(libs.slf4j.api) 19 | testImplementation(libs.okhttp.mockwebserver) 20 | 21 | testRuntimeOnly(libs.slf4j.simple) 22 | } 23 | 24 | tasks.withType().configureEach { 25 | useJUnitPlatform() 26 | } 27 | -------------------------------------------------------------------------------- /central-portal/src/main/kotlin/com/vanniktech/maven/publish/portal/SonatypeCentralPortalOkHttpInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.portal 2 | 3 | import okhttp3.Interceptor 4 | import okhttp3.Response 5 | 6 | internal class SonatypeCentralPortalOkHttpInterceptor( 7 | private val usertoken: String, 8 | private val userAgentName: String, 9 | private val userAgentVersion: String, 10 | ) : Interceptor { 11 | override fun intercept(chain: Interceptor.Chain): Response { 12 | val requestBuilder = chain.request().newBuilder() 13 | 14 | requestBuilder.addHeader("Accept", "application/json") // request json by default, XML is returned else 15 | requestBuilder.addHeader("Authorization", "Bearer $usertoken") 16 | requestBuilder.addHeader("User-Agent", "$userAgentName/$userAgentVersion") 17 | 18 | return chain.proceed(requestBuilder.build()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/BasePluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.testing.junit.testparameterinjector.junit5.TestParameter 4 | import com.vanniktech.maven.publish.TestOptions.Signing.IN_MEMORY_KEY 5 | import java.nio.file.Path 6 | import org.junit.jupiter.api.BeforeEach 7 | import org.junit.jupiter.api.io.TempDir 8 | 9 | abstract class BasePluginTest { 10 | @TempDir 11 | lateinit var testProjectDir: Path 12 | private set 13 | 14 | @TestParameter(valuesProvider = TestOptionsConfigProvider::class) 15 | lateinit var config: TestOptions.Config 16 | private set 17 | 18 | @TestParameter(valuesProvider = GradleVersionProvider::class) 19 | lateinit var gradleVersion: GradleVersion 20 | private set 21 | 22 | open val testOptions get() = TestOptions(config, IN_MEMORY_KEY, gradleVersion) 23 | 24 | @BeforeEach 25 | fun setup() { 26 | gradleVersion.assumeSupportedJdkVersion() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 2 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{kt,kts}] 10 | ij_kotlin_imports_layout = * 11 | 12 | ij_kotlin_allow_trailing_comma = true 13 | ij_kotlin_allow_trailing_comma_on_call_site = true 14 | 15 | # Disable wildcard imports entirely. 16 | ij_kotlin_name_count_to_use_star_import = 2147483647 17 | ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 18 | ij_kotlin_packages_to_use_import_on_demand = unset 19 | 20 | ktlint_code_style = ktlint_official 21 | # makes constructor annotations indent the whole class 22 | ktlint_standard_annotation = disabled 23 | # too aggressive in wrapping and indenting the expression in an assignment 24 | ktlint_standard_multiline-expression-wrapping = disabled 25 | # make ktlint less eager to wrap function signatures and expression bodies 26 | ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = unset 27 | ktlint_function_signature_body_expression_wrapping = default 28 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing 2 | ======== 3 | 4 | The release process consists of first publishing a release candidate and then using 5 | that release candidate to first publish a new snapshot and then the final release. This 6 | is a final test that publishing generally works with the new release. 7 | 8 | 1. Make sure `CHANGELOG.md` is up-to-date on `main` for the impeding release. 9 | 2. `git tag -a X.Y.X-rc1 -m "Version X.Y.Z Release Candidate 1"` (where X.Y.Z is the new version) 10 | 3. `git push --tags` 11 | 4. Update `libs.versions.toml` to use `X.Y.X-rc1` 12 | 5. `git commit -am "Update to X.Y.X-rc1"` 13 | 6. `dependency-watch await com.vanniktech:gradle-maven-publish-plugin:X.Y.X-rc1 && git push` 14 | 7. Wait for snapshot to be published successfully 15 | 8. `git tag -a X.Y.X -m "Version X.Y.Z"` 16 | 9. `git push --tags` 17 | 18 | If the snapshot publishing fails in step 7 or the final release publishing after step 9 fails: 19 | 1. Fix the issue that caused the failure 20 | 2. Downgrade back to stable publish plugin 21 | 3. Start the whole process again and bump the `rc` version by 1 22 | -------------------------------------------------------------------------------- /central-portal/api/central-portal.api: -------------------------------------------------------------------------------- 1 | public final class com/vanniktech/maven/publish/portal/SonatypeCentralPortal { 2 | public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJJLorg/slf4j/Logger;)V 3 | public final fun deleteDeployment (Ljava/lang/String;)V 4 | public final fun publishDeployment (Ljava/lang/String;)V 5 | public final fun upload (Ljava/lang/String;Lcom/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType;Ljava/io/File;)Ljava/lang/String; 6 | public final fun validateDeployment (Ljava/lang/String;Z)V 7 | } 8 | 9 | public final class com/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType : java/lang/Enum { 10 | public static final field AUTOMATIC Lcom/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType; 11 | public static final field USER_MANAGED Lcom/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType; 12 | public static fun getEntries ()Lkotlin/enums/EnumEntries; 13 | public static fun valueOf (Ljava/lang/String;)Lcom/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType; 14 | public static fun values ()[Lcom/vanniktech/maven/publish/portal/SonatypeCentralPortal$PublishingType; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Omit automatic compile dependency on kotlin-stdlib. 2 | # https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library 3 | kotlin.stdlib.default.dependency=false 4 | 5 | org.gradle.caching=true 6 | org.gradle.configuration-cache=true 7 | org.gradle.configuration-cache.parallel=true 8 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx2g 9 | org.gradle.parallel=true 10 | 11 | mavenCentralAutomaticPublishing=true 12 | mavenCentralPublishing=true 13 | signAllPublications=true 14 | 15 | GROUP=com.vanniktech 16 | 17 | POM_INCEPTION_YEAR=2018 18 | 19 | POM_URL=https://github.com/vanniktech/gradle-maven-publish-plugin/ 20 | POM_SCM_URL=https://github.com/vanniktech/gradle-maven-publish-plugin/ 21 | POM_SCM_CONNECTION=scm:git:git://github.com/vanniktech/gradle-maven-publish-plugin.git 22 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/vanniktech/gradle-maven-publish-plugin.git 23 | 24 | POM_LICENCE_NAME=Apache-2.0 25 | POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt 26 | POM_LICENCE_DIST=repo 27 | 28 | POM_DEVELOPER_ID=vanniktech 29 | POM_DEVELOPER_NAME=Niklas Baudy 30 | POM_DEVELOPER_URL=https://github.com/vanniktech/ 31 | 32 | # only exists for integration tests, not used for actual publishing: 33 | VERSION_NAME=0.1.0-SNAPSHOT 34 | -------------------------------------------------------------------------------- /central-portal/src/main/kotlin/com/vanniktech/maven/publish/portal/SonatypeCentralPortalService.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.portal 2 | 3 | import okhttp3.MultipartBody 4 | import retrofit2.Call 5 | import retrofit2.http.DELETE 6 | import retrofit2.http.Multipart 7 | import retrofit2.http.POST 8 | import retrofit2.http.Part 9 | import retrofit2.http.Path 10 | import retrofit2.http.Query 11 | 12 | /** 13 | * Sonatype Central Portal Publishing based on https://central.sonatype.org/publish/publish-portal-api/ 14 | */ 15 | internal interface SonatypeCentralPortalService { 16 | @DELETE("api/v1/publisher/deployment/{deploymentId}") 17 | fun deleteDeployment( 18 | @Path("deploymentId") deploymentId: String, 19 | ): Call 20 | 21 | @POST("api/v1/publisher/deployment/{deploymentId}") 22 | fun publishDeployment( 23 | @Path("deploymentId") deploymentId: String, 24 | ): Call 25 | 26 | @Multipart 27 | @POST("api/v1/publisher/upload") 28 | fun uploadBundle( 29 | @Query("name") name: String, 30 | @Query("publishingType") publishingType: SonatypeCentralPortal.PublishingType, 31 | @Part input: MultipartBody.Part, 32 | ): Call 33 | 34 | @POST("api/v1/publisher/status") 35 | fun checkDeploymentStatus( 36 | @Query("id") deploymentId: String, 37 | ): Call 38 | } 39 | -------------------------------------------------------------------------------- /kotlinw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | ROOT_DIR="$(dirname "$0")" 5 | 6 | KOTLIN_VERSION="$(grep -m 1 '^kotlin \?=' "$ROOT_DIR/gradle/libs.versions.toml" | cut -d'"' -f2)" 7 | 8 | INSTALLATION_DIR="${HOME}/.kotlinw/${KOTLIN_VERSION}" 9 | BINARY_DIR="${INSTALLATION_DIR}/kotlinc/bin" 10 | 11 | if [ ! -f "${BINARY_DIR}/kotlin" ]; then 12 | echo "Downloading Kotlin ${KOTLIN_VERSION}" 13 | mkdir -p "${INSTALLATION_DIR}" 14 | temp_file=$(mktemp /tmp/kotlin.zip.XXXXXX) 15 | curl -sLo "${temp_file}" "https://github.com/JetBrains/kotlin/releases/download/v${KOTLIN_VERSION}/kotlin-compiler-${KOTLIN_VERSION}.zip" 16 | unzip -q "${temp_file}" -d "${INSTALLATION_DIR}" 17 | rm -f "${temp_file}" 18 | fi 19 | 20 | # this works around an issue where the Kotlin compiler used by ktlint accesses code that JDK 12+ don't allow access to 21 | export JAVA_OPTS="--add-opens java.base/java.lang=ALL-UNNAMED" 22 | 23 | SCRIPT_FILE="$1" 24 | # will remove the first element of the $@ arguments array since we read it already above 25 | shift 26 | # uses kotlinc instead of kotlin because the latter doesn't allow specifying a jvm target and defaults to Java 8 27 | # the -- between SCRIPT_FILE and the other arguments is there so that the arguments are treated as arguments to the 28 | # script and not to kotlinc 29 | "${BINARY_DIR}/kotlinc" "-script" "$SCRIPT_FILE" "--" "${@}" 30 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/ProjectSpec.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import java.nio.file.Path 4 | import org.gradle.testkit.runner.BuildResult 5 | 6 | data class ProjectSpec( 7 | val plugins: List, 8 | val group: String?, 9 | val artifactId: String?, 10 | val version: String?, 11 | val properties: Map, 12 | val sourceFiles: List, 13 | val basePluginConfig: String, 14 | val defaultProjectName: String = "default-root-project-name", 15 | val buildFileExtra: String = "", 16 | val propertiesExtra: String = "", 17 | ) 18 | 19 | data class PluginSpec( 20 | val id: String, 21 | val version: String? = null, 22 | ) 23 | 24 | data class ProjectResult( 25 | val result: BuildResult?, 26 | val task: String, 27 | val projectSpec: ProjectSpec, 28 | val project: Path, 29 | val repo: Path, 30 | ) { 31 | fun withArtifactIdSuffix(suffix: String): ProjectResult { 32 | val updatedSpec = projectSpec.copy(artifactId = "${projectSpec.artifactId}-$suffix") 33 | return copy(projectSpec = updatedSpec) 34 | } 35 | } 36 | 37 | data class SourceFile( 38 | val sourceSet: String, 39 | val sourceSetFolder: String, 40 | val file: String, 41 | ) { 42 | fun resolveIn(root: Path): Path = root.resolve("src/$sourceSet/$sourceSetFolder/$file") 43 | } 44 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/MavenPublishPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import org.gradle.api.Plugin 4 | import org.gradle.api.Project 5 | 6 | public abstract class MavenPublishPlugin : Plugin { 7 | override fun apply(project: Project) { 8 | project.plugins.apply(MavenPublishBasePlugin::class.java) 9 | val baseExtension = project.baseExtension 10 | 11 | if (project.mavenCentralPublishing()) { 12 | baseExtension.publishToMavenCentral( 13 | project.automaticRelease(), 14 | project.validateDeployment(), 15 | ) 16 | } 17 | 18 | if (project.signAllPublications()) { 19 | baseExtension.signAllPublications() 20 | } 21 | 22 | baseExtension.pomFromGradleProperties() 23 | 24 | // afterEvaluate is too late for AGP which doesn't allow configuration after finalizeDsl 25 | project.plugins.withId("com.android.library") { 26 | project.androidComponents.finalizeDsl { 27 | baseExtension.configureBasedOnAppliedPlugins() 28 | } 29 | } 30 | project.plugins.withId("com.android.fused-library") { 31 | baseExtension.configureBasedOnAppliedPlugins() 32 | } 33 | 34 | project.afterEvaluate { 35 | // will no-op if it was already called 36 | baseExtension.configureBasedOnAppliedPlugins() 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: Publish Snapshot 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '**' 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | publish: 16 | 17 | runs-on: ubuntu-latest 18 | if: github.repository == 'vanniktech/gradle-maven-publish-plugin' 19 | 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v6 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Install JDK 27 | uses: actions/setup-java@v5 28 | with: 29 | distribution: zulu 30 | java-version-file: .github/workflows/.java-version 31 | 32 | - name: Set version 33 | run: | 34 | echo "ORG_GRADLE_PROJECT_VERSION_NAME=$(git describe --tags --abbrev=0 | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.)-SNAPSHOT" >> $GITHUB_ENV 35 | 36 | - uses: gradle/actions/setup-gradle@v5 37 | 38 | - name: Publish 39 | run: ./gradlew publishToMavenCentral 40 | env: 41 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 42 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 43 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }} 44 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} 45 | -------------------------------------------------------------------------------- /central-portal/src/main/kotlin/com/vanniktech/maven/publish/portal/DeploymentStatus.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.portal 2 | 3 | /** 4 | * Response from the Central Portal API status endpoint. 5 | * See https://central.sonatype.org/publish/publish-portal-api/#verify-status-of-the-deployment 6 | */ 7 | internal data class DeploymentStatusResponse( 8 | val deploymentId: String, 9 | val deploymentName: String, 10 | val deploymentState: DeploymentState, 11 | val purls: List? = null, 12 | val errors: Map>? = null, 13 | ) 14 | 15 | /** 16 | * Possible states of a deployment in Maven Central. 17 | */ 18 | internal enum class DeploymentState { 19 | /** 20 | * A deployment is uploaded and waiting for processing by the validation service 21 | */ 22 | PENDING, 23 | 24 | /** 25 | * A deployment is being processed by the validation service 26 | */ 27 | VALIDATING, 28 | 29 | /** 30 | * A deployment has passed validation and is waiting on a user to manually publish via the Central Portal UI 31 | */ 32 | VALIDATED, 33 | 34 | /** 35 | * A deployment has been either automatically or manually published and is being uploaded to Maven Central 36 | */ 37 | PUBLISHING, 38 | 39 | /** 40 | * A deployment has successfully been uploaded to Maven Central 41 | */ 42 | PUBLISHED, 43 | 44 | /** 45 | * A deployment has encountered an error (additional context will be present in an errors field) 46 | */ 47 | FAILED, 48 | } 49 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/DropMavenCentralDeploymentTask.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.provider.Property 5 | import org.gradle.api.provider.Provider 6 | import org.gradle.api.tasks.Input 7 | import org.gradle.api.tasks.Internal 8 | import org.gradle.api.tasks.TaskAction 9 | import org.gradle.api.tasks.TaskContainer 10 | import org.gradle.api.tasks.TaskProvider 11 | import org.gradle.api.tasks.UntrackedTask 12 | import org.gradle.api.tasks.options.Option 13 | 14 | @UntrackedTask(because = "Not worth tracking") 15 | internal abstract class DropMavenCentralDeploymentTask : DefaultTask() { 16 | @get:Internal 17 | abstract val buildService: Property 18 | 19 | @get:Input 20 | @get:Option(option = "deployment-id", description = "Specify which deployment to drop.") 21 | abstract val deploymentId: Property 22 | 23 | @TaskAction 24 | fun dropDeployment() { 25 | buildService.get().dropDeployment(deploymentId.get()) 26 | } 27 | 28 | companion object { 29 | private const val NAME = "dropMavenCentralDeployment" 30 | 31 | fun TaskContainer.registerDropMavenCentralDeploymentTask( 32 | buildService: Provider, 33 | ): TaskProvider = register(NAME, DropMavenCentralDeploymentTask::class.java) { 34 | it.description = "Drops the deployment with the supplied id" 35 | it.group = "publishing" 36 | it.buildService.set(buildService) 37 | it.usesService(buildService) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/workaround/DirectorySignatureType.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.workaround 2 | 3 | import java.io.File 4 | import java.io.InputStream 5 | import java.io.OutputStream 6 | import org.gradle.api.file.Directory 7 | import org.gradle.api.provider.Provider 8 | import org.gradle.api.tasks.Internal 9 | import org.gradle.api.tasks.Nested 10 | import org.gradle.plugins.signing.signatory.Signatory 11 | import org.gradle.plugins.signing.type.AbstractSignatureType 12 | import org.gradle.plugins.signing.type.SignatureType 13 | 14 | /** 15 | * Creates signature files in a separate given directory to avoid 16 | * Gradle complaining about task depedencies missing when running 17 | * KMP tests and signing in the same build. 18 | * 19 | * https://youtrack.jetbrains.com/issue/KT-61313/ 20 | * https://github.com/gradle/gradle/issues/26132 21 | */ 22 | internal class DirectorySignatureType( 23 | @Nested 24 | val actual: SignatureType, 25 | @Internal 26 | val directory: Provider, 27 | ) : AbstractSignatureType() { 28 | override fun fileFor(toSign: File): File { 29 | val original = super.fileFor(toSign) 30 | return directory.get().file(original.name).asFile 31 | } 32 | 33 | override fun sign(signatory: Signatory?, toSign: File?): File { 34 | // needs to call super and not actual because this is what will call fileFor 35 | return super.sign(signatory, toSign) 36 | } 37 | 38 | override fun getExtension(): String = actual.extension 39 | 40 | override fun sign(signatory: Signatory?, toSign: InputStream?, destination: OutputStream?) { 41 | actual.sign(signatory, toSign, destination) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/EnableAutomaticMavenCentralPublishingTask.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | import com.vanniktech.maven.publish.DeploymentValidation 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.provider.Property 6 | import org.gradle.api.provider.Provider 7 | import org.gradle.api.tasks.Internal 8 | import org.gradle.api.tasks.TaskAction 9 | import org.gradle.api.tasks.TaskContainer 10 | import org.gradle.api.tasks.TaskProvider 11 | import org.gradle.api.tasks.UntrackedTask 12 | 13 | @UntrackedTask(because = "Not worth tracking") 14 | internal abstract class EnableAutomaticMavenCentralPublishingTask : DefaultTask() { 15 | @get:Internal 16 | abstract val buildService: Property 17 | 18 | @get:Internal 19 | abstract val validateDeployment: Property 20 | 21 | @TaskAction 22 | fun enableAutomaticPublishing() { 23 | buildService.get().enableAutomaticPublishing(validateDeployment.get()) 24 | } 25 | 26 | companion object { 27 | private const val NAME = "enableAutomaticMavenCentralPublishing" 28 | 29 | fun TaskContainer.registerEnableAutomaticMavenCentralPublishingTask( 30 | buildService: Provider, 31 | validateDeployment: DeploymentValidation, 32 | ): TaskProvider = register(NAME, EnableAutomaticMavenCentralPublishingTask::class.java) { 33 | it.description = "Enables automatic publishing for Maven Central" 34 | it.buildService.set(buildService) 35 | it.validateDeployment.set(validateDeployment) 36 | it.usesService(buildService) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "gradle-maven-publish-plugin" 2 | 3 | pluginManagement { 4 | includeBuild("build-logic") 5 | repositories { 6 | mavenCentral() 7 | google { 8 | mavenContent { 9 | includeGroupAndSubgroups("androidx") 10 | includeGroupAndSubgroups("com.android") 11 | includeGroupAndSubgroups("com.google") 12 | } 13 | } 14 | gradlePluginPortal() 15 | } 16 | } 17 | 18 | plugins { 19 | id("com.gradle.develocity") version "4.3" 20 | id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" 21 | } 22 | 23 | develocity { 24 | buildScan { 25 | termsOfUseUrl = "https://gradle.com/terms-of-service" 26 | termsOfUseAgree = "yes" 27 | // TODO: workaround for https://github.com/gradle/gradle/issues/22879. 28 | val isCI = providers.environmentVariable("CI").isPresent 29 | publishing.onlyIf { isCI } 30 | } 31 | } 32 | 33 | dependencyResolutionManagement { 34 | repositories { 35 | mavenCentral() 36 | google { 37 | mavenContent { 38 | includeGroupAndSubgroups("androidx") 39 | includeGroupAndSubgroups("com.android") 40 | includeGroupAndSubgroups("com.google") 41 | } 42 | } 43 | gradlePluginPortal() 44 | } 45 | 46 | versionCatalogs { 47 | create("alpha") { 48 | from(files("gradle/alpha.versions.toml")) 49 | } 50 | create("beta") { 51 | from(files("gradle/beta.versions.toml")) 52 | } 53 | create("rc") { 54 | from(files("gradle/rc.versions.toml")) 55 | } 56 | } 57 | } 58 | 59 | enableFeaturePreview("STABLE_CONFIGURATION_CACHE") 60 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 61 | 62 | include(":plugin") 63 | include(":central-portal") 64 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Gradle Maven Publish Plugin 2 | site_url: https://vanniktech.github.io/gradle-maven-publish-plugin/ 3 | repo_name: gradle-maven-publish-plugin 4 | repo_url: https://github.com/vanniktech/gradle-maven-publish-plugin 5 | site_author: Niklas Baudy 6 | edit_uri: edit/main/docs 7 | 8 | copyright: 'Copyright (C) 2018 Vanniktech - Niklas Baudy' 9 | 10 | theme: 11 | name: 'material' 12 | palette: 13 | - media: '(prefers-color-scheme: light)' 14 | scheme: default 15 | primary: 'white' 16 | toggle: 17 | icon: material/brightness-7 18 | name: Switch to dark mode 19 | - media: '(prefers-color-scheme: dark)' 20 | scheme: slate 21 | primary: 'black' 22 | toggle: 23 | icon: material/brightness-4 24 | name: Switch to light mode 25 | font: 26 | text: 'Lato' 27 | code: 'JetBrains Mono' 28 | features: 29 | - navigation.sections 30 | - navigation.tracking 31 | - navigation.tabs 32 | - content.code.copy 33 | - content.code.select 34 | - content.tabs.link 35 | - toc.integrate 36 | - content.action.edit 37 | 38 | nav: 39 | - 'index.md' 40 | - 'central.md' 41 | - 'other.md' 42 | - 'base.md' 43 | - 'changelog.md' 44 | - 'API': api/index.html 45 | 46 | markdown_extensions: 47 | - smarty 48 | - codehilite: 49 | guess_lang: false 50 | - footnotes 51 | - meta 52 | - toc: 53 | permalink: true 54 | - pymdownx.betterem: 55 | smart_enable: all 56 | - pymdownx.caret 57 | - pymdownx.inlinehilite 58 | - pymdownx.magiclink 59 | - pymdownx.smartsymbols 60 | - pymdownx.superfences 61 | - pymdownx.tilde 62 | - pymdownx.superfences 63 | - pymdownx.tabbed: 64 | alternate_style: true 65 | - admonition 66 | - pymdownx.details 67 | - tables 68 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/TestOptionsProvider.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.testing.junit.testparameterinjector.junit5.TestParameterValuesProvider 4 | import com.vanniktech.maven.publish.IntegrationTestBuildConfig.QUICK_TEST 5 | import com.vanniktech.maven.publish.IntegrationTestBuildConfig.TEST_CONFIG_METHOD 6 | 7 | internal class TestOptionsConfigProvider : TestParameterValuesProvider() { 8 | override fun provideValues(context: Context?): List<*> = when { 9 | QUICK_TEST -> listOf(TestOptions.Config.BASE) 10 | TEST_CONFIG_METHOD.isNotBlank() -> listOf(TestOptions.Config.valueOf(TEST_CONFIG_METHOD)) 11 | else -> TestOptions.Config.entries 12 | } 13 | } 14 | 15 | internal class GradleVersionProvider : TestParameterValuesProvider() { 16 | override fun provideValues(context: Context?): List<*> { 17 | if (QUICK_TEST) { 18 | return listOf(GradleVersion.VERSIONS.max()) 19 | } 20 | return GradleVersion.VERSIONS.toList() 21 | } 22 | } 23 | 24 | internal class AgpVersionProvider : TestParameterValuesProvider() { 25 | override fun provideValues(context: Context?): List<*> { 26 | if (QUICK_TEST) { 27 | return listOf(AgpVersion.VERSIONS.max()) 28 | } 29 | return AgpVersion.VERSIONS.toList() 30 | } 31 | } 32 | 33 | internal class KotlinVersionProvider : TestParameterValuesProvider() { 34 | override fun provideValues(context: Context?): List<*> { 35 | if (QUICK_TEST) { 36 | return listOf(KotlinVersion.VERSIONS.max()) 37 | } 38 | return KotlinVersion.VERSIONS.toList() 39 | } 40 | } 41 | 42 | internal class GradlePluginPublishVersionProvider : TestParameterValuesProvider() { 43 | override fun provideValues(context: Context?): List<*> { 44 | if (QUICK_TEST) { 45 | return listOf(GradlePluginPublish.VERSIONS.max()) 46 | } 47 | return GradlePluginPublish.VERSIONS.toList() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | build: 15 | name: JDK ${{ matrix.test_java_version }}, Config ${{ matrix.test_config_method }} 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | test_java_version: 21 | - 17 # Minimum supported version. 22 | - latest # Latest supported version, the value syncs with the .java-version file. 23 | test_config_method: ["DSL", "PROPERTIES", "BASE"] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v6 28 | 29 | - name: Install JDK 30 | uses: actions/setup-java@v5 31 | with: 32 | distribution: zulu 33 | java-version-file: .github/workflows/.java-version 34 | 35 | - name: Accept Android SDK license 36 | run: echo -e "24333f8a63b6825ea9c5514f83c2829b004d1fee\n" > "$ANDROID_HOME/licenses/android-sdk-license" 37 | 38 | - uses: gradle/actions/setup-gradle@v5 39 | 40 | - name: Run ktlint 41 | run: ./kotlinw ktlint.main.kts --fail-on-changes 42 | 43 | - name: Build with Gradle 44 | run: ./gradlew build --stacktrace -PtestJavaVersion=${{ matrix.test_java_version }} -PtestConfigMethod=${{ matrix.test_config_method }} 45 | 46 | # Status check that is required in branch protection rules. 47 | build-status: 48 | needs: 49 | - build 50 | runs-on: ubuntu-latest 51 | if: always() 52 | steps: 53 | - name: Check 54 | run: | 55 | results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}') 56 | if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then 57 | echo "One or more required jobs failed" 58 | exit 1 59 | fi 60 | echo "All required jobs completed successfully." 61 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '**' 7 | 8 | jobs: 9 | publish: 10 | 11 | runs-on: ubuntu-latest 12 | if: github.repository == 'vanniktech/gradle-maven-publish-plugin' 13 | permissions: 14 | contents: write 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v6 19 | 20 | - name: Install JDK 21 | uses: actions/setup-java@v5 22 | with: 23 | distribution: zulu 24 | java-version-file: .github/workflows/.java-version 25 | 26 | - name: Set version for tag 27 | run: | 28 | echo "ORG_GRADLE_PROJECT_VERSION_NAME=${{ github.ref_name }}" >> $GITHUB_ENV 29 | 30 | - uses: gradle/actions/setup-gradle@v5 31 | 32 | - name: Publish 33 | run: ./gradlew publishToMavenCentral 34 | env: 35 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }} 36 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }} 37 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_PRIVATE_KEY }} 38 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} 39 | 40 | - name: Extract release notes 41 | uses: ffurrer2/extract-release-notes@v3 42 | with: 43 | changelog_file: CHANGELOG.md 44 | release_notes_file: RELEASE_NOTES.md 45 | prerelease: ${{ contains(github.ref_name, 'rc') }} 46 | 47 | - name: Create prerelease 48 | if: ${{ contains(github.ref_name, 'rc') }} 49 | run: gh release create "${{ github.ref_name }}" --notes-file RELEASE_NOTES.md --prerelease 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | - name: Create final release 54 | if: ${{ !contains(github.ref_name, 'rc') }} 55 | run: gh release create "${{ github.ref_name }}" --notes-file RELEASE_NOTES.md 56 | env: 57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 58 | -------------------------------------------------------------------------------- /docs/base.md: -------------------------------------------------------------------------------- 1 | # Base plugin 2 | 3 | The base plugin generally provides the same functionality as the main plugin, 4 | but it doesn't configure anything automatically and instead relies on manual 5 | configuration. 6 | 7 | ## Applying the plugin 8 | 9 | Add the plugin to any Gradle project that should be published 10 | 11 | === "build.gradle" 12 | 13 | ```groovy 14 | plugins { 15 | id "com.vanniktech.maven.publish.base" version "" 16 | } 17 | ``` 18 | 19 | === "build.gradle.kts" 20 | 21 | ```kotlin 22 | plugins { 23 | id("com.vanniktech.maven.publish.base") version "" 24 | } 25 | ``` 26 | 27 | ## General configuration 28 | 29 | Follow the steps of the [Maven Central](central.md) or [other Maven repositories](other.md) guides. 30 | 31 | !!! note 32 | 33 | The `mavenCentralPublishing`, `mavenCentralAutomaticPublishing` and `signAllPublications` properties are not 34 | considered by the base plugin and the appropriate DSL methods need to be called. 35 | 36 | For the pom configuration via Gradle properties the following needs to be enabled in the DSL: 37 | 38 | === "build.gradle" 39 | 40 | ```groovy 41 | mavenPublishing { 42 | pomFromGradleProperties() 43 | } 44 | ``` 45 | 46 | === "build.gradle.kts" 47 | 48 | ```kotlin 49 | mavenPublishing { 50 | pomFromGradleProperties() 51 | } 52 | ``` 53 | 54 | ## Configuring what to publish 55 | 56 | To define what should be published, the `configure` method in the `mavenPublishing` 57 | block needs to be called. Check out the [what to publish page](what.md) which 58 | contains a detailed description of available options for each project type. 59 | 60 | It is also possible to get the automatic behavior of the main plugin by calling 61 | `configureBasedOnAppliedPlugins()` instead of `configure` 62 | 63 | === "build.gradle" 64 | 65 | ```groovy 66 | mavenPublishing { 67 | configure(...) 68 | // or 69 | configureBasedOnAppliedPlugins() 70 | } 71 | ``` 72 | 73 | === "build.gradle.kts" 74 | 75 | ```kotlin 76 | mavenPublishing { 77 | configure(...) 78 | // or 79 | configureBasedOnAppliedPlugins() 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/ProjectExtensions.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UnstableApiUsage") 2 | 3 | package com.vanniktech.maven.publish 4 | 5 | import com.android.build.api.AndroidPluginVersion 6 | import com.android.build.api.variant.AndroidComponentsExtension 7 | import org.gradle.api.Action 8 | import org.gradle.api.Project 9 | import org.gradle.api.publish.PublishingExtension 10 | import org.gradle.api.publish.maven.MavenPublication 11 | import org.gradle.plugins.signing.SigningExtension 12 | import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin 13 | import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion 14 | 15 | internal inline val Project.baseExtension: MavenPublishBaseExtension 16 | get() = extensions.getByType(MavenPublishBaseExtension::class.java) 17 | 18 | internal inline val Project.gradleSigning: SigningExtension 19 | get() = extensions.getByType(SigningExtension::class.java) 20 | 21 | internal inline val Project.gradlePublishing: PublishingExtension 22 | get() = extensions.getByType(PublishingExtension::class.java) 23 | 24 | internal inline val Project.androidComponents: AndroidComponentsExtension<*, *, *> 25 | get() = extensions.getByType(AndroidComponentsExtension::class.java) 26 | 27 | internal fun Project.mavenPublications(action: Action) { 28 | gradlePublishing.publications.withType(MavenPublication::class.java).configureEach(action) 29 | } 30 | 31 | internal fun Project.mavenPublicationsWithoutPluginMarker(action: Action) { 32 | mavenPublications { 33 | if (!it.name.endsWith("PluginMarkerMaven")) { 34 | action.execute(it) 35 | } 36 | } 37 | } 38 | 39 | internal fun Project.isAtLeastKgp(id: String, version: String): Boolean { 40 | val actual = (plugins.getPlugin(id) as KotlinBasePlugin).pluginVersion 41 | return KotlinToolingVersion(actual) >= KotlinToolingVersion(version) 42 | } 43 | 44 | internal fun isAtLeastAgp(version: String): Boolean { 45 | // Drop everything after '-' to ignore rc/beta/alpha suffixes. If you want to compare those, use the rc/beta/alpha functions in AndroidPluginVersion. 46 | val (major, minor, patch) = version.takeWhile { it != '-' }.split(".").map { it.toInt() } 47 | return AndroidPluginVersion.getCurrent() >= AndroidPluginVersion(major, minor, patch) 48 | } 49 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/workaround/TestFixtures.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("InternalGradleApiUsage") 2 | 3 | package com.vanniktech.maven.publish.workaround 4 | 5 | import com.vanniktech.maven.publish.baseExtension 6 | import org.gradle.api.Project 7 | import org.gradle.api.attributes.DocsType 8 | import org.gradle.api.component.AdhocComponentWithVariants 9 | import org.gradle.api.internal.project.ProjectInternal 10 | import org.gradle.api.plugins.JavaPluginExtension 11 | import org.gradle.api.plugins.internal.JavaConfigurationVariantMapping 12 | import org.gradle.api.plugins.internal.JvmPluginsHelper 13 | import org.gradle.internal.component.external.model.ProjectDerivedCapability 14 | 15 | /** 16 | * Gradle currently doesn't publish a sources jar for test fixtures and the APIs to add 17 | * one are internal. 18 | * 19 | * https://github.com/gradle/gradle/issues/20539 20 | */ 21 | internal fun addTestFixturesSourcesJar(project: Project) { 22 | val testFixtureSourceSetName = "testFixtures" 23 | val extension = project.extensions.getByType(JavaPluginExtension::class.java) 24 | val testFixturesSourceSet = extension.sourceSets.maybeCreate(testFixtureSourceSetName) 25 | val projectInternal = project as ProjectInternal 26 | val projectDerivedCapability = ProjectDerivedCapability(projectInternal, "testFixtures") 27 | val sourceElements = JvmPluginsHelper.createDocumentationVariantWithArtifact( 28 | testFixturesSourceSet.sourcesElementsConfigurationName, 29 | testFixtureSourceSetName, 30 | DocsType.SOURCES, 31 | setOf(projectDerivedCapability), 32 | testFixturesSourceSet.sourcesJarTaskName, 33 | testFixturesSourceSet.allSource, 34 | projectInternal, 35 | ) 36 | val component = project.components.findByName("java") as AdhocComponentWithVariants 37 | component.addVariantsFromConfiguration(sourceElements, JavaConfigurationVariantMapping("compile", true)) 38 | } 39 | 40 | /** 41 | * Gradle will put the project group and version into capabilities instead of using 42 | * the publication, this can lead to invalid published metadata 43 | * 44 | * https://github.com/gradle/gradle/issues/23354 45 | */ 46 | internal fun fixTestFixturesMetadata(project: Project) { 47 | project.afterEvaluate { 48 | project.group = project.baseExtension.groupId.get() 49 | project.version = project.baseExtension.version.get() 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | name: Publish Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | # TODO: replace with on push tag https://github.com/actions/deploy-pages/issues/76 8 | workflow_run: 9 | workflows: [Publish Release] 10 | types: 11 | - completed 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | publish: 19 | 20 | runs-on: ubuntu-latest 21 | if: github.repository == 'vanniktech/gradle-maven-publish-plugin' 22 | 23 | permissions: 24 | contents: read 25 | pages: write 26 | id-token: write 27 | 28 | environment: 29 | name: github-pages 30 | url: ${{ steps.deployment.outputs.page_url }} 31 | 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v6 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Install JDK 39 | uses: actions/setup-java@v5 40 | with: 41 | distribution: zulu 42 | java-version-file: .github/workflows/.java-version 43 | 44 | - uses: gradle/actions/setup-gradle@v5 45 | with: 46 | cache-read-only: true 47 | 48 | - name: Install Python 49 | uses: actions/setup-python@v6 50 | with: 51 | python-version: 3.x 52 | 53 | - name: Install MkDocs Material 54 | run: pip install mkdocs-material 55 | 56 | - name: Copy docs 57 | run: | 58 | cp README.md docs/index.md 59 | cp CHANGELOG.md docs/changelog.md 60 | # Update page title 61 | sed -i 's/# gradle-maven-publish-plugin/# Overview/' docs/index.md 62 | # Update plugin version 63 | sed -i "s//$(git describe --tags --abbrev=0)/" docs/base.md 64 | sed -i "s//$(git describe --tags --abbrev=0)/" docs/central.md 65 | sed -i "s//$(git describe --tags --abbrev=0)/" docs/other.md 66 | 67 | - name: Generate API documentation 68 | run: ./gradlew dokkaGenerate 69 | 70 | - name: Build MkDocs 71 | run: mkdocs build 72 | 73 | - name: Upload artifact 74 | uses: actions/upload-pages-artifact@v4 75 | with: 76 | path: site 77 | 78 | - name: Deploy to GitHub Pages 79 | id: deployment 80 | uses: actions/deploy-pages@v4 81 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: 'https://docs.renovatebot.com/renovate-schema.json', 3 | extends: [ 4 | 'config:recommended', 5 | ], 6 | rebaseWhen: 'conflicted', 7 | ignoreUnstable: false, 8 | packageRules: [ 9 | { 10 | allowedVersions: '/^v?[0-9]+(\\.[0-9]+){0,2}$/', 11 | matchPackageNames: [ 12 | '/.*/', 13 | ], 14 | }, 15 | // Enable auto-merge for minor/patch updates. 16 | { 17 | matchUpdateTypes: [ 18 | 'minor', 19 | 'patch', 20 | ], 21 | automerge: true, 22 | matchPackageNames: [ 23 | '/.*/', 24 | ], 25 | }, 26 | { 27 | // See https://github.com/vanniktech/gradle-maven-publish-plugin/issues/1131. 28 | groupName: 'Retrofit and OkHttp', 29 | enabled: false, 30 | matchPackageNames: [ 31 | '/com.squareup.retrofit2:*/', 32 | '/com.squareup.okhttp3:*/', 33 | ], 34 | }, 35 | { 36 | matchFileNames: [ 37 | '**/alpha.versions.toml', 38 | ], 39 | allowedVersions: '!/(-SNAPSHOT)/', 40 | }, 41 | { 42 | matchFileNames: [ 43 | '**/beta.versions.toml', 44 | ], 45 | allowedVersions: '!/(-alpha|-Alpha|-ALPHA|-milestone|-m|-SNAPSHOT)/', 46 | }, 47 | { 48 | matchFileNames: [ 49 | '**/rc.versions.toml', 50 | ], 51 | allowedVersions: '!/(-alpha|-Alpha|-ALPHA|-beta|-Beta|-BETA|-milestone|-m|-SNAPSHOT)/', 52 | }, 53 | ], 54 | ignorePresets: [ 55 | // Ensure we get the latest version and are not pinned to old versions. 56 | 'workarounds:javaLTSVersions', 57 | ], 58 | customManagers: [ 59 | { 60 | customType: 'regex', 61 | managerFilePatterns: [ 62 | '/\\.versions.toml$/', 63 | ], 64 | matchStrings: [ 65 | '(^|\n)(?gradle) *= *"(?.*)"', 66 | ], 67 | depNameTemplate: 'gradle', 68 | datasourceTemplate: 'gradle-version', 69 | }, 70 | // Update .java-version file with the latest JDK version. 71 | { 72 | customType: 'regex', 73 | managerFilePatterns: [ 74 | '/\\.java-version$/', 75 | ], 76 | matchStrings: [ 77 | '(?.*)\\n', 78 | ], 79 | datasourceTemplate: 'java-version', 80 | depNameTemplate: 'java', 81 | // Only write the major version. 82 | extractVersionTemplate: '^(?\\d+)', 83 | }, 84 | ], 85 | } 86 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/MavenPublishBasePlugin.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.vanniktech.maven.publish.BuildConfig.ANDROID_GRADLE_MIN 4 | import com.vanniktech.maven.publish.BuildConfig.KOTLIN_MIN 5 | import org.gradle.api.Plugin 6 | import org.gradle.api.Project 7 | import org.gradle.api.publish.maven.plugins.MavenPublishPlugin as GradleMavenPublishPlugin 8 | 9 | public abstract class MavenPublishBasePlugin : Plugin { 10 | override fun apply(project: Project) { 11 | project.plugins.apply(GradleMavenPublishPlugin::class.java) 12 | 13 | project.checkMinimumVersions() 14 | 15 | project.extensions.create("mavenPublishing", MavenPublishBaseExtension::class.java, project) 16 | } 17 | 18 | private fun Project.checkMinimumVersions() { 19 | plugins.withId("com.android.library") { 20 | try { 21 | if (!isAtLeastAgp(ANDROID_GRADLE_MIN)) { 22 | error("You need AGP version $ANDROID_GRADLE_MIN or newer") 23 | } 24 | } catch (t: Throwable) { 25 | throw IllegalStateException( 26 | "Make sure the AGP version $ANDROID_GRADLE_MIN or newer is applied." + 27 | "Otherwise, detected Android Library plugin but was not able to access AGP classes. Please make sure " + 28 | "that the Android plugin and the publish plugin are applied to the same project. In many cases this means " + 29 | "you need to add both the root project with `apply false`.", 30 | t, 31 | ) 32 | } 33 | } 34 | KOTLIN_PLUGIN_IDS.forEach { pluginId -> 35 | plugins.withId(pluginId) { 36 | try { 37 | if (!isAtLeastKgp(pluginId, KOTLIN_MIN)) { 38 | error("You need Kotlin version $KOTLIN_MIN or newer") 39 | } 40 | } catch (t: Throwable) { 41 | throw IllegalStateException( 42 | "Make sure the Kotlin version $KOTLIN_MIN or newer is applied." + 43 | "Otherwise, detected Kotlin plugin $pluginId but was not able to access Kotlin plugin classes. Please make sure " + 44 | "that the Kotlin plugin and the publish plugin are applied to the same project. In many cases this means " + 45 | "you need to add both the root project with `apply false`.", 46 | t, 47 | ) 48 | } 49 | } 50 | } 51 | } 52 | 53 | private companion object { 54 | val KOTLIN_PLUGIN_IDS = listOf( 55 | "org.jetbrains.kotlin.jvm", 56 | "org.jetbrains.kotlin.multiplatform", 57 | ) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/tasks/JavadocJar.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.tasks 2 | 3 | import com.vanniktech.maven.publish.JavadocJar as JavadocJarOption 4 | import com.vanniktech.maven.publish.JavadocJar.Dokka.DokkaTaskWrapper 5 | import com.vanniktech.maven.publish.baseExtension 6 | import java.util.Locale 7 | import org.gradle.api.Project 8 | import org.gradle.api.tasks.CacheableTask 9 | import org.gradle.api.tasks.TaskProvider 10 | import org.gradle.jvm.tasks.Jar 11 | 12 | @CacheableTask 13 | public abstract class JavadocJar : Jar() { 14 | init { 15 | archiveClassifier.set("javadoc") 16 | } 17 | 18 | internal companion object { 19 | internal fun Project.javadocJarTask(javadocJar: JavadocJarOption, prefix: String?): TaskProvider? = when (javadocJar) { 20 | is JavadocJarOption.None -> null 21 | is JavadocJarOption.Empty -> emptyJavadocJar(prefix) 22 | is JavadocJarOption.Javadoc -> plainJavadocJar(prefix) 23 | is JavadocJarOption.Dokka -> dokkaJavadocJar(prefix, javadocJar.wrapper) 24 | } 25 | 26 | private fun Project.emptyJavadocJar(prefix: String?): TaskProvider = tasks.register( 27 | prefixedTaskName("emptyJavadocJar", prefix), 28 | JavadocJar::class.java, 29 | ) { 30 | it.updateArchivesBaseNameWithPrefix(project, prefix) 31 | } 32 | 33 | private fun Project.plainJavadocJar(prefix: String?): TaskProvider = 34 | tasks.register(prefixedTaskName("plainJavadocJar", prefix), JavadocJar::class.java) { 35 | val javadocTask = tasks.named("javadoc") 36 | it.dependsOn(javadocTask) 37 | it.from(javadocTask) 38 | it.updateArchivesBaseNameWithPrefix(project, prefix) 39 | } 40 | 41 | private fun Project.dokkaJavadocJar(prefix: String?, dokkaTaskWrapper: DokkaTaskWrapper): TaskProvider = 42 | tasks.register(prefixedTaskName("dokkaJavadocJar", prefix), JavadocJar::class.java) { 43 | val dokkaTask = dokkaTaskWrapper.asProvider(project) 44 | it.dependsOn(dokkaTask) 45 | it.from(dokkaTask) 46 | it.updateArchivesBaseNameWithPrefix(project, prefix) 47 | } 48 | 49 | private fun prefixedTaskName(name: String, prefix: String?): String = if (prefix != null) { 50 | "${prefix}${name.replaceFirstChar { it.titlecase(Locale.US) }}" 51 | } else { 52 | name 53 | } 54 | 55 | private fun Jar.updateArchivesBaseNameWithPrefix(project: Project, prefix: String?) { 56 | if (prefix != null) { 57 | archiveBaseName.set(project.baseExtension.artifactId.map { "$it-$prefix" }) 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | jdkRelease = "17" 3 | minGradle = "9.0.0" 4 | minAgp = "8.13.0" 5 | minKgp = "2.2.0" 6 | 7 | gradle = "9.2.1" 8 | 9 | kotlin = "2.2.21" 10 | 11 | android-gradle = "8.13.2" 12 | 13 | dokka = "2.1.0" 14 | 15 | gradle-plugin-publish = "1.3.1" 16 | 17 | junit-platform = "6.0.1" 18 | 19 | # Becareful when updating these versions, as they may conflict with Gradle's builtin Kotlin versions. 20 | # See https://github.com/vanniktech/gradle-maven-publish-plugin/issues/1131. 21 | retrofit = "3.0.0" 22 | okhttp = "5.1.0" 23 | moshi = "1.15.2" 24 | 25 | slf4j = "1.7.36" 26 | 27 | [libraries] 28 | dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka" } 29 | android-pluginApi = { module = "com.android.tools.build:gradle-api", version.ref = "android-gradle" } 30 | maven-publish-plugin = "com.vanniktech:gradle-maven-publish-plugin:0.35.0" 31 | 32 | kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } 33 | kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 34 | 35 | retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } 36 | retrofit-converter-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" } 37 | retrofit-converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" } 38 | moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } 39 | moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } 40 | okhttp-client = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } 41 | okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } 42 | 43 | slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } 44 | slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } 45 | 46 | junit-jupiter = "org.junit.jupiter:junit-jupiter:6.0.1" 47 | junit-engine = { module = "org.junit.platform:junit-platform-engine", version.ref = "junit-platform" } 48 | junit-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" } 49 | testParameterInjector = "com.google.testparameterinjector:test-parameter-injector-junit5:1.20" 50 | truth = "com.google.truth:truth:1.4.5" 51 | truth-testKit = "com.autonomousapps:testkit-truth:1.1" 52 | maven-model = "org.apache.maven:maven-model:3.9.12" 53 | semver = "net.swiftzer.semver:semver:2.1.0" 54 | 55 | androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" 56 | 57 | [plugins] 58 | buildconfig = "com.github.gmazzo.buildconfig:6.0.6" 59 | android-lint = { id = "com.android.lint", version.ref = "android-gradle" } 60 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/Properties.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import kotlin.text.toBoolean 4 | import kotlin.time.Duration 5 | import kotlin.time.Duration.Companion.minutes 6 | import kotlin.time.Duration.Companion.seconds 7 | import org.gradle.api.Project 8 | import org.gradle.api.provider.Provider 9 | 10 | internal fun Project.mavenCentralPublishing(): Boolean { 11 | val central = providers.gradleProperty("mavenCentralPublishing").orNull 12 | if (central != null) { 13 | return central.toBoolean() 14 | } 15 | return when (providers.gradleProperty("SONATYPE_HOST").orNull) { 16 | null -> false 17 | "CENTRAL_PORTAL" -> true 18 | else -> error( 19 | """ 20 | OSSRH was shut down on June 30, 2025. Migrate to CENTRAL_PORTAL instead. 21 | See more info at https://central.sonatype.org/news/20250326_ossrh_sunset. 22 | """.trimIndent(), 23 | ) 24 | } 25 | } 26 | 27 | internal fun Project.automaticRelease(): Boolean { 28 | val automatic = providers.gradleProperty("mavenCentralAutomaticPublishing").orNull 29 | if (automatic != null) { 30 | return automatic.toBoolean() 31 | } 32 | return providers.gradleProperty("SONATYPE_AUTOMATIC_RELEASE").getOrElse("false").toBoolean() 33 | } 34 | 35 | internal fun Project.validateDeployment(): DeploymentValidation { 36 | val automatic = providers.gradleProperty("mavenCentralDeploymentValidation").orNull 37 | if (automatic != null) { 38 | return automatic.toDeploymentValidation() 39 | } 40 | return providers.gradleProperty("SONATYPE_DEPLOYMENT_VALIDATION").getOrElse("true").toDeploymentValidation() 41 | } 42 | 43 | private fun String.toDeploymentValidation() = when (this) { 44 | "true" -> DeploymentValidation.PUBLISH 45 | "false" -> DeploymentValidation.NONE 46 | else -> DeploymentValidation.valueOf(this) 47 | } 48 | 49 | internal fun Project.signAllPublications(): Boolean { 50 | val sign = providers.gradleProperty("signAllPublications").orNull 51 | if (sign != null) { 52 | return sign.toBoolean() 53 | } 54 | return providers.gradleProperty("RELEASE_SIGNING_ENABLED").getOrElse("false").toBoolean() 55 | } 56 | 57 | internal fun Project.connectTimeout(): Provider = providers 58 | .gradleProperty("SONATYPE_CONNECT_TIMEOUT_SECONDS") 59 | .map { it.toLong().seconds } 60 | .orElse(60.seconds) 61 | 62 | internal fun Project.closeTimeout(): Provider = providers 63 | .gradleProperty("SONATYPE_CLOSE_TIMEOUT_SECONDS") 64 | .map { it.toLong().seconds } 65 | .orElse(60.minutes) 66 | 67 | internal fun Project.pollIntervalSeconds(): Provider = providers 68 | .gradleProperty("SONATYPE_POLL_INTERVAL_SECONDS") 69 | .map { it.toLong().seconds } 70 | .orElse(5.seconds) 71 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/DokkaPluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.testing.junit.testparameterinjector.junit5.TestParameterInjectorTest 4 | import com.vanniktech.maven.publish.ProjectResultSubject.Companion.assertThat 5 | import com.vanniktech.maven.publish.TestOptions.Signing.NO_SIGNING 6 | import org.junit.jupiter.api.condition.DisabledOnJre 7 | import org.junit.jupiter.api.condition.JRE 8 | 9 | class DokkaPluginTest : BasePluginTest() { 10 | override val testOptions get() = TestOptions(config, NO_SIGNING, gradleVersion) 11 | 12 | @DisabledOnJre( 13 | value = [JRE.JAVA_25], 14 | disabledReason = "Dokka 1.x does not support Java 25+.", 15 | ) 16 | @TestParameterInjectorTest 17 | fun dokka() { 18 | val kotlinVersion = KotlinVersion.VERSIONS.last() 19 | val original = kotlinJvmProjectSpec(kotlinVersion) 20 | val project = original.copy( 21 | plugins = original.plugins + dokkaPlugin, 22 | basePluginConfig = original.basePluginConfig.replace( 23 | "JavadocJar.Empty()", 24 | "JavadocJar.Dokka(\"dokkaGeneratePublicationHtml\")", 25 | ), 26 | ) 27 | val result = project.run(fixtures, testProjectDir, testOptions) 28 | 29 | assertThat(result).outcome().succeeded() 30 | assertThat(result).artifact("jar").exists() 31 | assertThat(result).pom().exists() 32 | assertThat(result).pom().matchesExpectedPom(kotlinStdlibJdk(kotlinVersion)) 33 | assertThat(result).module().exists() 34 | assertThat(result).sourcesJar().exists() 35 | assertThat(result).sourcesJar().containsAllSourceFiles() 36 | assertThat(result).javadocJar().exists() 37 | assertThat(result).javadocJar().containsFiles(ignoreAdditionalFiles = true, "index.html") 38 | } 39 | 40 | @TestParameterInjectorTest 41 | fun dokkaJavadoc() { 42 | val kotlinVersion = KotlinVersion.VERSIONS.last() 43 | val original = kotlinJvmProjectSpec(kotlinVersion) 44 | val project = original.copy( 45 | plugins = original.plugins + dokkaJavadocPlugin, 46 | basePluginConfig = original.basePluginConfig.replace( 47 | "JavadocJar.Empty()", 48 | "JavadocJar.Dokka(\"dokkaGeneratePublicationJavadoc\")", 49 | ), 50 | ) 51 | val result = project.run(fixtures, testProjectDir, testOptions) 52 | 53 | assertThat(result).outcome().succeeded() 54 | assertThat(result).artifact("jar").exists() 55 | assertThat(result).pom().exists() 56 | assertThat(result).pom().matchesExpectedPom(kotlinStdlibJdk(kotlinVersion)) 57 | assertThat(result).module().exists() 58 | assertThat(result).sourcesJar().exists() 59 | assertThat(result).sourcesJar().containsAllSourceFiles() 60 | assertThat(result).javadocJar().exists() 61 | assertThat(result).javadocJar().containsFiles(ignoreAdditionalFiles = true, "index.html") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/PrepareMavenCentralPublishingTask.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.file.Directory 5 | import org.gradle.api.file.DirectoryProperty 6 | import org.gradle.api.provider.Property 7 | import org.gradle.api.provider.Provider 8 | import org.gradle.api.tasks.Input 9 | import org.gradle.api.tasks.Internal 10 | import org.gradle.api.tasks.TaskAction 11 | import org.gradle.api.tasks.TaskContainer 12 | import org.gradle.api.tasks.TaskProvider 13 | import org.gradle.api.tasks.UntrackedTask 14 | 15 | @UntrackedTask(because = "Not worth tracking") 16 | internal abstract class PrepareMavenCentralPublishingTask : DefaultTask() { 17 | @get:Internal 18 | abstract val projectGroup: Property 19 | 20 | @get:Input 21 | abstract val artifactId: Property 22 | 23 | @get:Input 24 | abstract val version: Property 25 | 26 | @get:Internal 27 | abstract val localRepository: DirectoryProperty 28 | 29 | @get:Internal 30 | abstract val buildService: Property 31 | 32 | @TaskAction 33 | fun registerProject() { 34 | val localRepository = localRepository.asFile.get() 35 | 36 | // delete local repository from previous publishing attempts to ensure only current files are published. 37 | if (localRepository.exists()) { 38 | localRepository.deleteRecursively() 39 | } 40 | 41 | check( 42 | buildService 43 | .get() 44 | .parameters.repositoryUsername.isPresent, 45 | ) { 46 | "mavenCentralUsername not found, which is required for publishing to Maven Central." 47 | } 48 | check( 49 | buildService 50 | .get() 51 | .parameters.repositoryPassword.isPresent, 52 | ) { 53 | "mavenCentralPassword not found, which is required for publishing to Maven Central." 54 | } 55 | 56 | buildService.get().registerProject(projectGroup.get(), artifactId.get(), version.get(), localRepository) 57 | } 58 | 59 | companion object { 60 | private const val NAME = "prepareMavenCentralPublishing" 61 | 62 | fun TaskContainer.registerPrepareMavenCentralPublishingTask( 63 | buildService: Provider, 64 | group: Provider, 65 | artifactId: Provider, 66 | version: Provider, 67 | localRepository: Provider, 68 | ): TaskProvider = register(NAME, PrepareMavenCentralPublishingTask::class.java) { 69 | it.description = "Prepare for publishing to Maven Central" 70 | it.projectGroup.set(group) 71 | it.artifactId.set(artifactId) 72 | it.version.set(version) 73 | it.localRepository.set(localRepository) 74 | it.buildService.set(buildService) 75 | it.usesService(buildService) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gradle-maven-publish-plugin 2 | 3 | Gradle plugin that creates a `publish` task to automatically upload all of your Java, Kotlin or Android 4 | libraries to any Maven instance. This plugin is based on [Chris Banes initial implementation](https://github.com/chrisbanes/gradle-mvn-push) 5 | and has been enhanced to add Kotlin support and keep up with the latest changes. 6 | 7 | # Setup 8 | 9 | - [Publishing open source projects to Maven Central](https://vanniktech.github.io/gradle-maven-publish-plugin/central/) 10 | - [Publishing to other Maven repositories](https://vanniktech.github.io/gradle-maven-publish-plugin/other/) 11 | 12 | For modifying what is getting published see [configuring what to publish](https://vanniktech.github.io/gradle-maven-publish-plugin/what/). 13 | 14 | There is also a [base plugin](https://vanniktech.github.io/gradle-maven-publish-plugin/base/) that doesn't apply any 15 | default configuration and allows the most customization. 16 | 17 | # Supported plugins 18 | 19 | The output of the following Gradle plugins is supported to be published with this plugin: 20 | 21 | - `com.android.fused-library` 22 | - `com.android.kotlin.multiplatform.library` 23 | - `com.android.library` 24 | - `com.gradle.plugin-publish` 25 | - `java` 26 | - `java-gradle-plugin` 27 | - `java-library` 28 | - `java-platform` 29 | - `org.jetbrains.kotlin.jvm` 30 | - `org.jetbrains.kotlin.multiplatform` 31 | - `version-catalog` 32 | 33 | # Advantages over `maven-publish` 34 | 35 | Gradle ships with the `maven-publish` and many other plugins like the Android Gradle Plugin or the Kotlin Multiplatform 36 | plugin directly integrate with, so why should you use this plugin? 37 | 38 | - **No need to know how publishing works for different project types**. AGP provides an API to configure publishing, 39 | `java-library` too, Kotlin Multiplatform does most things automatically but not everything. This plugin configures 40 | as much as possible on its own. 41 | - **An unified approach for all kinds of projects**. Some parts require manual configuration and for those we provide an API 42 | that works regardless of whether this is a Gradle plugin, an Android library or a Kotlin Multiplatform project. This 43 | is especially useful for projects that combine multiple of these. 44 | - **Maven central integration**. The plugin makes it easy to configure publishing to Maven Central with dedicated 45 | APIs to set it up and configure everything that is required. It also supports automatic releasing without requiring 46 | any interaction with the web interface. 47 | - **In memory GPG signing keys**. Easily sign artifacts on CI by simply setting the required environment variables, 48 | no extra setup required. 49 | - **Gradle property based config**. Easily configure the plugin with Gradle properties that will apply to all 50 | subprojects 51 | 52 | # License 53 | 54 | Copyright (C) 2018 Vanniktech - Niklas Baudy 55 | 56 | Licensed under the Apache License, Version 2.0 57 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/Pom.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import org.apache.maven.model.Dependency 4 | import org.apache.maven.model.DependencyManagement 5 | import org.apache.maven.model.Developer 6 | import org.apache.maven.model.License 7 | import org.apache.maven.model.Model 8 | import org.apache.maven.model.Scm 9 | 10 | data class PomDependency( 11 | val groupId: String, 12 | val artifactId: String, 13 | val version: String, 14 | val scope: String?, 15 | val optional: Boolean? = null, 16 | ) 17 | 18 | fun kotlinStdlibCommon(version: KotlinVersion) = kotlinStdlibCommon(version.value) 19 | 20 | fun kotlinStdlibCommon(version: String) = PomDependency("org.jetbrains.kotlin", "kotlin-stdlib", version, "compile") 21 | 22 | fun kotlinStdlibJdk(version: KotlinVersion) = kotlinStdlibJdk(version.value) 23 | 24 | fun kotlinStdlibJdk(version: String) = PomDependency("org.jetbrains.kotlin", "kotlin-stdlib", version, "compile") 25 | 26 | fun kotlinStdlibJs(version: KotlinVersion) = kotlinStdlibJs(version.value) 27 | 28 | fun kotlinStdlibJs(version: String) = PomDependency("org.jetbrains.kotlin", "kotlin-stdlib-js", version, "compile") 29 | 30 | fun kotlinDomApi(version: KotlinVersion) = kotlinDomApi(version.value) 31 | 32 | fun kotlinDomApi(version: String) = PomDependency("org.jetbrains.kotlin", "kotlin-dom-api-compat", version, "compile") 33 | 34 | fun createPom( 35 | groupId: String, 36 | artifactId: String, 37 | version: String, 38 | packaging: String?, 39 | dependencies: List, 40 | dependencyManagementDependencies: List, 41 | ): Model { 42 | val model = createMinimalPom(groupId, artifactId, version, packaging, dependencies, dependencyManagementDependencies) 43 | 44 | model.name = "Gradle Maven Publish Plugin Test Artifact" 45 | model.description = "Testing the Gradle Maven Publish Plugin" 46 | model.url = "https://github.com/vanniktech/gradle-maven-publish-plugin/" 47 | model.inceptionYear = "2018" 48 | model.addLicense( 49 | License().apply { 50 | name = "Apache-2.0" 51 | url = "https://www.apache.org/licenses/LICENSE-2.0.txt" 52 | distribution = "repo" 53 | }, 54 | ) 55 | model.addDeveloper( 56 | Developer().apply { 57 | id = "vanniktech" 58 | name = "Niklas Baudy" 59 | url = "https://github.com/vanniktech/" 60 | }, 61 | ) 62 | model.scm = Scm().apply { 63 | connection = "scm:git:git://github.com/vanniktech/gradle-maven-publish-plugin.git" 64 | developerConnection = "scm:git:ssh://git@github.com/vanniktech/gradle-maven-publish-plugin.git" 65 | url = "https://github.com/vanniktech/gradle-maven-publish-plugin/" 66 | } 67 | 68 | return model 69 | } 70 | 71 | fun createMinimalPom( 72 | groupId: String, 73 | artifactId: String, 74 | version: String, 75 | packaging: String?, 76 | dependencies: List, 77 | dependencyManagementDependencies: List, 78 | ): Model { 79 | val model = Model() 80 | model.modelVersion = "4.0.0" 81 | model.modelEncoding = "UTF-8" 82 | model.groupId = groupId 83 | model.artifactId = artifactId 84 | model.version = version 85 | if (packaging != null) { 86 | model.packaging = packaging 87 | } 88 | dependencies.distinct().forEach { 89 | model.addDependency( 90 | Dependency().apply { 91 | this.groupId = it.groupId 92 | this.artifactId = it.artifactId 93 | this.version = it.version 94 | this.scope = it.scope 95 | if (it.optional != null) { 96 | this.isOptional = it.optional 97 | } 98 | }, 99 | ) 100 | } 101 | 102 | if (dependencyManagementDependencies.isNotEmpty()) { 103 | model.dependencyManagement = DependencyManagement().apply { 104 | dependencyManagementDependencies.distinct().forEach { 105 | addDependency( 106 | Dependency().apply { 107 | this.groupId = it.groupId 108 | this.artifactId = it.artifactId 109 | this.version = it.version 110 | this.scope = it.scope 111 | if (it.optional != null) { 112 | this.isOptional = it.optional 113 | } 114 | }, 115 | ) 116 | } 117 | } 118 | } 119 | 120 | return model 121 | } 122 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/TestOptions.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.common.truth.TruthJUnit.assume 4 | import com.vanniktech.maven.publish.IntegrationTestBuildConfig as Versions 5 | import net.swiftzer.semver.SemVer 6 | import org.gradle.api.JavaVersion 7 | 8 | data class TestOptions( 9 | val config: Config, 10 | val signing: Signing, 11 | val gradleVersion: GradleVersion, 12 | ) { 13 | enum class Config { 14 | DSL, 15 | PROPERTIES, 16 | BASE, 17 | } 18 | 19 | enum class Signing { 20 | NO_SIGNING, 21 | GPG_KEY, 22 | IN_MEMORY_KEY, 23 | } 24 | } 25 | 26 | sealed class ComparableVersion( 27 | private val name: String, 28 | ) : Comparable { 29 | abstract val value: String 30 | val semVer: SemVer get() = SemVer.parse(value) 31 | 32 | override fun compareTo(other: ComparableVersion): Int = semVer.compareTo(other.semVer) 33 | 34 | override fun equals(other: Any?): Boolean = semVer == (other as? ComparableVersion)?.semVer 35 | 36 | override fun hashCode(): Int = semVer.hashCode() 37 | 38 | override fun toString(): String = "$name($semVer)" 39 | } 40 | 41 | class AgpVersion( 42 | override val value: String, 43 | val minJdkVersion: JavaVersion = JavaVersion.VERSION_17, 44 | val minGradleVersion: GradleVersion = GradleVersion.VERSIONS.min(), 45 | val firstUnsupportedJdkVersion: JavaVersion? = null, 46 | val firstUnsupportedGradleVersion: GradleVersion? = null, 47 | ) : ComparableVersion("AGP") { 48 | companion object { 49 | val VERSIONS = setOf( 50 | // minimum supported 51 | AgpVersion(Versions.ANDROID_GRADLE_MIN), 52 | // latest versions of each type 53 | AgpVersion(Versions.ANDROID_GRADLE_STABLE), 54 | AgpVersion(Versions.ANDROID_GRADLE_RC), 55 | AgpVersion(Versions.ANDROID_GRADLE_BETA, minGradleVersion = GradleVersion.GRADLE_9_1_0), 56 | AgpVersion(Versions.ANDROID_GRADLE_ALPHA, minGradleVersion = GradleVersion.GRADLE_9_1_0), 57 | ) 58 | 59 | // versions used for checks instead of test matrix 60 | val AGP_9_0_0 = AgpVersion("9.0.0-alpha01") 61 | } 62 | } 63 | 64 | class KotlinVersion( 65 | override val value: String, 66 | val minJdkVersion: JavaVersion = JavaVersion.VERSION_17, 67 | val minGradleVersion: GradleVersion = GradleVersion.VERSIONS.min(), 68 | val firstUnsupportedJdkVersion: JavaVersion? = null, 69 | val firstUnsupportedGradleVersion: GradleVersion? = null, 70 | ) : ComparableVersion("KGP") { 71 | companion object { 72 | val VERSIONS = setOf( 73 | // minimum supported 74 | KotlinVersion(Versions.KOTLIN_MIN), 75 | // latest versions of each type 76 | KotlinVersion(Versions.KOTLIN_STABLE), 77 | KotlinVersion(Versions.KOTLIN_RC), 78 | KotlinVersion(Versions.KOTLIN_BETA), 79 | KotlinVersion(Versions.KOTLIN_ALPHA), 80 | ) 81 | 82 | // versions used for checks instead of test matrix 83 | val KOTLIN_2_2_10 = KotlinVersion("2.2.10") 84 | } 85 | } 86 | 87 | class GradleVersion( 88 | override val value: String, 89 | val minJdkVersion: JavaVersion = JavaVersion.VERSION_17, 90 | val firstUnsupportedJdkVersion: JavaVersion? = null, 91 | ) : ComparableVersion("Gradle") { 92 | companion object { 93 | val VERSIONS = setOf( 94 | // minimum supported 95 | GradleVersion( 96 | value = Versions.GRADLE_MIN, 97 | firstUnsupportedJdkVersion = JavaVersion.VERSION_25, 98 | ), 99 | // latest versions of each type 100 | GradleVersion(Versions.GRADLE_STABLE), 101 | GradleVersion(Versions.GRADLE_RC), 102 | GradleVersion(Versions.GRADLE_BETA), 103 | GradleVersion(Versions.GRADLE_ALPHA), 104 | ) 105 | 106 | // versions used for checks instead of test matrix 107 | val GRADLE_9_1_0 = GradleVersion("9.1.0") 108 | } 109 | } 110 | 111 | class GradlePluginPublish( 112 | override val value: String, 113 | ) : ComparableVersion("PluginPublish") { 114 | companion object { 115 | val VERSIONS = setOf( 116 | // minimum supported 117 | GradlePluginPublish("1.0.0"), 118 | // latest versions of each type 119 | GradlePluginPublish(Versions.GRADLE_PUBLISH_STABLE), 120 | GradlePluginPublish(Versions.GRADLE_PUBLISH_RC), 121 | GradlePluginPublish(Versions.GRADLE_PUBLISH_BETA), 122 | GradlePluginPublish(Versions.GRADLE_PUBLISH_ALPHA), 123 | ) 124 | } 125 | } 126 | 127 | fun GradleVersion.assumeSupportedJdkVersion() { 128 | assume().that(JavaVersion.current()).isAtLeast(minJdkVersion) 129 | if (firstUnsupportedJdkVersion != null) { 130 | assume().that(JavaVersion.current()).isLessThan(firstUnsupportedJdkVersion) 131 | } 132 | } 133 | 134 | fun KotlinVersion.assumeSupportedJdkAndGradleVersion(gradleVersion: GradleVersion) { 135 | assume().that(JavaVersion.current()).isAtLeast(minJdkVersion) 136 | assume().that(gradleVersion).isAtLeast(minGradleVersion) 137 | if (firstUnsupportedJdkVersion != null) { 138 | assume().that(JavaVersion.current()).isLessThan(firstUnsupportedJdkVersion) 139 | } 140 | if (firstUnsupportedGradleVersion != null) { 141 | assume().that(gradleVersion).isLessThan(firstUnsupportedGradleVersion) 142 | } 143 | } 144 | 145 | fun AgpVersion.assumeSupportedJdkAndGradleVersion(gradleVersion: GradleVersion) { 146 | assume().that(JavaVersion.current()).isAtLeast(minJdkVersion) 147 | assume().that(gradleVersion).isAtLeast(minGradleVersion) 148 | if (firstUnsupportedJdkVersion != null) { 149 | assume().that(JavaVersion.current()).isLessThan(firstUnsupportedJdkVersion) 150 | } 151 | if (firstUnsupportedGradleVersion != null) { 152 | assume().that(gradleVersion).isLessThan(firstUnsupportedGradleVersion) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /central-portal/src/main/kotlin/com/vanniktech/maven/publish/portal/SonatypeCentralPortal.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.portal 2 | 3 | import com.squareup.moshi.Moshi 4 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 5 | import java.io.File 6 | import java.io.IOException 7 | import java.util.concurrent.TimeUnit 8 | import kotlin.time.Duration.Companion.seconds 9 | import kotlin.time.ExperimentalTime 10 | import kotlin.time.TimeSource 11 | import okhttp3.MediaType.Companion.toMediaType 12 | import okhttp3.MultipartBody 13 | import okhttp3.OkHttpClient 14 | import okhttp3.RequestBody.Companion.asRequestBody 15 | import org.slf4j.Logger 16 | import retrofit2.Retrofit 17 | import retrofit2.converter.moshi.MoshiConverterFactory 18 | import retrofit2.converter.scalars.ScalarsConverterFactory 19 | 20 | public class SonatypeCentralPortal( 21 | private val baseUrl: String, 22 | private val usertoken: String, 23 | userAgentName: String, 24 | userAgentVersion: String, 25 | okhttpTimeoutSeconds: Long, 26 | private val closeTimeoutSeconds: Long, 27 | private val pollIntervalMs: Long, 28 | private val logger: Logger, 29 | ) { 30 | private val service by lazy { 31 | val moshi = Moshi 32 | .Builder() 33 | .add(KotlinJsonAdapterFactory()) 34 | .build() 35 | 36 | val okHttpClient = OkHttpClient 37 | .Builder() 38 | .addInterceptor(SonatypeCentralPortalOkHttpInterceptor(usertoken, userAgentName, userAgentVersion)) 39 | .connectTimeout(okhttpTimeoutSeconds, TimeUnit.SECONDS) 40 | .readTimeout(okhttpTimeoutSeconds, TimeUnit.SECONDS) 41 | .writeTimeout(okhttpTimeoutSeconds, TimeUnit.SECONDS) 42 | .build() 43 | val retrofit = Retrofit 44 | .Builder() 45 | .client(okHttpClient) 46 | .baseUrl(baseUrl) 47 | .addConverterFactory(ScalarsConverterFactory.create()) 48 | .addConverterFactory(MoshiConverterFactory.create(moshi)) 49 | .build() 50 | 51 | retrofit.create(SonatypeCentralPortalService::class.java) 52 | } 53 | 54 | public fun deleteDeployment(deploymentId: String) { 55 | val deleteDeploymentResponse = service.deleteDeployment(deploymentId).execute() 56 | if (!deleteDeploymentResponse.isSuccessful) { 57 | throw IOException( 58 | "Failed to delete deploymentId $deploymentId code: ${deleteDeploymentResponse.code()} msg: ${ 59 | deleteDeploymentResponse.errorBody()?.string() 60 | }", 61 | ) 62 | } 63 | } 64 | 65 | public fun publishDeployment(deploymentId: String) { 66 | val publishDeploymentResponse = service.publishDeployment(deploymentId).execute() 67 | if (!publishDeploymentResponse.isSuccessful) { 68 | throw IOException( 69 | "Failed to delete deploymentId $deploymentId code: ${publishDeploymentResponse.code()} msg: ${ 70 | publishDeploymentResponse.errorBody()?.string() 71 | }", 72 | ) 73 | } 74 | } 75 | 76 | public fun upload(name: String, publishingType: PublishingType, file: File): String { 77 | val uploadFile = file.asRequestBody("application/octet-stream".toMediaType()) 78 | val multipart = MultipartBody.Part.createFormData("bundle", file.name, uploadFile) 79 | val uploadResponse = service.uploadBundle(name, publishingType, multipart).execute() 80 | if (uploadResponse.isSuccessful) { 81 | return checkNotNull(uploadResponse.body()) 82 | } else { 83 | throw IOException("Upload failed: ${uploadResponse.errorBody()?.string()}") 84 | } 85 | } 86 | 87 | /** 88 | * Validates the deployment by polling its status until it reaches `PUBLISHED` or `FAILED`. 89 | * 90 | * @param deploymentId The ID of the deployment to validate 91 | * @param logger An SLF4J logger instance for logging deployment status updates 92 | * @throws IOException if the deployment fails validation or an API error occurs 93 | */ 94 | @OptIn(ExperimentalTime::class) 95 | public fun validateDeployment(deploymentId: String, waitForPublishing: Boolean) { 96 | val startMark = TimeSource.Monotonic.markNow() 97 | val timeout = closeTimeoutSeconds.seconds 98 | var lastState: DeploymentState? = null 99 | 100 | logger.warn("Validating deployment $deploymentId...") 101 | 102 | while (startMark.elapsedNow() < timeout) { 103 | val statusResponse = service.checkDeploymentStatus(deploymentId).execute() 104 | 105 | if (!statusResponse.isSuccessful) { 106 | throw IOException( 107 | "Failed to check deployment status for $deploymentId code: ${statusResponse.code()} msg: ${ 108 | statusResponse.errorBody()?.string() 109 | }", 110 | ) 111 | } 112 | 113 | val status = checkNotNull(statusResponse.body()) { 114 | "Status response body is null for deployment $deploymentId" 115 | } 116 | 117 | if (status.deploymentState != lastState) { 118 | lastState = status.deploymentState 119 | 120 | when (status.deploymentState) { 121 | DeploymentState.PENDING -> logger.warn("Deployment is pending validation") 122 | DeploymentState.VALIDATING -> logger.warn("Deployment is being validated") 123 | DeploymentState.VALIDATED -> { 124 | logger.warn("Deployment has been validated successfully") 125 | if (!waitForPublishing) { 126 | return 127 | } 128 | } 129 | DeploymentState.PUBLISHING -> { 130 | logger.warn("Deployment is being published to Maven Central") 131 | if (!waitForPublishing) { 132 | return 133 | } 134 | } 135 | DeploymentState.PUBLISHED -> { 136 | logger.warn("Deployment has been published to Maven Central") 137 | return 138 | } 139 | 140 | DeploymentState.FAILED -> { 141 | val errorMessages = 142 | status.errors?.entries?.joinToString("\n") { (publication, errors) -> 143 | buildString { 144 | appendLine("Publication $publication:") 145 | errors.forEach { error -> 146 | appendLine("* $error") 147 | } 148 | } 149 | } ?: "No error details available" 150 | 151 | throw IOException( 152 | "Deployment $deploymentId failed validation:\n$errorMessages", 153 | ) 154 | } 155 | } 156 | } 157 | 158 | Thread.sleep(pollIntervalMs) 159 | } 160 | 161 | throw IOException( 162 | "Deployment validation timed out after ${closeTimeoutSeconds}s. " + 163 | "Last known state: ${lastState ?: "UNKNOWN"}", 164 | ) 165 | } 166 | 167 | public enum class PublishingType { 168 | AUTOMATIC, 169 | USER_MANAGED, 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/AndroidPluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.common.truth.TruthJUnit.assume 4 | import com.google.testing.junit.testparameterinjector.junit5.TestParameter 5 | import com.google.testing.junit.testparameterinjector.junit5.TestParameterInjectorTest 6 | import com.vanniktech.maven.publish.AgpVersion.Companion.AGP_9_0_0 7 | import com.vanniktech.maven.publish.ProjectResultSubject.Companion.assertThat 8 | import org.junit.jupiter.api.condition.DisabledOnJre 9 | import org.junit.jupiter.api.condition.JRE 10 | 11 | class AndroidPluginTest : BasePluginTest() { 12 | @TestParameterInjectorTest 13 | fun androidFusedLibraryProject( 14 | @TestParameter(valuesProvider = AgpVersionProvider::class) agpVersion: AgpVersion, 15 | ) { 16 | agpVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 17 | 18 | val project = androidFusedLibraryProjectSpec(agpVersion) 19 | // TODO: signing plugin currently unsupported 20 | val result = project.run(fixtures, testProjectDir, testOptions.copy(signing = TestOptions.Signing.NO_SIGNING)) 21 | 22 | assertThat(result).outcome().succeeded() 23 | assertThat(result).artifact("aar").exists() 24 | // TODO: signing plugin currently unsupported 25 | // assertThat(result).artifact("aar").isSigned() 26 | assertThat(result).pom().exists() 27 | // TODO: signing plugin currently unsupported 28 | // assertThat(result).pom().isSigned() 29 | assertThat(result).pom().matchesExpectedPom("aar") 30 | assertThat(result).module().exists() 31 | // TODO: signing plugin currently unsupported 32 | // assertThat(result).module().isSigned() 33 | assertThat(result).sourcesJar().exists() 34 | // TODO: signing plugin currently unsupported 35 | // assertThat(result).sourcesJar().isSigned() 36 | // TODO: actual sources jar currently unsupported 37 | // assertThat(result).sourcesJar().containsAllSourceFiles() 38 | assertThat(result).javadocJar().exists() 39 | // TODO: signing plugin currently unsupported 40 | // assertThat(result).javadocJar().isSigned() 41 | } 42 | 43 | @TestParameterInjectorTest 44 | fun androidLibraryKotlinProject( 45 | @TestParameter(valuesProvider = AgpVersionProvider::class) agpVersion: AgpVersion, 46 | @TestParameter(valuesProvider = KotlinVersionProvider::class) kotlinVersion: KotlinVersion, 47 | ) { 48 | agpVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 49 | kotlinVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 50 | 51 | val project = androidLibraryKotlinProjectSpec(agpVersion, kotlinVersion) 52 | val result = project.run(fixtures, testProjectDir, testOptions) 53 | 54 | assertThat(result).outcome().succeeded() 55 | assertThat(result).artifact("aar").exists() 56 | assertThat(result).artifact("aar").isSigned() 57 | assertThat(result).pom().exists() 58 | assertThat(result).pom().isSigned() 59 | if (agpVersion >= AGP_9_0_0) { 60 | assertThat(result).pom().matchesExpectedPom("aar", kotlinStdlibJdk("2.2.10")) 61 | } else { 62 | assertThat(result).pom().matchesExpectedPom("aar", kotlinStdlibJdk(kotlinVersion)) 63 | } 64 | assertThat(result).module().exists() 65 | assertThat(result).module().isSigned() 66 | assertThat(result).sourcesJar().exists() 67 | assertThat(result).sourcesJar().isSigned() 68 | assertThat(result).sourcesJar().containsAllSourceFiles() 69 | assertThat(result).javadocJar().exists() 70 | assertThat(result).javadocJar().isSigned() 71 | } 72 | 73 | @TestParameterInjectorTest 74 | fun androidLibraryProject( 75 | @TestParameter(valuesProvider = AgpVersionProvider::class) agpVersion: AgpVersion, 76 | ) { 77 | agpVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 78 | 79 | val project = androidLibraryProjectSpec(agpVersion) 80 | val result = project.run(fixtures, testProjectDir, testOptions) 81 | 82 | assertThat(result).outcome().succeeded() 83 | assertThat(result).artifact("aar").exists() 84 | assertThat(result).artifact("aar").isSigned() 85 | assertThat(result).pom().exists() 86 | assertThat(result).pom().isSigned() 87 | if (agpVersion >= AGP_9_0_0) { 88 | assertThat(result).pom().matchesExpectedPom("aar", kotlinStdlibJdk("2.2.10")) 89 | } else { 90 | assertThat(result).pom().matchesExpectedPom("aar") 91 | } 92 | assertThat(result).module().exists() 93 | assertThat(result).module().isSigned() 94 | assertThat(result).sourcesJar().exists() 95 | assertThat(result).sourcesJar().isSigned() 96 | assertThat(result).sourcesJar().containsAllSourceFiles() 97 | assertThat(result).javadocJar().exists() 98 | assertThat(result).javadocJar().isSigned() 99 | } 100 | 101 | @DisabledOnJre( 102 | value = [JRE.JAVA_25], 103 | disabledReason = "Dokka 1.x does not support Java 25+.", 104 | ) 105 | @TestParameterInjectorTest 106 | fun androidMultiVariantLibraryProject( 107 | @TestParameter(valuesProvider = AgpVersionProvider::class) agpVersion: AgpVersion, 108 | ) { 109 | // regular plugin does not have a way to enable multi variant config 110 | assume().that(config).isEqualTo(TestOptions.Config.BASE) 111 | agpVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 112 | 113 | val project = androidLibraryProjectSpec(agpVersion).copy( 114 | basePluginConfig = "configure(new AndroidMultiVariantLibrary(true, true))", 115 | ) 116 | val result = project.run(fixtures, testProjectDir, testOptions) 117 | 118 | assertThat(result).outcome().succeeded() 119 | assertThat(result).pom().exists() 120 | assertThat(result).pom().isSigned() 121 | if (agpVersion >= AGP_9_0_0) { 122 | assertThat(result).pom().matchesExpectedPom("pom", kotlinStdlibJdk("2.2.10")) 123 | } else { 124 | assertThat(result).pom().matchesExpectedPom("pom") 125 | } 126 | assertThat(result).module().exists() 127 | assertThat(result).module().isSigned() 128 | 129 | assertThat(result).artifact("debug", "aar").exists() 130 | assertThat(result).artifact("debug", "aar").isSigned() 131 | assertThat(result).sourcesJar("debug").exists() 132 | assertThat(result).sourcesJar("debug").isSigned() 133 | assertThat(result).sourcesJar("debug").containsAllSourceFiles() 134 | assertThat(result).javadocJar("debug").exists() 135 | assertThat(result).javadocJar("debug").isSigned() 136 | 137 | assertThat(result).artifact("release", "aar").exists() 138 | assertThat(result).artifact("release", "aar").isSigned() 139 | assertThat(result).sourcesJar("release").exists() 140 | assertThat(result).sourcesJar("release").isSigned() 141 | assertThat(result).sourcesJar("release").containsAllSourceFiles() 142 | assertThat(result).javadocJar("release").exists() 143 | assertThat(result).javadocJar("release").isSigned() 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME 2 | 3 | plugins { 4 | id("shared") 5 | id("java-gradle-plugin") 6 | alias(libs.plugins.buildconfig) 7 | alias(libs.plugins.android.lint) 8 | } 9 | 10 | dokka { 11 | dokkaPublications.html { 12 | outputDirectory = rootDir.resolve("docs/api") 13 | } 14 | } 15 | 16 | gradlePlugin { 17 | plugins { 18 | create("mavenPublishPlugin") { 19 | id = "com.vanniktech.maven.publish" 20 | implementationClass = "com.vanniktech.maven.publish.MavenPublishPlugin" 21 | displayName = "Gradle Maven Publish Plugin" 22 | description = "Gradle plugin that configures publish tasks to automatically upload all of your Java, Kotlin, " + 23 | "Gradle, or Android libraries to any Maven instance." 24 | } 25 | create("mavenPublishBasePlugin") { 26 | id = "com.vanniktech.maven.publish.base" 27 | implementationClass = "com.vanniktech.maven.publish.MavenPublishBasePlugin" 28 | displayName = "Gradle Maven Publish Base Plugin" 29 | description = "Gradle plugin that configures publish tasks to automatically upload all of your Java, Kotlin, " + 30 | "Gradle, or Android libraries to any Maven instance." 31 | } 32 | } 33 | } 34 | 35 | val integrationTestSourceSet = sourceSets.create("integrationTest") { 36 | compileClasspath += sourceSets["main"].output 37 | compileClasspath += configurations.testRuntimeClasspath.get() 38 | runtimeClasspath += output + compileClasspath 39 | } 40 | val integrationTestImplementation by configurations.getting { 41 | extendsFrom(configurations.testImplementation.get()) 42 | } 43 | 44 | configurations.named(API_ELEMENTS_CONFIGURATION_NAME) { 45 | attributes.attribute( 46 | // TODO: https://github.com/gradle/gradle/issues/24608 47 | GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, 48 | objects.named(libs.versions.minGradle.get()), 49 | ) 50 | } 51 | 52 | buildConfig { 53 | packageName("com.vanniktech.maven.publish") 54 | generateAtSync = true 55 | 56 | sourceSets.configureEach { 57 | buildConfigField("VERSION_NAME", providers.gradleProperty("VERSION_NAME")) 58 | buildConfigField("ANDROID_GRADLE_MIN", libs.versions.minAgp) 59 | buildConfigField("KOTLIN_MIN", libs.versions.minKgp) 60 | } 61 | 62 | sourceSets.named("main") { 63 | buildConfigField("PLUGIN_NAME", "com.vanniktech.maven.publish") 64 | } 65 | 66 | sourceSets.named(integrationTestSourceSet.name) { 67 | buildConfigField("TEST_CONFIG_METHOD", providers.gradleProperty("testConfigMethod").orElse("")) 68 | buildConfigField("Boolean", "QUICK_TEST", providers.gradleProperty("quickTest").orElse("false")) 69 | 70 | buildConfigField("GRADLE_MIN", libs.versions.minGradle) 71 | buildConfigField("GRADLE_ALPHA", alpha.versions.gradle.asProvider()) 72 | buildConfigField("GRADLE_BETA", beta.versions.gradle.asProvider()) 73 | buildConfigField("GRADLE_RC", rc.versions.gradle.asProvider()) 74 | buildConfigField("GRADLE_STABLE", libs.versions.gradle.asProvider()) 75 | buildConfigField("ANDROID_GRADLE_ALPHA", alpha.versions.android.gradle) 76 | buildConfigField("ANDROID_GRADLE_BETA", beta.versions.android.gradle) 77 | buildConfigField("ANDROID_GRADLE_RC", rc.versions.android.gradle) 78 | buildConfigField("ANDROID_GRADLE_STABLE", libs.versions.android.gradle) 79 | buildConfigField("KOTLIN_ALPHA", alpha.versions.kotlin) 80 | buildConfigField("KOTLIN_BETA", beta.versions.kotlin) 81 | buildConfigField("KOTLIN_RC", rc.versions.kotlin) 82 | buildConfigField("KOTLIN_STABLE", libs.versions.kotlin) 83 | buildConfigField("GRADLE_PUBLISH_ALPHA", alpha.versions.gradle.plugin.publish) 84 | buildConfigField("GRADLE_PUBLISH_BETA", beta.versions.gradle.plugin.publish) 85 | buildConfigField("GRADLE_PUBLISH_RC", rc.versions.gradle.plugin.publish) 86 | buildConfigField("GRADLE_PUBLISH_STABLE", libs.versions.gradle.plugin.publish) 87 | buildConfigField("DOKKA_STABLE", libs.versions.dokka) 88 | } 89 | } 90 | 91 | lint { 92 | baseline = file("lint-baseline.xml") 93 | ignoreTestSources = true 94 | warningsAsErrors = true 95 | disable += "NewerVersionAvailable" 96 | disable += "GradleDependency" 97 | disable += "AndroidGradlePluginVersion" 98 | } 99 | 100 | dependencies { 101 | compileOnly(libs.dokka) 102 | compileOnly(libs.kotlin.plugin) 103 | compileOnly(libs.android.pluginApi) 104 | 105 | implementation(projects.centralPortal) 106 | 107 | testImplementation(libs.junit.jupiter) 108 | testImplementation(libs.junit.engine) 109 | testImplementation(libs.junit.launcher) 110 | testImplementation(libs.truth) 111 | integrationTestImplementation(libs.testParameterInjector) 112 | integrationTestImplementation(libs.truth.testKit) 113 | integrationTestImplementation(libs.maven.model) 114 | integrationTestImplementation(libs.semver) 115 | 116 | lintChecks(libs.androidx.gradlePluginLints) 117 | } 118 | 119 | tasks.validatePlugins { 120 | // TODO: https://github.com/gradle/gradle/issues/22600 121 | enableStricterValidation = true 122 | } 123 | 124 | val integrationTest by tasks.registering(Test::class) { 125 | dependsOn( 126 | tasks.publishToMavenLocal, 127 | project(projects.centralPortal.path).tasks.publishToMavenLocal, 128 | ) 129 | mustRunAfter(tasks.test) 130 | 131 | description = "Runs the integration tests." 132 | group = "verification" 133 | 134 | testClassesDirs = integrationTestSourceSet.output.classesDirs 135 | classpath = integrationTestSourceSet.runtimeClasspath 136 | 137 | val testJavaVersion = providers.gradleProperty("testJavaVersion").orNull 138 | if (testJavaVersion != null && testJavaVersion != "latest") { 139 | javaLauncher = javaToolchains.launcherFor { 140 | languageVersion = JavaLanguageVersion.of(testJavaVersion) 141 | } 142 | } 143 | 144 | useJUnitPlatform() 145 | testLogging.showStandardStreams = true 146 | maxHeapSize = "2g" 147 | maxParallelForks = Runtime.getRuntime().availableProcessors() 148 | jvmArgs( 149 | "--add-opens", 150 | "java.base/java.lang.invoke=ALL-UNNAMED", 151 | "--add-opens", 152 | "java.base/java.net=ALL-UNNAMED", 153 | "--add-opens", 154 | "java.base/java.util=ALL-UNNAMED", 155 | ) 156 | 157 | beforeTest( 158 | closureOf { 159 | logger.lifecycle("Running test: ${this.className} ${this.displayName}") 160 | }, 161 | ) 162 | 163 | develocity { 164 | testRetry { 165 | if (providers.environmentVariable("CI").isPresent) { 166 | maxRetries = 2 167 | maxFailures = 10 168 | } 169 | } 170 | } 171 | } 172 | 173 | tasks.test { 174 | // The generated build config fails the test task. 175 | failOnNoDiscoveredTests = false 176 | } 177 | 178 | tasks.check { 179 | dependsOn(integrationTest) 180 | } 181 | 182 | tasks.clean { 183 | delete += listOf( 184 | dokka.dokkaPublications.html.map { it.outputDirectory }, 185 | // Generated by MkDocs. 186 | rootDir.resolve("site"), 187 | ) 188 | } 189 | -------------------------------------------------------------------------------- /docs/other.md: -------------------------------------------------------------------------------- 1 | # Other Maven Repositories 2 | 3 | This describes how to configure the plugin to publish to arbitrary Maven repositories. For publishing open source 4 | projects see [Maven Central](central.md). 5 | 6 | ## Applying the plugin 7 | 8 | Add the plugin to any Gradle project that should be published 9 | 10 | === "build.gradle" 11 | 12 | ```groovy 13 | plugins { 14 | id "com.vanniktech.maven.publish" version "" 15 | } 16 | ``` 17 | 18 | === "build.gradle.kts" 19 | 20 | ```kotlin 21 | plugins { 22 | id("com.vanniktech.maven.publish") version "" 23 | } 24 | ``` 25 | 26 | ## Configuring what to publish 27 | 28 | By default, the plugin will automatically detect other applied plugins like the 29 | Android Gradle plugin or Kotlin Gradle plugin and set up what to publish automatically. 30 | This automatic configuration includes publishing a sources and a javadoc jar. The 31 | javadoc jar content is either created from the default javadoc task or from Dokka if 32 | applied. 33 | 34 | To modify these defaults it is possible to call `configure` in the DSL. For 35 | more check out the [what to publish page](what.md) which contains a detailed 36 | description of available options for each project type. 37 | 38 | === "build.gradle" 39 | 40 | ```groovy 41 | mavenPublishing { 42 | configure(...) 43 | } 44 | ``` 45 | 46 | === "build.gradle.kts" 47 | 48 | ```kotlin 49 | mavenPublishing { 50 | configure(...) 51 | } 52 | ``` 53 | 54 | ## Configuring the repository 55 | 56 | A new repository to publish to can be added like this 57 | 58 | === "build.gradle" 59 | 60 | ```groovy 61 | publishing { 62 | repositories { 63 | maven { 64 | name = "myRepo" 65 | url = layout.buildDirectory.dir('repo') 66 | // or 67 | url = "http://my.org/repo" 68 | // or when a separate snapshot repository is required 69 | url = version.toString().endsWith("SNAPSHOT") ? "http://my.org/repos/snapshots" : "http://my.org/repos/releases" 70 | } 71 | 72 | // more repositories can go here 73 | } 74 | } 75 | ``` 76 | 77 | === "build.gradle.kts" 78 | 79 | ```kotlin 80 | publishing { 81 | repositories { 82 | maven { 83 | name = "myRepo" 84 | url = uri(layout.buildDirectory.dir("repo")) 85 | // or 86 | url = uri("http://my.org/repo") 87 | // or when a separate snapshot repository is required 88 | url = uri(if (version.toString().endsWith("SNAPSHOT")) "http://my.org/repos/snapshots" else "http://my.org/repos/releases") 89 | } 90 | 91 | // more repositories can go here 92 | } 93 | } 94 | ``` 95 | 96 | ### Github Packages example 97 | 98 | === "build.gradle" 99 | 100 | ```groovy 101 | publishing { 102 | repositories { 103 | maven { 104 | name = "githubPackages" 105 | url = "https://maven.pkg.github.com/your-org/your-project" 106 | // username and password (a personal Github access token) should be specified as 107 | // `githubPackagesUsername` and `githubPackagesPassword` Gradle properties or alternatively 108 | // as `ORG_GRADLE_PROJECT_githubPackagesUsername` and `ORG_GRADLE_PROJECT_githubPackagesPassword` 109 | // environment variables 110 | credentials(PasswordCredentials) 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | === "build.gradle.kts" 117 | 118 | ```kotlin 119 | publishing { 120 | repositories { 121 | maven { 122 | name = "githubPackages" 123 | url = uri("https://maven.pkg.github.com/your-org/your-project") 124 | // username and password (a personal Github access token) should be specified as 125 | // `githubPackagesUsername` and `githubPackagesPassword` Gradle properties or alternatively 126 | // as `ORG_GRADLE_PROJECT_githubPackagesUsername` and `ORG_GRADLE_PROJECT_githubPackagesPassword` 127 | // environment variables 128 | credentials(PasswordCredentials::class) 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | 135 | ## Configuring the POM 136 | 137 | The pom is published alongside the project and contains the project coordinates 138 | as well as some general information about the project like an url and the used 139 | license. 140 | 141 | This configuration also determines the coordinates (`group:artifactId:version`) used to consume the library. 142 | 143 | === "build.gradle" 144 | 145 | ```groovy 146 | mavenPublishing { 147 | coordinates("com.example.mylibrary", "library-name", "1.0.3-SNAPSHOT") 148 | 149 | // the following is optional 150 | 151 | pom { 152 | name = "My Library" 153 | description = "A description of what my library does." 154 | inceptionYear = "2020" 155 | url = "https://github.com/username/mylibrary/" 156 | licenses { 157 | license { 158 | name = "The Apache License, Version 2.0" 159 | url = "http://www.apache.org/licenses/LICENSE-2.0.txt" 160 | distribution = "http://www.apache.org/licenses/LICENSE-2.0.txt" 161 | } 162 | } 163 | developers { 164 | developer { 165 | id = "username" 166 | name = "User Name" 167 | url = "https://github.com/username/" 168 | } 169 | } 170 | scm { 171 | url = "https://github.com/username/mylibrary/" 172 | connection = "scm:git:git://github.com/username/mylibrary.git" 173 | developerConnection = "scm:git:ssh://git@github.com/username/mylibrary.git" 174 | } 175 | } 176 | } 177 | ``` 178 | 179 | === "build.gradle.kts" 180 | 181 | ```kotlin 182 | mavenPublishing { 183 | coordinates("com.example.mylibrary", "library-name", "1.0.3-SNAPSHOT") 184 | 185 | // the following is optional 186 | 187 | pom { 188 | name.set("My Library") 189 | description.set("A description of what my library does.") 190 | inceptionYear.set("2020") 191 | url.set("https://github.com/username/mylibrary/") 192 | licenses { 193 | license { 194 | name.set("The Apache License, Version 2.0") 195 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 196 | distribution.set("http://www.apache.org/licenses/LICENSE-2.0.txt") 197 | } 198 | } 199 | developers { 200 | developer { 201 | id.set("username") 202 | name.set("User Name") 203 | url.set("https://github.com/username/") 204 | } 205 | } 206 | scm { 207 | url.set("https://github.com/username/mylibrary/") 208 | connection.set("scm:git:git://github.com/username/mylibrary.git") 209 | developerConnection.set("scm:git:ssh://git@github.com/username/mylibrary.git") 210 | } 211 | } 212 | } 213 | ``` 214 | 215 | === "gradle.properties" 216 | 217 | ```properties 218 | GROUP=com.test.mylibrary 219 | POM_ARTIFACT_ID=mylibrary-runtime 220 | VERSION_NAME=3.0.5 221 | 222 | # the following is optional 223 | 224 | POM_NAME=My Library 225 | POM_DESCRIPTION=A description of what my library does. 226 | POM_INCEPTION_YEAR=2020 227 | POM_URL=https://github.com/username/mylibrary/ 228 | 229 | POM_LICENSE_NAME=Apache-2.0 230 | POM_LICENSE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt 231 | POM_LICENSE_DIST=repo 232 | 233 | POM_SCM_URL=https://github.com/username/mylibrary/ 234 | POM_SCM_CONNECTION=scm:git:git://github.com/username/mylibrary.git 235 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/username/mylibrary.git 236 | 237 | POM_DEVELOPER_ID=username 238 | POM_DEVELOPER_NAME=User Name 239 | POM_DEVELOPER_URL=https://github.com/username/ 240 | ``` 241 | 242 | ## Publishing 243 | 244 | To publish the project run 245 | ``` 246 | ./gradlew publishAllPublicationsToRepository 247 | ``` 248 | 249 | `` refers to the name used in the [configuring the repository section](#configuring-the-repository) 250 | and would be `MyRepo` for the example there. 251 | -------------------------------------------------------------------------------- /plugin/src/main/kotlin/com/vanniktech/maven/publish/central/MavenCentralBuildService.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish.central 2 | 3 | import com.vanniktech.maven.publish.BuildConfig 4 | import com.vanniktech.maven.publish.DeploymentValidation 5 | import com.vanniktech.maven.publish.closeTimeout 6 | import com.vanniktech.maven.publish.connectTimeout 7 | import com.vanniktech.maven.publish.pollIntervalSeconds 8 | import com.vanniktech.maven.publish.portal.SonatypeCentralPortal 9 | import com.vanniktech.maven.publish.portal.SonatypeCentralPortal.PublishingType.AUTOMATIC 10 | import com.vanniktech.maven.publish.portal.SonatypeCentralPortal.PublishingType.USER_MANAGED 11 | import java.io.File 12 | import java.io.IOException 13 | import java.util.Base64 14 | import java.util.zip.ZipEntry 15 | import java.util.zip.ZipOutputStream 16 | import org.gradle.api.Project 17 | import org.gradle.api.file.Directory 18 | import org.gradle.api.file.DirectoryProperty 19 | import org.gradle.api.logging.Logging 20 | import org.gradle.api.provider.Property 21 | import org.gradle.api.provider.Provider 22 | import org.gradle.api.services.BuildService 23 | import org.gradle.api.services.BuildServiceParameters 24 | import org.gradle.build.event.BuildEventsListenerRegistry 25 | import org.gradle.tooling.events.FailureResult 26 | import org.gradle.tooling.events.FinishEvent 27 | import org.gradle.tooling.events.OperationCompletionListener 28 | 29 | internal abstract class MavenCentralBuildService : 30 | BuildService, 31 | AutoCloseable, 32 | OperationCompletionListener { 33 | internal interface Params : BuildServiceParameters { 34 | val repositoryUsername: Property 35 | val repositoryPassword: Property 36 | val okhttpTimeoutSeconds: Property 37 | val closeTimeoutSeconds: Property 38 | val pollIntervalMillis: Property 39 | val rootBuildDirectory: DirectoryProperty 40 | } 41 | 42 | private val logger = Logging.getLogger(MavenCentralBuildService::class.java) 43 | 44 | private val centralPortal by lazy { 45 | SonatypeCentralPortal( 46 | baseUrl = "https://central.sonatype.com", 47 | usertoken = Base64 48 | .getEncoder() 49 | .encode( 50 | "${parameters.repositoryUsername.get()}:${parameters.repositoryPassword.get()}".toByteArray(), 51 | ).toString(Charsets.UTF_8), 52 | userAgentName = BuildConfig.PLUGIN_NAME, 53 | userAgentVersion = BuildConfig.VERSION_NAME, 54 | okhttpTimeoutSeconds = parameters.okhttpTimeoutSeconds.get(), 55 | closeTimeoutSeconds = parameters.closeTimeoutSeconds.get(), 56 | pollIntervalMs = parameters.pollIntervalMillis.get(), 57 | logger = logger, 58 | ) 59 | } 60 | 61 | private var deploymentId: String? = null 62 | 63 | private val endOfBuildActions = mutableSetOf() 64 | 65 | private val projectsToPublish = mutableSetOf() 66 | 67 | private var buildIsSuccess: Boolean = true 68 | 69 | /** 70 | * Is only allowed to be called from task actions. 71 | */ 72 | fun registerProject(group: String, artifactId: String, version: String, localRepository: File) { 73 | if (version.endsWith("-SNAPSHOT")) { 74 | return 75 | } 76 | 77 | val coordinates = MavenCentralCoordinates(group, artifactId, version) 78 | val project = MavenCentralProject(coordinates, localRepository) 79 | projectsToPublish.add(project) 80 | 81 | endOfBuildActions += EndOfBuildAction.Upload 82 | endOfBuildActions += EndOfBuildAction.Drop(runAfterFailure = true) 83 | } 84 | 85 | /** 86 | * Is only allowed to be called from task actions. 87 | */ 88 | fun enableAutomaticPublishing(validateDeployment: DeploymentValidation) { 89 | endOfBuildActions += EndOfBuildAction.Publish 90 | when (validateDeployment) { 91 | DeploymentValidation.NONE -> {} 92 | DeploymentValidation.VALIDATE -> endOfBuildActions += EndOfBuildAction.Validate(waitForPublishing = false) 93 | DeploymentValidation.PUBLISH -> endOfBuildActions += EndOfBuildAction.Validate(waitForPublishing = true) 94 | } 95 | } 96 | 97 | /** 98 | * Is only allowed to be called from task actions. Tasks calling this must run after tasks 99 | * that call [registerProject]. 100 | */ 101 | fun dropDeployment(deploymentId: String) { 102 | this.deploymentId = deploymentId 103 | 104 | endOfBuildActions += EndOfBuildAction.Drop(runAfterFailure = false) 105 | } 106 | 107 | override fun onFinish(event: FinishEvent) { 108 | if (event.result is FailureResult) { 109 | buildIsSuccess = false 110 | } 111 | } 112 | 113 | override fun close() { 114 | if (buildIsSuccess) { 115 | runEndOfBuildActions(endOfBuildActions.filter { !it.runAfterFailure }) 116 | } else { 117 | // surround with try catch since failing again on cleanup actions causes confusion 118 | try { 119 | runEndOfBuildActions(endOfBuildActions.filter { it.runAfterFailure }) 120 | } catch (_: IOException) { 121 | } 122 | } 123 | } 124 | 125 | private fun runEndOfBuildActions(actions: List) { 126 | if (actions.contains(EndOfBuildAction.Upload)) { 127 | val coordinates = projectsToPublish.map { it.coordinates }.toSet() 128 | val deploymentName = if (coordinates.size == 1) { 129 | val coordinate = coordinates.single() 130 | "${coordinate.group}-${coordinate.artifactId}-${coordinate.version}" 131 | } else if (coordinates.distinctBy { it.group + it.version }.size == 1) { 132 | val coordinate = coordinates.first() 133 | "${coordinate.group}-${coordinate.version}" 134 | } else { 135 | val coordinate = coordinates.first() 136 | "${coordinate.group}-${System.currentTimeMillis()}" 137 | } 138 | 139 | val publishingType = if (actions.contains(EndOfBuildAction.Publish)) { 140 | AUTOMATIC 141 | } else { 142 | USER_MANAGED 143 | } 144 | 145 | val zipFile = parameters.rootBuildDirectory 146 | .file("publish/$deploymentName-${System.currentTimeMillis()}.zip") 147 | .get() 148 | .asFile 149 | zipFile.parentFile.mkdirs() 150 | check(zipFile.createNewFile()) { "$zipFile already exists" } 151 | val out = ZipOutputStream(zipFile.outputStream()) 152 | projectsToPublish.forEach { project -> 153 | project.localRepository 154 | .walkTopDown() 155 | .filter { it.isFile && !it.name.contains("maven-metadata") } 156 | .forEach { 157 | val entry = ZipEntry(it.toRelativeString(project.localRepository)) 158 | out.putNextEntry(entry) 159 | out.write(it.readBytes()) 160 | out.closeEntry() 161 | } 162 | } 163 | out.close() 164 | 165 | val deploymentId = centralPortal.upload(deploymentName, publishingType, zipFile) 166 | this.deploymentId = deploymentId 167 | 168 | val validate = actions.find { it is EndOfBuildAction.Validate } as EndOfBuildAction.Validate? 169 | if (validate != null) { 170 | centralPortal.validateDeployment(deploymentId, validate.waitForPublishing) 171 | } else { 172 | logger.lifecycle("Skipping deployment validation!") 173 | } 174 | } 175 | 176 | val dropAction = actions.filterIsInstance().singleOrNull() 177 | if (dropAction != null) { 178 | val id = deploymentId 179 | if (id != null) { 180 | centralPortal.deleteDeployment(id) 181 | } 182 | } 183 | } 184 | 185 | companion object { 186 | private const val NAME = "maven-central-build-service" 187 | 188 | fun Project.registerMavenCentralBuildService( 189 | repositoryUsername: Provider, 190 | repositoryPassword: Provider, 191 | rootBuildDirectory: Directory, 192 | buildEventsListenerRegistry: BuildEventsListenerRegistry, 193 | ): Provider { 194 | val service = gradle.sharedServices.registerIfAbsent(NAME, MavenCentralBuildService::class.java) { 195 | it.maxParallelUsages.set(1) 196 | it.parameters.repositoryUsername.set(repositoryUsername) 197 | it.parameters.repositoryPassword.set(repositoryPassword) 198 | it.parameters.okhttpTimeoutSeconds.set(project.connectTimeout().get().inWholeSeconds) 199 | it.parameters.closeTimeoutSeconds.set(project.closeTimeout().get().inWholeSeconds) 200 | it.parameters.pollIntervalMillis.set(project.pollIntervalSeconds().get().inWholeMilliseconds) 201 | it.parameters.rootBuildDirectory.set(rootBuildDirectory) 202 | } 203 | buildEventsListenerRegistry.onTaskCompletion(service) 204 | return service 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | 118 | 119 | # Determine the Java command to use to start the JVM. 120 | if [ -n "$JAVA_HOME" ] ; then 121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 122 | # IBM's JDK on AIX uses strange locations for the executables 123 | JAVACMD=$JAVA_HOME/jre/sh/java 124 | else 125 | JAVACMD=$JAVA_HOME/bin/java 126 | fi 127 | if [ ! -x "$JAVACMD" ] ; then 128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 129 | 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | if ! command -v java >/dev/null 2>&1 136 | then 137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 138 | 139 | Please set the JAVA_HOME variable in your environment to match the 140 | location of your Java installation." 141 | fi 142 | fi 143 | 144 | # Increase the maximum file descriptors if we can. 145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 146 | case $MAX_FD in #( 147 | max*) 148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 149 | # shellcheck disable=SC2039,SC3045 150 | MAX_FD=$( ulimit -H -n ) || 151 | warn "Could not query maximum file descriptor limit" 152 | esac 153 | case $MAX_FD in #( 154 | '' | soft) :;; #( 155 | *) 156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 157 | # shellcheck disable=SC2039,SC3045 158 | ulimit -n "$MAX_FD" || 159 | warn "Could not set maximum file descriptor limit to $MAX_FD" 160 | esac 161 | fi 162 | 163 | # Collect all arguments for the java command, stacking in reverse order: 164 | # * args from the command line 165 | # * the main class name 166 | # * -classpath 167 | # * -D...appname settings 168 | # * --module-path (only if needed) 169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 170 | 171 | # For Cygwin or MSYS, switch paths to Windows format before running java 172 | if "$cygwin" || "$msys" ; then 173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/SpecialCasePluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.testing.junit.testparameterinjector.junit5.TestParameter 4 | import com.google.testing.junit.testparameterinjector.junit5.TestParameterInjectorTest 5 | import com.vanniktech.maven.publish.ProjectResultSubject.Companion.assertThat 6 | import com.vanniktech.maven.publish.TestOptions.Signing.GPG_KEY 7 | import com.vanniktech.maven.publish.TestOptions.Signing.NO_SIGNING 8 | 9 | class SpecialCasePluginTest : BasePluginTest() { 10 | override val testOptions get() = TestOptions(config, NO_SIGNING, gradleVersion) 11 | 12 | @TestParameterInjectorTest 13 | fun artifactIdThatContainsProjectNameProducesCorrectArtifactId( 14 | @TestParameter(valuesProvider = KotlinVersionProvider::class) kotlinVersion: KotlinVersion, 15 | ) { 16 | kotlinVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 17 | 18 | val project = kotlinMultiplatformProjectSpec(kotlinVersion).copy( 19 | defaultProjectName = "foo", 20 | artifactId = "foo-bar", 21 | ) 22 | val result = project.run(fixtures, testProjectDir, testOptions) 23 | 24 | assertThat(result).outcome().succeeded() 25 | assertThat(result).artifact("jar").exists() 26 | assertThat(result).pom().exists() 27 | assertThat(result).pom().matchesExpectedPom( 28 | kotlinStdlibCommon(kotlinVersion).copy(scope = "runtime"), 29 | ) 30 | assertThat(result).module().exists() 31 | assertThat(result).sourcesJar().exists() 32 | assertThat(result).sourcesJar().containsSourceSetFiles("commonMain") 33 | assertThat(result).javadocJar().exists() 34 | 35 | val jvmResult = result.withArtifactIdSuffix("jvm") 36 | assertThat(jvmResult).outcome().succeeded() 37 | assertThat(jvmResult).artifact("jar").exists() 38 | assertThat(jvmResult).pom().exists() 39 | assertThat(jvmResult).pom().matchesExpectedPom( 40 | kotlinStdlibJdk(kotlinVersion), 41 | kotlinStdlibCommon(kotlinVersion), 42 | ) 43 | assertThat(jvmResult).module().exists() 44 | assertThat(jvmResult).sourcesJar().exists() 45 | assertThat(jvmResult).sourcesJar().containsSourceSetFiles("commonMain", "jvmMain") 46 | assertThat(jvmResult).javadocJar().exists() 47 | 48 | val linuxResult = result.withArtifactIdSuffix("linuxx64") 49 | assertThat(linuxResult).outcome().succeeded() 50 | assertThat(linuxResult).artifact("klib").exists() 51 | assertThat(linuxResult).pom().exists() 52 | assertThat(linuxResult).pom().matchesExpectedPom( 53 | "klib", 54 | kotlinStdlibCommon(kotlinVersion), 55 | ) 56 | assertThat(linuxResult).module().exists() 57 | assertThat(linuxResult).sourcesJar().exists() 58 | assertThat(linuxResult).sourcesJar().containsSourceSetFiles("commonMain", "linuxX64Main") 59 | assertThat(linuxResult).javadocJar().exists() 60 | 61 | val nodejsResult = result.withArtifactIdSuffix("nodejs") 62 | assertThat(nodejsResult).outcome().succeeded() 63 | assertThat(nodejsResult).artifact("klib").exists() 64 | assertThat(nodejsResult).pom().exists() 65 | assertThat(nodejsResult).pom().matchesExpectedPom("klib", kotlinStdlibJs(kotlinVersion), kotlinDomApi(kotlinVersion)) 66 | assertThat(nodejsResult).module().exists() 67 | assertThat(nodejsResult).sourcesJar().exists() 68 | assertThat(nodejsResult).sourcesJar().containsSourceSetFiles("commonMain", "nodeJsMain") 69 | assertThat(nodejsResult).javadocJar().exists() 70 | } 71 | 72 | @TestParameterInjectorTest 73 | fun artifactIdThatContainsProjectNameProducesCorrectArtifactId2( 74 | @TestParameter(valuesProvider = KotlinVersionProvider::class) kotlinVersion: KotlinVersion, 75 | ) { 76 | kotlinVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 77 | 78 | val project = kotlinMultiplatformProjectSpec(kotlinVersion).copy( 79 | defaultProjectName = "foo", 80 | artifactId = "bar-foo", 81 | ) 82 | val result = project.run(fixtures, testProjectDir, testOptions) 83 | 84 | assertThat(result).outcome().succeeded() 85 | assertThat(result).artifact("jar").exists() 86 | assertThat(result).pom().exists() 87 | assertThat(result).pom().matchesExpectedPom( 88 | kotlinStdlibCommon(kotlinVersion).copy(scope = "runtime"), 89 | ) 90 | assertThat(result).module().exists() 91 | assertThat(result).sourcesJar().exists() 92 | assertThat(result).sourcesJar().containsSourceSetFiles("commonMain") 93 | assertThat(result).javadocJar().exists() 94 | 95 | val jvmResult = result.withArtifactIdSuffix("jvm") 96 | assertThat(jvmResult).outcome().succeeded() 97 | assertThat(jvmResult).artifact("jar").exists() 98 | assertThat(jvmResult).pom().exists() 99 | assertThat(jvmResult).pom().matchesExpectedPom( 100 | kotlinStdlibJdk(kotlinVersion), 101 | kotlinStdlibCommon(kotlinVersion), 102 | ) 103 | assertThat(jvmResult).module().exists() 104 | assertThat(jvmResult).sourcesJar().exists() 105 | assertThat(jvmResult).sourcesJar().containsSourceSetFiles("commonMain", "jvmMain") 106 | assertThat(jvmResult).javadocJar().exists() 107 | 108 | val linuxResult = result.withArtifactIdSuffix("linuxx64") 109 | assertThat(linuxResult).outcome().succeeded() 110 | assertThat(linuxResult).artifact("klib").exists() 111 | assertThat(linuxResult).pom().exists() 112 | assertThat(linuxResult).pom().matchesExpectedPom( 113 | "klib", 114 | kotlinStdlibCommon(kotlinVersion), 115 | ) 116 | assertThat(linuxResult).module().exists() 117 | assertThat(linuxResult).sourcesJar().exists() 118 | assertThat(linuxResult).sourcesJar().containsSourceSetFiles("commonMain", "linuxX64Main") 119 | assertThat(linuxResult).javadocJar().exists() 120 | 121 | val nodejsResult = result.withArtifactIdSuffix("nodejs") 122 | assertThat(nodejsResult).outcome().succeeded() 123 | assertThat(nodejsResult).artifact("klib").exists() 124 | assertThat(nodejsResult).pom().exists() 125 | assertThat(nodejsResult).pom().matchesExpectedPom("klib", kotlinStdlibJs(kotlinVersion), kotlinDomApi(kotlinVersion)) 126 | assertThat(nodejsResult).module().exists() 127 | assertThat(nodejsResult).sourcesJar().exists() 128 | assertThat(nodejsResult).sourcesJar().containsSourceSetFiles("commonMain", "nodeJsMain") 129 | assertThat(nodejsResult).javadocJar().exists() 130 | } 131 | 132 | @TestParameterInjectorTest 133 | fun minimalPomProject() { 134 | val project = javaProjectSpec().copy( 135 | properties = emptyMap(), 136 | ) 137 | val result = project.run(fixtures, testProjectDir, testOptions) 138 | 139 | assertThat(result).outcome().succeeded() 140 | assertThat(result).artifact("jar").exists() 141 | assertThat(result).pom().exists() 142 | assertThat(result).pom().matchesExpectedPom(modelFactory = ::createMinimalPom) 143 | assertThat(result).module().exists() 144 | assertThat(result).sourcesJar().exists() 145 | assertThat(result).sourcesJar().containsAllSourceFiles() 146 | assertThat(result).javadocJar().exists() 147 | } 148 | 149 | @TestParameterInjectorTest 150 | fun groupAndVersionFromProjectProject() { 151 | val project = javaProjectSpec().copy( 152 | group = null, 153 | artifactId = null, 154 | version = null, 155 | buildFileExtra = 156 | """ 157 | group = "com.example.test2" 158 | version = "3.2.1" 159 | """.trimIndent(), 160 | ) 161 | val result = project.run(fixtures, testProjectDir, testOptions) 162 | 163 | val resultSpec = project.copy( 164 | group = "com.example.test2", 165 | // the project name is used as default value for the artifact id 166 | artifactId = "module", 167 | version = "3.2.1", 168 | ) 169 | val actualResult = result.copy(projectSpec = resultSpec) 170 | assertThat(actualResult).outcome().succeeded() 171 | assertThat(actualResult).artifact("jar").exists() 172 | assertThat(actualResult).pom().exists() 173 | assertThat(actualResult).pom().matchesExpectedPom() 174 | assertThat(actualResult).module().exists() 175 | assertThat(actualResult).sourcesJar().exists() 176 | assertThat(actualResult).sourcesJar().containsAllSourceFiles() 177 | assertThat(actualResult).javadocJar().exists() 178 | } 179 | 180 | @TestParameterInjectorTest 181 | fun withoutSigning() { 182 | val project = javaProjectSpec() 183 | val result = project.run(fixtures, testProjectDir, testOptions) 184 | 185 | assertThat(result).outcome().succeeded() 186 | assertThat(result).artifact("jar").exists() 187 | assertThat(result).artifact("jar").isNotSigned() 188 | assertThat(result).pom().exists() 189 | assertThat(result).pom().isNotSigned() 190 | assertThat(result).module().exists() 191 | assertThat(result).module().isNotSigned() 192 | assertThat(result).sourcesJar().exists() 193 | assertThat(result).sourcesJar().isNotSigned() 194 | assertThat(result).javadocJar().exists() 195 | assertThat(result).javadocJar().isNotSigned() 196 | } 197 | 198 | @TestParameterInjectorTest 199 | fun signWithGpgKey() { 200 | val project = javaProjectSpec() 201 | val result = project.run(fixtures, testProjectDir, testOptions.copy(signing = GPG_KEY)) 202 | 203 | assertThat(result).outcome().succeeded() 204 | assertThat(result).artifact("jar").exists() 205 | assertThat(result).artifact("jar").isSigned() 206 | assertThat(result).pom().exists() 207 | assertThat(result).pom().isSigned() 208 | assertThat(result).pom().matchesExpectedPom() 209 | assertThat(result).module().exists() 210 | assertThat(result).module().isSigned() 211 | assertThat(result).sourcesJar().exists() 212 | assertThat(result).sourcesJar().isSigned() 213 | assertThat(result).sourcesJar().containsAllSourceFiles() 214 | assertThat(result).javadocJar().exists() 215 | assertThat(result).javadocJar().isSigned() 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/JavaPluginTest.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.google.testing.junit.testparameterinjector.junit5.TestParameter 4 | import com.google.testing.junit.testparameterinjector.junit5.TestParameterInjectorTest 5 | import com.vanniktech.maven.publish.ProjectResultSubject.Companion.assertThat 6 | 7 | class JavaPluginTest : BasePluginTest() { 8 | @TestParameterInjectorTest 9 | fun javaProject() { 10 | val project = javaProjectSpec() 11 | val result = project.run(fixtures, testProjectDir, testOptions) 12 | 13 | assertThat(result).outcome().succeeded() 14 | assertThat(result).artifact("jar").exists() 15 | assertThat(result).artifact("jar").isSigned() 16 | assertThat(result).pom().exists() 17 | assertThat(result).pom().isSigned() 18 | assertThat(result).pom().matchesExpectedPom() 19 | assertThat(result).module().exists() 20 | assertThat(result).module().isSigned() 21 | assertThat(result).sourcesJar().exists() 22 | assertThat(result).sourcesJar().isSigned() 23 | assertThat(result).sourcesJar().containsAllSourceFiles() 24 | assertThat(result).javadocJar().exists() 25 | assertThat(result).javadocJar().isSigned() 26 | } 27 | 28 | @TestParameterInjectorTest 29 | fun javaLibraryProject() { 30 | val project = javaLibraryProjectSpec() 31 | val result = project.run(fixtures, testProjectDir, testOptions) 32 | 33 | assertThat(result).outcome().succeeded() 34 | assertThat(result).artifact("jar").exists() 35 | assertThat(result).artifact("jar").isSigned() 36 | assertThat(result).pom().exists() 37 | assertThat(result).pom().isSigned() 38 | assertThat(result).pom().matchesExpectedPom() 39 | assertThat(result).module().exists() 40 | assertThat(result).module().isSigned() 41 | assertThat(result).sourcesJar().exists() 42 | assertThat(result).sourcesJar().isSigned() 43 | assertThat(result).sourcesJar().containsAllSourceFiles() 44 | assertThat(result).javadocJar().exists() 45 | assertThat(result).javadocJar().isSigned() 46 | } 47 | 48 | @TestParameterInjectorTest 49 | fun javaLibraryWithTestFixturesProject() { 50 | val default = javaLibraryProjectSpec() 51 | val project = default.copy( 52 | plugins = default.plugins + javaTestFixturesPlugin, 53 | sourceFiles = default.sourceFiles + 54 | SourceFile("testFixtures", "java", "com/vanniktech/maven/publish/test/TestFixtureClass.java"), 55 | ) 56 | val result = project.run(fixtures, testProjectDir, testOptions) 57 | 58 | assertThat(result).outcome().succeeded() 59 | assertThat(result).artifact("jar").exists() 60 | assertThat(result).artifact("jar").isSigned() 61 | assertThat(result).pom().exists() 62 | assertThat(result).pom().isSigned() 63 | assertThat(result).pom().matchesExpectedPom() 64 | assertThat(result).module().exists() 65 | assertThat(result).module().isSigned() 66 | assertThat(result).sourcesJar().exists() 67 | assertThat(result).sourcesJar().isSigned() 68 | assertThat(result).sourcesJar().containsSourceSetFiles("main") 69 | assertThat(result).javadocJar().exists() 70 | assertThat(result).javadocJar().isSigned() 71 | assertThat(result).artifact("test-fixtures", "jar").exists() 72 | assertThat(result).artifact("test-fixtures", "jar").isSigned() 73 | assertThat(result).sourcesJar("test-fixtures").exists() 74 | assertThat(result).sourcesJar("test-fixtures").isSigned() 75 | assertThat(result).sourcesJar("test-fixtures").containsSourceSetFiles("testFixtures") 76 | } 77 | 78 | @TestParameterInjectorTest 79 | fun javaGradlePluginProject() { 80 | val project = javaGradlePluginProjectSpec() 81 | val result = project.run(fixtures, testProjectDir, testOptions) 82 | 83 | assertThat(result).outcome().succeeded() 84 | assertThat(result).artifact("jar").exists() 85 | assertThat(result).artifact("jar").isSigned() 86 | assertThat(result).pom().exists() 87 | assertThat(result).pom().isSigned() 88 | assertThat(result).pom().matchesExpectedPom() 89 | assertThat(result).module().exists() 90 | assertThat(result).module().isSigned() 91 | assertThat(result).sourcesJar().exists() 92 | assertThat(result).sourcesJar().isSigned() 93 | assertThat(result).sourcesJar().containsAllSourceFiles() 94 | assertThat(result).javadocJar().exists() 95 | assertThat(result).javadocJar().isSigned() 96 | 97 | val pluginId = "com.example.test-plugin" 98 | val pluginMarkerSpec = project.copy(group = pluginId, artifactId = "$pluginId.gradle.plugin") 99 | val pluginMarkerResult = result.copy(projectSpec = pluginMarkerSpec) 100 | assertThat(pluginMarkerResult).pom().exists() 101 | assertThat(pluginMarkerResult).pom().isSigned() 102 | assertThat(pluginMarkerResult).pom().matchesExpectedPom( 103 | "pom", 104 | PomDependency("com.example", "test-artifact", "1.0.0", null), 105 | ) 106 | } 107 | 108 | @TestParameterInjectorTest 109 | fun javaGradlePluginWithPluginPublishProject( 110 | @TestParameter(valuesProvider = GradlePluginPublishVersionProvider::class) gradlePluginPublish: GradlePluginPublish, 111 | ) { 112 | val project = javaGradlePluginWithGradlePluginPublish(gradlePluginPublish) 113 | val result = project.run(fixtures, testProjectDir, testOptions) 114 | 115 | assertThat(result).outcome().succeeded() 116 | assertThat(result).artifact("jar").exists() 117 | assertThat(result).artifact("jar").isSigned() 118 | assertThat(result).pom().exists() 119 | assertThat(result).pom().isSigned() 120 | assertThat(result).pom().matchesExpectedPom() 121 | assertThat(result).module().exists() 122 | assertThat(result).module().isSigned() 123 | assertThat(result).sourcesJar().exists() 124 | assertThat(result).sourcesJar().isSigned() 125 | assertThat(result).sourcesJar().containsAllSourceFiles() 126 | assertThat(result).javadocJar().exists() 127 | assertThat(result).javadocJar().isSigned() 128 | 129 | val pluginId = "com.example.test-plugin" 130 | val pluginMarkerSpec = project.copy(group = pluginId, artifactId = "$pluginId.gradle.plugin") 131 | val pluginMarkerResult = result.copy(projectSpec = pluginMarkerSpec) 132 | assertThat(pluginMarkerResult).pom().exists() 133 | assertThat(pluginMarkerResult).pom().isSigned() 134 | assertThat(pluginMarkerResult).pom().matchesExpectedPom( 135 | "pom", 136 | PomDependency("com.example", "test-artifact", "1.0.0", null), 137 | ) 138 | } 139 | 140 | @TestParameterInjectorTest 141 | fun javaGradlePluginKotlinProject( 142 | @TestParameter(valuesProvider = KotlinVersionProvider::class) kotlinVersion: KotlinVersion, 143 | ) { 144 | kotlinVersion.assumeSupportedJdkAndGradleVersion(gradleVersion) 145 | 146 | val project = javaGradlePluginKotlinProjectSpec(kotlinVersion) 147 | val result = project.run(fixtures, testProjectDir, testOptions) 148 | 149 | assertThat(result).outcome().succeeded() 150 | assertThat(result).artifact("jar").exists() 151 | assertThat(result).artifact("jar").isSigned() 152 | assertThat(result).pom().exists() 153 | assertThat(result).pom().isSigned() 154 | assertThat(result).pom().matchesExpectedPom(kotlinStdlibJdk(kotlinVersion)) 155 | assertThat(result).module().exists() 156 | assertThat(result).module().isSigned() 157 | assertThat(result).sourcesJar().exists() 158 | assertThat(result).sourcesJar().isSigned() 159 | assertThat(result).sourcesJar().containsAllSourceFiles() 160 | assertThat(result).javadocJar().exists() 161 | assertThat(result).javadocJar().isSigned() 162 | 163 | val pluginId = "com.example.test-plugin" 164 | val pluginMarkerSpec = project.copy(group = pluginId, artifactId = "$pluginId.gradle.plugin") 165 | val pluginMarkerResult = result.copy(projectSpec = pluginMarkerSpec) 166 | assertThat(pluginMarkerResult).pom().exists() 167 | assertThat(pluginMarkerResult).pom().isSigned() 168 | assertThat(pluginMarkerResult).pom().matchesExpectedPom( 169 | "pom", 170 | PomDependency("com.example", "test-artifact", "1.0.0", null), 171 | ) 172 | } 173 | 174 | @TestParameterInjectorTest 175 | fun javaLibraryWithToolchainProject() { 176 | val project = javaLibraryProjectSpec().copy( 177 | buildFileExtra = 178 | """ 179 | java { 180 | toolchain.languageVersion = JavaLanguageVersion.of(11) 181 | } 182 | """.trimIndent(), 183 | ) 184 | val result = project.run(fixtures, testProjectDir, testOptions) 185 | 186 | assertThat(result).outcome().succeeded() 187 | assertThat(result).artifact("jar").exists() 188 | assertThat(result).artifact("jar").isSigned() 189 | assertThat(result).pom().exists() 190 | assertThat(result).pom().isSigned() 191 | assertThat(result).pom().matchesExpectedPom() 192 | assertThat(result).module().exists() 193 | assertThat(result).module().isSigned() 194 | assertThat(result).sourcesJar().exists() 195 | assertThat(result).sourcesJar().isSigned() 196 | assertThat(result).sourcesJar().containsAllSourceFiles() 197 | assertThat(result).javadocJar().exists() 198 | assertThat(result).javadocJar().isSigned() 199 | } 200 | 201 | @TestParameterInjectorTest 202 | fun javaPlatformProject() { 203 | val project = javaPlatformProjectSpec() 204 | val result = project.run(fixtures, testProjectDir, testOptions) 205 | 206 | assertThat(result).outcome().succeeded() 207 | assertThat(result).pom().exists() 208 | assertThat(result).pom().isSigned() 209 | assertThat(result).pom().matchesExpectedPom( 210 | packaging = "pom", 211 | dependencyManagementDependencies = listOf( 212 | PomDependency("commons-httpclient", "commons-httpclient", "3.1", null), 213 | PomDependency("org.postgresql", "postgresql", "42.2.5", null), 214 | ), 215 | ) 216 | assertThat(result).module().exists() 217 | assertThat(result).module().isSigned() 218 | } 219 | 220 | @TestParameterInjectorTest 221 | fun versionCatalogProject() { 222 | val project = versionCatalogProjectSpec() 223 | val result = project.run(fixtures, testProjectDir, testOptions) 224 | 225 | assertThat(result).outcome().succeeded() 226 | assertThat(result).artifact("toml").exists() 227 | assertThat(result).artifact("toml").isSigned() 228 | assertThat(result).pom().exists() 229 | assertThat(result).pom().isSigned() 230 | assertThat(result).pom().matchesExpectedPom("toml") 231 | assertThat(result).module().exists() 232 | assertThat(result).module().isSigned() 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/Subjects.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.autonomousapps.kit.truth.BuildResultSubject.Companion.buildResults 4 | import com.autonomousapps.kit.truth.BuildTaskSubject 5 | import com.google.common.truth.Fact 6 | import com.google.common.truth.Fact.fact 7 | import com.google.common.truth.Fact.simpleFact 8 | import com.google.common.truth.FailureMetadata 9 | import com.google.common.truth.Subject 10 | import com.google.common.truth.Truth.assertAbout 11 | import com.google.common.truth.Truth.assertThat 12 | import com.vanniktech.maven.publish.ArtifactSubject.Companion.artifact 13 | import com.vanniktech.maven.publish.PomSubject.Companion.pomSubject 14 | import com.vanniktech.maven.publish.SourcesJarSubject.Companion.sourcesJarSubject 15 | import java.io.StringWriter 16 | import java.nio.file.Path 17 | import java.util.zip.ZipEntry 18 | import java.util.zip.ZipFile 19 | import kotlin.io.path.exists 20 | import kotlin.io.path.inputStream 21 | import kotlin.io.path.name 22 | import kotlin.io.path.readText 23 | import org.apache.maven.model.Dependency 24 | import org.apache.maven.model.Model 25 | import org.apache.maven.model.io.xpp3.MavenXpp3Reader 26 | import org.apache.maven.model.io.xpp3.MavenXpp3Writer 27 | 28 | class ProjectResultSubject private constructor( 29 | failureMetadata: FailureMetadata, 30 | private val result: ProjectResult, 31 | ) : Subject(failureMetadata, result) { 32 | companion object { 33 | private val BUILD_RESULT_SUBJECT_FACTORY: Factory = 34 | Factory { metadata, actual -> ProjectResultSubject(metadata, actual!!) } 35 | 36 | fun projectResult() = BUILD_RESULT_SUBJECT_FACTORY 37 | 38 | fun assertThat(actual: ProjectResult): ProjectResultSubject = assertAbout(projectResult()).that(actual) 39 | } 40 | 41 | fun outcome(): BuildTaskSubject = check("outcome") 42 | .about(buildResults()) 43 | .that(result.result) 44 | .task(result.task) 45 | 46 | fun artifact(extension: String): ArtifactSubject = check("artifact") 47 | .about(artifact()) 48 | .that(artifactPath("", extension) to result) 49 | 50 | fun artifact(qualifier: String, extension: String): ArtifactSubject = check("artifact") 51 | .about(artifact()) 52 | .that(artifactPath("-$qualifier", extension) to result) 53 | 54 | fun sourcesJar(): SourcesJarSubject = check("sourcesJar") 55 | .about(sourcesJarSubject()) 56 | .that(artifactPath("-sources", "jar") to result) 57 | 58 | fun sourcesJar(qualifier: String): SourcesJarSubject = check("sourcesJar") 59 | .about(sourcesJarSubject()) 60 | .that(artifactPath("-$qualifier-sources", "jar") to result) 61 | 62 | fun javadocJar(): ArtifactSubject = check("javadocJar") 63 | .about(artifact()) 64 | .that(artifactPath("-javadoc", "jar") to result) 65 | 66 | fun javadocJar(qualifier: String): ArtifactSubject = check("javadocJar") 67 | .about(artifact()) 68 | .that(artifactPath("-$qualifier-javadoc", "jar") to result) 69 | 70 | fun pom(): PomSubject = check("pom") 71 | .about(pomSubject()) 72 | .that(artifactPath("", "pom") to result) 73 | 74 | fun module(): ArtifactSubject = check("module") 75 | .about(artifact()) 76 | .that(artifactPath("", "module") to result) 77 | 78 | private fun artifactPath(suffix: String, extension: String): Path = with(result.projectSpec) { 79 | return result.repo 80 | .resolve(group!!.replace(".", "/")) 81 | .resolve(artifactId!!) 82 | .resolve(version!!) 83 | .resolve("$artifactId-$version$suffix.$extension") 84 | } 85 | } 86 | 87 | open class ArtifactSubject internal constructor( 88 | failureMetadata: FailureMetadata, 89 | private val artifact: Path, 90 | private val result: ProjectResult, 91 | ) : Subject(failureMetadata, artifact) { 92 | companion object { 93 | private val BUILD_RESULT_SUBJECT_FACTORY: Factory> = 94 | Factory { metadata, actual -> ArtifactSubject(metadata, actual!!.first, actual.second) } 95 | 96 | fun artifact() = BUILD_RESULT_SUBJECT_FACTORY 97 | } 98 | 99 | fun exists() { 100 | if (!artifact.exists()) { 101 | val files = result.repo 102 | .toFile() 103 | .walkTopDown() 104 | .filter { it.isFile } 105 | .toList() 106 | failWithActual(fact("expected to exist", artifact), fact("but repo contained", files)) 107 | } 108 | } 109 | 110 | fun doesNotExist() { 111 | if (artifact.exists()) { 112 | val files = result.repo 113 | .toFile() 114 | .walkTopDown() 115 | .filter { it.isFile } 116 | .toList() 117 | failWithActual(fact("expected not to exist", artifact), fact("but repo contained", files)) 118 | } 119 | } 120 | 121 | fun isSigned() { 122 | val signedArtifact = artifact.resolveSibling("${artifact.name}.asc") 123 | if (!signedArtifact.exists()) { 124 | failWithoutActual(fact("expected to exist", signedArtifact)) 125 | } 126 | } 127 | 128 | fun isNotSigned() { 129 | val signedArtifact = artifact.resolveSibling("${artifact.name}.asc") 130 | if (signedArtifact.exists()) { 131 | failWithoutActual(fact("expected not to exist", signedArtifact)) 132 | } 133 | } 134 | 135 | fun containsFiles(ignoreAdditionalFiles: Boolean, vararg files: String) { 136 | containsMatchingFiles( 137 | filesToFind = files.toList(), 138 | filesToIgnore = emptyList(), 139 | failWhenAdditionalFilesFound = !ignoreAdditionalFiles, 140 | fileMatcher = { sourceFile, zipEntry -> zipEntry.name == sourceFile }, 141 | fileDescriptor = { it }, 142 | fileContent = { null }, 143 | ) 144 | } 145 | 146 | protected fun containsMatchingFiles( 147 | filesToFind: List, 148 | filesToIgnore: List, 149 | failWhenAdditionalFilesFound: Boolean, 150 | fileMatcher: (T, ZipEntry) -> Boolean, 151 | fileDescriptor: (T) -> String, 152 | // only match file content if this does not return null 153 | fileContent: (T) -> String?, 154 | ) { 155 | val zip = ZipFile(artifact.toFile()) 156 | val zipFiles = zip 157 | .entries() 158 | .toList() 159 | .filter { zipEntry -> !zipEntry.isDirectory && filesToIgnore.none { zipEntry.name.contains(it) } } 160 | .toMutableList() 161 | 162 | val missingFiles = mutableListOf() 163 | val notMatchingFiles = mutableListOf() 164 | 165 | filesToFind.forEach { sourceFile -> 166 | // fallback is a workaround for Kotlin creating a main folder inside the jar 167 | val entry = zipFiles.find { fileMatcher(sourceFile, it) } 168 | if (entry == null) { 169 | missingFiles.add(fileDescriptor(sourceFile)) 170 | } else { 171 | zipFiles.remove(entry) 172 | 173 | val content = zip 174 | .getInputStream(entry) 175 | ?.reader() 176 | ?.buffered() 177 | ?.readText() 178 | val expectedContent = fileContent(sourceFile) 179 | if (expectedContent != null && expectedContent != content) { 180 | notMatchingFiles += fact("expected ${fileDescriptor(sourceFile)} to equal", expectedContent) 181 | notMatchingFiles += fact("but was", content) 182 | } 183 | } 184 | } 185 | 186 | val facts = mutableListOf() 187 | 188 | if (missingFiles.isNotEmpty()) { 189 | facts += fact("expected to contain", missingFiles) 190 | facts += simpleFact("but did not.") 191 | } 192 | 193 | if (failWhenAdditionalFilesFound) { 194 | if (zipFiles.isNotEmpty()) { 195 | facts += fact("expected not to contain", zipFiles.map { it.name }) 196 | facts += simpleFact("but did.") 197 | } 198 | } 199 | 200 | facts += notMatchingFiles 201 | 202 | if (facts.isNotEmpty()) { 203 | failWithoutActual(facts.first(), *facts.drop(1).toTypedArray()) 204 | } 205 | } 206 | } 207 | 208 | class SourcesJarSubject private constructor( 209 | failureMetadata: FailureMetadata, 210 | artifact: Path, 211 | private val result: ProjectResult, 212 | ) : ArtifactSubject(failureMetadata, artifact, result) { 213 | companion object { 214 | private val BUILD_RESULT_SUBJECT_FACTORY: Factory> = 215 | Factory { metadata, actual -> SourcesJarSubject(metadata, actual!!.first, actual.second) } 216 | 217 | fun sourcesJarSubject() = BUILD_RESULT_SUBJECT_FACTORY 218 | } 219 | 220 | fun containsAllSourceFiles() { 221 | containsSourceFiles(result.projectSpec.sourceFiles) 222 | } 223 | 224 | fun containsSourceSetFiles(vararg sourceSets: String) { 225 | containsSourceFiles(result.projectSpec.sourceFiles.filter { sourceSets.contains(it.sourceSet) }) 226 | } 227 | 228 | private fun containsSourceFiles(sourceFiles: List) { 229 | containsMatchingFiles( 230 | filesToFind = sourceFiles, 231 | filesToIgnore = listOf("META-INF", "BuildConfig.java"), 232 | failWhenAdditionalFilesFound = true, 233 | fileMatcher = { sourceFile, zipEntry -> 234 | zipEntry.name == sourceFile.file || zipEntry.name == "${sourceFile.sourceSet}/${sourceFile.file}" 235 | }, 236 | fileDescriptor = { "${it.sourceSet}/${it.file}" }, 237 | fileContent = { it.resolveIn(result.project).readText() }, 238 | ) 239 | } 240 | } 241 | 242 | class PomSubject private constructor( 243 | failureMetadata: FailureMetadata, 244 | private val artifact: Path, 245 | private val result: ProjectResult, 246 | ) : ArtifactSubject(failureMetadata, artifact, result) { 247 | companion object { 248 | private val BUILD_RESULT_SUBJECT_FACTORY: Factory> = 249 | Factory { metadata, actual -> PomSubject(metadata, actual!!.first, actual.second) } 250 | 251 | fun pomSubject() = BUILD_RESULT_SUBJECT_FACTORY 252 | } 253 | 254 | fun matchesExpectedPom(vararg dependencies: PomDependency) { 255 | matchesExpectedPom(dependencies = dependencies.toList()) 256 | } 257 | 258 | fun matchesExpectedPom(packaging: String, vararg dependencies: PomDependency) { 259 | matchesExpectedPom(packaging, dependencies.toList()) 260 | } 261 | 262 | fun matchesExpectedPom( 263 | packaging: String? = null, 264 | dependencies: List = emptyList(), 265 | dependencyManagementDependencies: List = emptyList(), 266 | modelFactory: (String, String, String, String?, List, List) -> Model = ::createPom, 267 | ) { 268 | val pomWriter = MavenXpp3Writer() 269 | 270 | val actualModel = MavenXpp3Reader().read(artifact.inputStream()) 271 | actualModel.sortDependencies() 272 | val actualWriter = StringWriter() 273 | pomWriter.write(actualWriter, actualModel) 274 | 275 | val expectedModel = modelFactory( 276 | result.projectSpec.group!!, 277 | result.projectSpec.artifactId!!, 278 | result.projectSpec.version!!, 279 | packaging, 280 | dependencies, 281 | dependencyManagementDependencies, 282 | ) 283 | expectedModel.sortDependencies() 284 | val expectedWriter = StringWriter() 285 | pomWriter.write(expectedWriter, expectedModel) 286 | 287 | assertThat(actualWriter.toString()).isEqualTo(expectedWriter.toString()) 288 | } 289 | 290 | private fun Model.sortDependencies() { 291 | dependencies = dependencies.sortedWith(comparator) 292 | if (dependencyManagement != null) { 293 | dependencyManagement.dependencies = dependencyManagement.dependencies.sortedWith(comparator) 294 | } 295 | } 296 | 297 | private val comparator = compareBy { 298 | "${it.scope} ${it.groupId}:${it.artifactId}:${it.version}@${it.classifier}" 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /plugin/api/plugin.api: -------------------------------------------------------------------------------- 1 | public final class com/vanniktech/maven/publish/AndroidFusedLibrary : com/vanniktech/maven/publish/Platform { 2 | public fun ()V 3 | public fun equals (Ljava/lang/Object;)Z 4 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 5 | public fun getSourcesJar ()Z 6 | public fun hashCode ()I 7 | } 8 | 9 | public final class com/vanniktech/maven/publish/AndroidMultiVariantLibrary : com/vanniktech/maven/publish/Platform { 10 | public fun ()V 11 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 12 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 13 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/Set;)V 14 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/Set;Ljava/util/Map;)V 15 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 16 | public fun (Z)V 17 | public fun (ZZ)V 18 | public fun (ZZLjava/util/Set;)V 19 | public fun (ZZLjava/util/Set;Ljava/util/Map;)V 20 | public synthetic fun (ZZLjava/util/Set;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 21 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 22 | public final fun component2 ()Z 23 | public final fun component3 ()Ljava/util/Set; 24 | public final fun component4 ()Ljava/util/Map; 25 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/Set;Ljava/util/Map;)Lcom/vanniktech/maven/publish/AndroidMultiVariantLibrary; 26 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/AndroidMultiVariantLibrary;Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/Set;Ljava/util/Map;ILjava/lang/Object;)Lcom/vanniktech/maven/publish/AndroidMultiVariantLibrary; 27 | public fun equals (Ljava/lang/Object;)Z 28 | public final fun getIncludedBuildTypeValues ()Ljava/util/Set; 29 | public final fun getIncludedFlavorDimensionsAndValues ()Ljava/util/Map; 30 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 31 | public fun getSourcesJar ()Z 32 | public fun hashCode ()I 33 | public fun toString ()Ljava/lang/String; 34 | } 35 | 36 | public final class com/vanniktech/maven/publish/AndroidSingleVariantLibrary : com/vanniktech/maven/publish/Platform { 37 | public fun ()V 38 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 39 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 40 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/lang/String;)V 41 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 42 | public fun (Ljava/lang/String;Z)V 43 | public fun (Ljava/lang/String;ZZ)V 44 | public synthetic fun (Ljava/lang/String;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V 45 | public fun (Z)V 46 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 47 | public final fun component2 ()Z 48 | public final fun component3 ()Ljava/lang/String; 49 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/lang/String;)Lcom/vanniktech/maven/publish/AndroidSingleVariantLibrary; 50 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/AndroidSingleVariantLibrary;Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/lang/String;ILjava/lang/Object;)Lcom/vanniktech/maven/publish/AndroidSingleVariantLibrary; 51 | public fun equals (Ljava/lang/Object;)Z 52 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 53 | public fun getSourcesJar ()Z 54 | public final fun getVariant ()Ljava/lang/String; 55 | public fun hashCode ()I 56 | public fun toString ()Ljava/lang/String; 57 | } 58 | 59 | public final class com/vanniktech/maven/publish/DeploymentValidation : java/lang/Enum { 60 | public static final field NONE Lcom/vanniktech/maven/publish/DeploymentValidation; 61 | public static final field PUBLISH Lcom/vanniktech/maven/publish/DeploymentValidation; 62 | public static final field VALIDATE Lcom/vanniktech/maven/publish/DeploymentValidation; 63 | public static fun getEntries ()Lkotlin/enums/EnumEntries; 64 | public static fun valueOf (Ljava/lang/String;)Lcom/vanniktech/maven/publish/DeploymentValidation; 65 | public static fun values ()[Lcom/vanniktech/maven/publish/DeploymentValidation; 66 | } 67 | 68 | public final class com/vanniktech/maven/publish/GradlePlugin : com/vanniktech/maven/publish/Platform { 69 | public fun ()V 70 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 71 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 72 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V 73 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 74 | public final fun component2 ()Z 75 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;Z)Lcom/vanniktech/maven/publish/GradlePlugin; 76 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/GradlePlugin;Lcom/vanniktech/maven/publish/JavadocJar;ZILjava/lang/Object;)Lcom/vanniktech/maven/publish/GradlePlugin; 77 | public fun equals (Ljava/lang/Object;)Z 78 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 79 | public fun getSourcesJar ()Z 80 | public fun hashCode ()I 81 | public fun toString ()Ljava/lang/String; 82 | } 83 | 84 | public final class com/vanniktech/maven/publish/GradlePublishPlugin : com/vanniktech/maven/publish/Platform { 85 | public fun ()V 86 | public fun equals (Ljava/lang/Object;)Z 87 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 88 | public fun getSourcesJar ()Z 89 | public fun hashCode ()I 90 | } 91 | 92 | public final class com/vanniktech/maven/publish/JavaLibrary : com/vanniktech/maven/publish/Platform { 93 | public fun ()V 94 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 95 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 96 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V 97 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 98 | public final fun component2 ()Z 99 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;Z)Lcom/vanniktech/maven/publish/JavaLibrary; 100 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/JavaLibrary;Lcom/vanniktech/maven/publish/JavadocJar;ZILjava/lang/Object;)Lcom/vanniktech/maven/publish/JavaLibrary; 101 | public fun equals (Ljava/lang/Object;)Z 102 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 103 | public fun getSourcesJar ()Z 104 | public fun hashCode ()I 105 | public fun toString ()Ljava/lang/String; 106 | } 107 | 108 | public final class com/vanniktech/maven/publish/JavaPlatform : com/vanniktech/maven/publish/Platform { 109 | public fun ()V 110 | public fun equals (Ljava/lang/Object;)Z 111 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 112 | public fun getSourcesJar ()Z 113 | public fun hashCode ()I 114 | } 115 | 116 | public abstract interface class com/vanniktech/maven/publish/JavadocJar { 117 | } 118 | 119 | public final class com/vanniktech/maven/publish/JavadocJar$Dokka : com/vanniktech/maven/publish/JavadocJar { 120 | public fun (Ljava/lang/String;)V 121 | public fun (Lorg/gradle/api/provider/Provider;)V 122 | public fun (Lorg/gradle/api/tasks/TaskProvider;)V 123 | public fun equals (Ljava/lang/Object;)Z 124 | public fun hashCode ()I 125 | } 126 | 127 | public final class com/vanniktech/maven/publish/JavadocJar$Empty : com/vanniktech/maven/publish/JavadocJar { 128 | public fun ()V 129 | public fun equals (Ljava/lang/Object;)Z 130 | public fun hashCode ()I 131 | } 132 | 133 | public final class com/vanniktech/maven/publish/JavadocJar$Javadoc : com/vanniktech/maven/publish/JavadocJar { 134 | public fun ()V 135 | public fun equals (Ljava/lang/Object;)Z 136 | public fun hashCode ()I 137 | } 138 | 139 | public final class com/vanniktech/maven/publish/JavadocJar$None : com/vanniktech/maven/publish/JavadocJar { 140 | public fun ()V 141 | public fun equals (Ljava/lang/Object;)Z 142 | public fun hashCode ()I 143 | } 144 | 145 | public final class com/vanniktech/maven/publish/KotlinJvm : com/vanniktech/maven/publish/Platform { 146 | public fun ()V 147 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 148 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 149 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V 150 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 151 | public final fun component2 ()Z 152 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;Z)Lcom/vanniktech/maven/publish/KotlinJvm; 153 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/KotlinJvm;Lcom/vanniktech/maven/publish/JavadocJar;ZILjava/lang/Object;)Lcom/vanniktech/maven/publish/KotlinJvm; 154 | public fun equals (Ljava/lang/Object;)Z 155 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 156 | public fun getSourcesJar ()Z 157 | public fun hashCode ()I 158 | public fun toString ()Ljava/lang/String; 159 | } 160 | 161 | public final class com/vanniktech/maven/publish/KotlinMultiplatform : com/vanniktech/maven/publish/Platform { 162 | public fun ()V 163 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;)V 164 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;Z)V 165 | public fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/List;)V 166 | public synthetic fun (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V 167 | public final fun component1 ()Lcom/vanniktech/maven/publish/JavadocJar; 168 | public final fun component2 ()Z 169 | public final fun component3 ()Ljava/util/List; 170 | public final fun copy (Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/List;)Lcom/vanniktech/maven/publish/KotlinMultiplatform; 171 | public static synthetic fun copy$default (Lcom/vanniktech/maven/publish/KotlinMultiplatform;Lcom/vanniktech/maven/publish/JavadocJar;ZLjava/util/List;ILjava/lang/Object;)Lcom/vanniktech/maven/publish/KotlinMultiplatform; 172 | public fun equals (Ljava/lang/Object;)Z 173 | public final fun getAndroidVariantsToPublish ()Ljava/util/List; 174 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 175 | public fun getSourcesJar ()Z 176 | public fun hashCode ()I 177 | public fun toString ()Ljava/lang/String; 178 | } 179 | 180 | public abstract class com/vanniktech/maven/publish/MavenPublishBaseExtension { 181 | public fun (Lorg/gradle/api/Project;Lorg/gradle/build/event/BuildEventsListenerRegistry;Lorg/gradle/api/configuration/BuildFeatures;)V 182 | public final fun configure (Lcom/vanniktech/maven/publish/Platform;)V 183 | public final fun configureBasedOnAppliedPlugins ()V 184 | public final fun configureBasedOnAppliedPlugins (Z)V 185 | public final fun configureBasedOnAppliedPlugins (ZLcom/vanniktech/maven/publish/JavadocJar;)V 186 | public final fun configureBasedOnAppliedPlugins (ZZ)V 187 | public static synthetic fun configureBasedOnAppliedPlugins$default (Lcom/vanniktech/maven/publish/MavenPublishBaseExtension;ZLcom/vanniktech/maven/publish/JavadocJar;ILjava/lang/Object;)V 188 | public final fun coordinates (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V 189 | public static synthetic fun coordinates$default (Lcom/vanniktech/maven/publish/MavenPublishBaseExtension;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V 190 | public final fun pom (Lorg/gradle/api/Action;)V 191 | public final fun pomFromGradleProperties ()V 192 | public final fun publishToMavenCentral ()V 193 | public final fun publishToMavenCentral (Z)V 194 | public final fun publishToMavenCentral (ZLcom/vanniktech/maven/publish/DeploymentValidation;)V 195 | public final fun publishToMavenCentral (ZZ)V 196 | public static synthetic fun publishToMavenCentral$default (Lcom/vanniktech/maven/publish/MavenPublishBaseExtension;ZLcom/vanniktech/maven/publish/DeploymentValidation;ILjava/lang/Object;)V 197 | public final fun signAllPublications ()V 198 | } 199 | 200 | public abstract class com/vanniktech/maven/publish/MavenPublishBasePlugin : org/gradle/api/Plugin { 201 | public fun ()V 202 | public synthetic fun apply (Ljava/lang/Object;)V 203 | public fun apply (Lorg/gradle/api/Project;)V 204 | } 205 | 206 | public abstract class com/vanniktech/maven/publish/MavenPublishPlugin : org/gradle/api/Plugin { 207 | public fun ()V 208 | public synthetic fun apply (Ljava/lang/Object;)V 209 | public fun apply (Lorg/gradle/api/Project;)V 210 | } 211 | 212 | public abstract class com/vanniktech/maven/publish/Platform { 213 | public abstract fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 214 | public abstract fun getSourcesJar ()Z 215 | } 216 | 217 | public final class com/vanniktech/maven/publish/VersionCatalog : com/vanniktech/maven/publish/Platform { 218 | public fun ()V 219 | public fun equals (Ljava/lang/Object;)Z 220 | public fun getJavadocJar ()Lcom/vanniktech/maven/publish/JavadocJar; 221 | public fun getSourcesJar ()Z 222 | public fun hashCode ()I 223 | } 224 | 225 | public abstract class com/vanniktech/maven/publish/tasks/JavadocJar : org/gradle/jvm/tasks/Jar { 226 | public fun ()V 227 | } 228 | 229 | -------------------------------------------------------------------------------- /plugin/src/integrationTest/kotlin/com/vanniktech/maven/publish/ProjectSpecs.kt: -------------------------------------------------------------------------------- 1 | package com.vanniktech.maven.publish 2 | 3 | import com.vanniktech.maven.publish.AgpVersion.Companion.AGP_9_0_0 4 | import com.vanniktech.maven.publish.IntegrationTestBuildConfig.DOKKA_STABLE 5 | import java.nio.file.Paths 6 | import kotlin.io.path.absolute 7 | 8 | val javaPlugin = PluginSpec("java") 9 | val javaLibraryPlugin = PluginSpec("java-library") 10 | val javaGradlePluginPlugin = PluginSpec("java-gradle-plugin") 11 | val javaTestFixturesPlugin = PluginSpec("java-test-fixtures") 12 | val javaPlatformPlugin = PluginSpec("java-platform") 13 | val versionCatalogPlugin = PluginSpec("version-catalog") 14 | val kotlinJvmPlugin = PluginSpec("org.jetbrains.kotlin.jvm") 15 | val kotlinMultiplatformPlugin = PluginSpec("org.jetbrains.kotlin.multiplatform") 16 | val kotlinAndroidPlugin = PluginSpec("org.jetbrains.kotlin.android") 17 | val androidLibraryPlugin = PluginSpec("com.android.library") 18 | val androidMultiplatformLibraryPlugin = PluginSpec("com.android.kotlin.multiplatform.library") 19 | val androidFusedLibraryPlugin = PluginSpec("com.android.fused-library") 20 | val gradlePluginPublishPlugin = PluginSpec("com.gradle.plugin-publish") 21 | val dokkaPlugin = PluginSpec("org.jetbrains.dokka", DOKKA_STABLE) 22 | val dokkaJavadocPlugin = PluginSpec("org.jetbrains.dokka-javadoc", DOKKA_STABLE) 23 | 24 | val fixtures = Paths.get("src/integrationTest/fixtures2").absolute() 25 | 26 | val defaultProperties = mapOf( 27 | "POM_NAME" to "Gradle Maven Publish Plugin Test Artifact", 28 | "POM_DESCRIPTION" to "Testing the Gradle Maven Publish Plugin", 29 | "POM_INCEPTION_YEAR" to "2018", 30 | "POM_URL" to "https://github.com/vanniktech/gradle-maven-publish-plugin/", 31 | "POM_SCM_URL" to "https://github.com/vanniktech/gradle-maven-publish-plugin/", 32 | "POM_SCM_CONNECTION" to "scm:git:git://github.com/vanniktech/gradle-maven-publish-plugin.git", 33 | "POM_SCM_DEV_CONNECTION" to "scm:git:ssh://git@github.com/vanniktech/gradle-maven-publish-plugin.git", 34 | "POM_LICENCE_NAME" to "Apache-2.0", 35 | "POM_LICENCE_URL" to "https://www.apache.org/licenses/LICENSE-2.0.txt", 36 | "POM_LICENCE_DIST" to "repo", 37 | "POM_DEVELOPER_ID" to "vanniktech", 38 | "POM_DEVELOPER_NAME" to "Niklas Baudy", 39 | "POM_DEVELOPER_URL" to "https://github.com/vanniktech/", 40 | ) 41 | 42 | fun javaProjectSpec() = ProjectSpec( 43 | plugins = listOf( 44 | javaPlugin, 45 | ), 46 | group = "com.example", 47 | artifactId = "test-artifact", 48 | version = "1.0.0", 49 | properties = defaultProperties, 50 | sourceFiles = listOf( 51 | SourceFile("main", "java", "com/vanniktech/maven/publish/test/JavaTestClass.java"), 52 | ), 53 | basePluginConfig = "configure(new JavaLibrary(new JavadocJar.Empty(), true))", 54 | ) 55 | 56 | fun javaLibraryProjectSpec() = ProjectSpec( 57 | plugins = listOf( 58 | javaLibraryPlugin, 59 | ), 60 | group = "com.example", 61 | artifactId = "test-artifact", 62 | version = "1.0.0", 63 | properties = defaultProperties, 64 | sourceFiles = listOf( 65 | SourceFile("main", "java", "com/vanniktech/maven/publish/test/JavaTestClass.java"), 66 | ), 67 | basePluginConfig = "configure(new JavaLibrary(new JavadocJar.Empty(), true))", 68 | ) 69 | 70 | fun javaGradlePluginProjectSpec() = ProjectSpec( 71 | plugins = listOf( 72 | javaGradlePluginPlugin, 73 | ), 74 | group = "com.example", 75 | artifactId = "test-artifact", 76 | version = "1.0.0", 77 | properties = defaultProperties, 78 | sourceFiles = listOf( 79 | SourceFile("main", "java", "com/vanniktech/maven/publish/test/JavaTestClass.java"), 80 | ), 81 | buildFileExtra = 82 | """ 83 | gradlePlugin { 84 | plugins { 85 | mavenPublishPlugin { 86 | // the id here should be different from the group id and artifact id 87 | id = 'com.example.test-plugin' 88 | implementationClass = 'com.vanniktech.maven.publish.test.TestPlugin' 89 | } 90 | } 91 | } 92 | """.trimIndent(), 93 | basePluginConfig = "configure(new GradlePlugin(new JavadocJar.Empty(), true))", 94 | ) 95 | 96 | fun javaGradlePluginWithGradlePluginPublish(gradlePluginPublish: GradlePluginPublish): ProjectSpec { 97 | val base = javaGradlePluginProjectSpec() 98 | return base.copy( 99 | plugins = base.plugins + gradlePluginPublishPlugin.copy(version = gradlePluginPublish.value), 100 | basePluginConfig = "configure(new GradlePublishPlugin())", 101 | ) 102 | } 103 | 104 | fun javaGradlePluginKotlinProjectSpec(version: KotlinVersion): ProjectSpec { 105 | val plainJavaGradlePluginProject = javaGradlePluginProjectSpec() 106 | return plainJavaGradlePluginProject.copy( 107 | plugins = plainJavaGradlePluginProject.plugins + kotlinJvmPlugin.copy(version = version.value), 108 | sourceFiles = plainJavaGradlePluginProject.sourceFiles + listOf( 109 | SourceFile("main", "kotlin", "com/vanniktech/maven/publish/test/KotlinTestClass.kt"), 110 | ), 111 | ) 112 | } 113 | 114 | fun kotlinJvmProjectSpec(version: KotlinVersion) = ProjectSpec( 115 | plugins = listOf( 116 | kotlinJvmPlugin.copy(version = version.value), 117 | ), 118 | group = "com.example", 119 | artifactId = "test-artifact", 120 | version = "1.0.0", 121 | properties = defaultProperties, 122 | sourceFiles = listOf( 123 | SourceFile("main", "java", "com/vanniktech/maven/publish/test/JavaTestClass.java"), 124 | SourceFile("main", "kotlin", "com/vanniktech/maven/publish/test/KotlinTestClass.kt"), 125 | ), 126 | basePluginConfig = "configure(new KotlinJvm(new JavadocJar.Empty(), true))", 127 | ) 128 | 129 | fun kotlinMultiplatformProjectSpec(version: KotlinVersion) = ProjectSpec( 130 | plugins = listOf( 131 | kotlinMultiplatformPlugin.copy(version = version.value), 132 | ), 133 | group = "com.example", 134 | artifactId = "test-artifact", 135 | version = "1.0.0", 136 | properties = defaultProperties, 137 | sourceFiles = listOf( 138 | SourceFile("commonMain", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 139 | SourceFile("jvmMain", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 140 | SourceFile("linuxX64Main", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 141 | SourceFile("nodeJsMain", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 142 | ), 143 | basePluginConfig = "configure(new KotlinMultiplatform(new JavadocJar.Empty()))", 144 | buildFileExtra = 145 | """ 146 | kotlin { 147 | jvm() 148 | js("nodeJs", "IR") { 149 | nodejs() 150 | } 151 | linuxX64() 152 | 153 | sourceSets { 154 | commonMain { 155 | dependencies { 156 | } 157 | } 158 | jvmMain { 159 | dependencies { 160 | } 161 | } 162 | nodeJsMain { 163 | dependencies { 164 | } 165 | } 166 | linuxX64Main { 167 | dependencies { 168 | } 169 | } 170 | } 171 | } 172 | """.trimIndent(), 173 | ) 174 | 175 | // TODO remove when min AGP version is AGP 9 176 | fun kotlinMultiplatformWithAndroidLibraryProjectSpec(agpVersion: AgpVersion, kotlinVersion: KotlinVersion): ProjectSpec { 177 | val baseProject = kotlinMultiplatformProjectSpec(kotlinVersion) 178 | return baseProject.copy( 179 | plugins = baseProject.plugins + listOf(androidLibraryPlugin.copy(version = agpVersion.value)), 180 | sourceFiles = baseProject.sourceFiles + listOf( 181 | SourceFile("androidMain", "kotlin", "com/vanniktech/maven/publish/test/AndroidTestClass.kt"), 182 | SourceFile("androidDebug", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 183 | SourceFile("androidRelease", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 184 | ), 185 | // needs to explicitly specify release to match the main plugin default behavior 186 | basePluginConfig = "configure(new KotlinMultiplatform(new JavadocJar.Empty(), true, [\"release\"]))", 187 | buildFileExtra = baseProject.buildFileExtra + 188 | """ 189 | 190 | android { 191 | compileSdk = 34 192 | namespace = "com.test.library" 193 | } 194 | 195 | kotlin { 196 | androidTarget {} 197 | 198 | jvmToolchain(11) 199 | } 200 | """.trimIndent(), 201 | propertiesExtra = 202 | """ 203 | android.builtInKotlin=false 204 | android.newDsl=false 205 | """.trimIndent(), 206 | ) 207 | } 208 | 209 | fun kotlinMultiplatformWithAndroidLibraryAndSpecifiedVariantsProjectSpec( 210 | agpVersion: AgpVersion, 211 | kotlinVersion: KotlinVersion, 212 | ): ProjectSpec { 213 | val baseProject = kotlinMultiplatformWithAndroidLibraryProjectSpec(agpVersion, kotlinVersion) 214 | return baseProject.copy( 215 | basePluginConfig = "configure(new KotlinMultiplatform(new JavadocJar.Empty()))", 216 | buildFileExtra = baseProject.buildFileExtra + 217 | """ 218 | 219 | kotlin { 220 | androidTarget { 221 | publishLibraryVariants("release", "debug") 222 | } 223 | } 224 | """.trimIndent(), 225 | ) 226 | } 227 | 228 | fun kotlinMultiplatformWithModernAndroidLibraryProjectSpec(agpVersion: AgpVersion, kotlinVersion: KotlinVersion): ProjectSpec { 229 | val baseProject = kotlinMultiplatformProjectSpec(kotlinVersion) 230 | return baseProject.copy( 231 | plugins = baseProject.plugins + listOf(androidMultiplatformLibraryPlugin.copy(version = agpVersion.value)), 232 | sourceFiles = baseProject.sourceFiles + listOf( 233 | SourceFile("androidMain", "kotlin", "com/vanniktech/maven/publish/test/AndroidTestClass.kt"), 234 | SourceFile("androidMain", "kotlin", "com/vanniktech/maven/publish/test/ExpectedTestClass.kt"), 235 | ), 236 | buildFileExtra = 237 | """ 238 | kotlin { 239 | androidLibrary { 240 | compileSdk = 36 241 | namespace = "com.example.namespace" 242 | } 243 | } 244 | 245 | """.trimIndent() + baseProject.buildFileExtra, 246 | ) 247 | } 248 | 249 | fun androidLibraryProjectSpec(version: AgpVersion) = ProjectSpec( 250 | plugins = listOf( 251 | androidLibraryPlugin.copy(version = version.value), 252 | ), 253 | group = "com.example", 254 | artifactId = "test-artifact", 255 | version = "1.0.0", 256 | properties = defaultProperties, 257 | sourceFiles = listOf( 258 | SourceFile("main", "java", "com/vanniktech/maven/publish/test/JavaTestClass.java"), 259 | ), 260 | basePluginConfig = "configure(new AndroidSingleVariantLibrary(\"release\", true, true))", 261 | buildFileExtra = 262 | """ 263 | android { 264 | compileSdk = 34 265 | namespace = "com.test.library" 266 | } 267 | 268 | // disable the dokka task to speed up Android tests significantly 269 | afterEvaluate { 270 | tasks.named("javaDocReleaseGeneration").configure { 271 | it.enabled = false 272 | } 273 | } 274 | """.trimIndent(), 275 | ) 276 | 277 | fun androidLibraryKotlinProjectSpec(agpVersion: AgpVersion, kotlinVersion: KotlinVersion): ProjectSpec { 278 | val plainAndroidProject = androidLibraryProjectSpec(agpVersion) 279 | val plugins = if (agpVersion >= AGP_9_0_0) { 280 | plainAndroidProject.plugins 281 | } else { 282 | plainAndroidProject.plugins + kotlinAndroidPlugin.copy(version = kotlinVersion.value) 283 | } 284 | return plainAndroidProject.copy( 285 | plugins = plugins, 286 | sourceFiles = plainAndroidProject.sourceFiles + listOf( 287 | SourceFile("main", "kotlin", "com/vanniktech/maven/publish/test/KotlinTestClass.kt"), 288 | ), 289 | buildFileExtra = plainAndroidProject.buildFileExtra + 290 | """ 291 | 292 | kotlin { 293 | jvmToolchain(11) 294 | } 295 | """.trimIndent(), 296 | ) 297 | } 298 | 299 | fun androidFusedLibraryProjectSpec(version: AgpVersion) = ProjectSpec( 300 | plugins = listOf( 301 | androidFusedLibraryPlugin.copy(version = version.value), 302 | ), 303 | group = "com.example", 304 | artifactId = "test-artifact", 305 | version = "1.0.0", 306 | properties = defaultProperties, 307 | sourceFiles = emptyList(), 308 | basePluginConfig = "configure(new AndroidFusedLibrary())", 309 | // TODO remove old min sdk syntax when min AGP version is 9 310 | buildFileExtra = 311 | """ 312 | androidFusedLibrary { 313 | namespace = "com.test.library" 314 | ${if (version >= AGP_9_0_0) "minSdk { version = release(34) }" else "minSdk = 29" } 315 | } 316 | """.trimIndent(), 317 | // TODO remove when stable 318 | propertiesExtra = 319 | """ 320 | android.experimental.fusedLibrarySupport=true 321 | """.trimIndent(), 322 | ) 323 | 324 | fun javaPlatformProjectSpec() = ProjectSpec( 325 | plugins = listOf( 326 | javaPlatformPlugin, 327 | ), 328 | group = "com.example", 329 | artifactId = "test-artifact", 330 | version = "1.0.0", 331 | properties = defaultProperties, 332 | sourceFiles = emptyList(), 333 | basePluginConfig = "configure(new JavaPlatform())", 334 | buildFileExtra = 335 | """ 336 | dependencies { 337 | constraints { 338 | api 'commons-httpclient:commons-httpclient:3.1' 339 | runtime 'org.postgresql:postgresql:42.2.5' 340 | } 341 | } 342 | """.trimIndent(), 343 | ) 344 | 345 | fun versionCatalogProjectSpec() = ProjectSpec( 346 | plugins = listOf( 347 | versionCatalogPlugin, 348 | ), 349 | group = "com.example", 350 | artifactId = "test-artifact", 351 | version = "1.0.0", 352 | properties = defaultProperties, 353 | sourceFiles = emptyList(), 354 | basePluginConfig = "configure(new VersionCatalog())", 355 | buildFileExtra = 356 | """ 357 | catalog { 358 | versionCatalog { 359 | library('my-lib', 'com.mycompany:mylib:1.2') 360 | } 361 | } 362 | """.trimIndent(), 363 | ) 364 | -------------------------------------------------------------------------------- /docs/what.md: -------------------------------------------------------------------------------- 1 | # Configuring what to publish 2 | 3 | It is possible to configure publishing for the following Gradle plugins: 4 | 5 | - `com.android.library` as [single variant library](#android-library-single-variant) or 6 | as [multi variant library](#android-library-multiple-variants) 7 | - [`com.android.fused-library`](#android-fused-library) 8 | - [`org.jetbrains.kotlin.jvm`](#kotlin-jvm-library) 9 | - [`org.jetbrains.kotlin.multiplatform`](#kotlin-multiplatform-library) 10 | - automatically includes `com.android.kotlin.multiplatform.library` 11 | - [`java`](#java-library) 12 | - [`java-library`](#java-library) 13 | - [`java-gradle-plugin`](#gradle-plugin) 14 | - [`com.gradle.plugin-publish`](#gradle-publish-plugin) 15 | - [`java-platform`](#java-platform) 16 | - [`version-catalog`](#version-catalog) 17 | 18 | ## Android Library (multiple variants) 19 | 20 | For projects using the `com.android.library` plugin. This will publish all variants of the project (e.g. both 21 | `debug` and `release`) or a subset of specified variants. 22 | 23 | === "build.gradle" 24 | 25 | ```groovy 26 | import com.vanniktech.maven.publish.AndroidMultiVariantLibrary 27 | 28 | mavenPublishing { 29 | // the first parameter represents whether to publish a sources jar 30 | // the second whether to publish a javadoc jar 31 | configure(new AndroidMultiVariantLibrary(true, true)) 32 | // or to limit which build types to include 33 | configure(new AndroidMultiVariantLibrary(true, true, ["beta", "release"] as Set)) 34 | // or to limit which flavors to include, the map key is a flavor dimension, the set contains the flavors 35 | configure(new AndroidMultiVariantLibrary(true, true, ["beta", "release"] as Set, ["store": ["google", "samsung"] as Set])) 36 | } 37 | ``` 38 | 39 | === "build.gradle.kts" 40 | 41 | ```kotlin 42 | import com.vanniktech.maven.publish.AndroidMultiVariantLibrary 43 | 44 | mavenPublishing { 45 | configure(AndroidMultiVariantLibrary( 46 | // whether to publish a sources jar 47 | sourcesJar = true, 48 | // whether to publish a javadoc jar 49 | publishJavadocJar = true, 50 | )) 51 | // or 52 | configure(AndroidMultiVariantLibrary( 53 | // whether to publish a sources jar 54 | sourcesJar = true, 55 | // whether to publish a javadoc jar 56 | publishJavadocJar = true, 57 | // limit which build types to include 58 | includedBuildTypeValues = setOf("beta", "release"), 59 | )) 60 | // or 61 | configure(AndroidMultiVariantLibrary( 62 | // whether to publish a sources jar 63 | sourcesJar = true, 64 | // whether to publish a javadoc jar 65 | publishJavadocJar = true, 66 | // limit which build types to include 67 | includedBuildTypeValues = setOf("beta", "release"), 68 | // limit which flavors to include, the map key is a flavor dimension, the set contains the flavors 69 | includedFlavorDimensionsAndValues = mapOf("store" to setOf("google", "samsung")), 70 | )) 71 | } 72 | ``` 73 | 74 | ## Android Library (single variant) 75 | 76 | For projects using the `com.android.library` plugin. Compared to the multi variant version above this will only publish 77 | the specified variant instead of publishing all of them. 78 | 79 | === "build.gradle" 80 | 81 | ```groovy 82 | import com.vanniktech.maven.publish.AndroidSingleVariantLibrary 83 | 84 | mavenPublishing { 85 | // the first parameter represennts which variant is published 86 | // the second whether to publish a sources jar 87 | // the third whether to publish a javadoc jar 88 | configure(new AndroidSingleVariantLibrary("release", true, true)) 89 | } 90 | ``` 91 | 92 | === "build.gradle.kts" 93 | 94 | ```kotlin 95 | import com.vanniktech.maven.publish.AndroidSingleVariantLibrary 96 | 97 | mavenPublishing { 98 | configure(AndroidSingleVariantLibrary( 99 | // the published variant 100 | variant = "release", 101 | // whether to publish a sources jar 102 | sourcesJar = true, 103 | // whether to publish a javadoc jar 104 | publishJavadocJar = true, 105 | )) 106 | } 107 | ``` 108 | 109 | 110 | ## Android Fused Library 111 | 112 | For projects using the `com.android.fused-library` plugin. 113 | 114 | === "build.gradle" 115 | 116 | ```groovy 117 | import com.vanniktech.maven.publish.AndroidSingleVariantLibrary 118 | 119 | mavenPublishing { 120 | configure(new AndroidFusedLibrary()) 121 | } 122 | ``` 123 | 124 | === "build.gradle.kts" 125 | 126 | ```kotlin 127 | import com.vanniktech.maven.publish.AndroidFusedLibrary 128 | 129 | mavenPublishing { 130 | configure(AndroidFusedLibrary()) 131 | } 132 | ``` 133 | 134 | !!! warning 135 | 136 | Support for Android fused libraries is considered experimental and might break 137 | with future Android Gradle plugin updates. 138 | 139 | !!! warning 140 | 141 | Signing is currently unsupported for Android fused libraries. 142 | 143 | !!! info 144 | 145 | Configuring source and javadoc publishing is currently not possible and the 146 | plugin will always publush empty jars for them. 147 | 148 | 149 | ## Java Library 150 | 151 | For projects using the `java-library` plugin. 152 | 153 | === "build.gradle" 154 | 155 | ```groovy 156 | import com.vanniktech.maven.publish.JavaLibrary 157 | import com.vanniktech.maven.publish.JavadocJar 158 | 159 | mavenPublishing { 160 | // the first parameter configures the -javadoc artifact, possible values: 161 | // - `JavadocJar.None()` don't publish this artifact 162 | // - `JavadocJar.Empty()` publish an empty jar 163 | // - `JavadocJar.Javadoc()` to publish standard javadocs 164 | // the second whether to publish a sources jar 165 | configure(new JavaLibrary(new JavadocJar.Javadoc(), true)) 166 | } 167 | ``` 168 | 169 | === "build.gradle.kts" 170 | 171 | ```kotlin 172 | import com.vanniktech.maven.publish.JavaLibrary 173 | import com.vanniktech.maven.publish.JavadocJar 174 | 175 | mavenPublishing { 176 | configure(JavaLibrary( 177 | // configures the -javadoc artifact, possible values: 178 | // - `JavadocJar.None()` don't publish this artifact 179 | // - `JavadocJar.Empty()` publish an empty jar 180 | // - `JavadocJar.Javadoc()` to publish standard javadocs 181 | javadocJar = JavadocJar.Javadoc(), 182 | // whether to publish a sources jar 183 | sourcesJar = true, 184 | )) 185 | } 186 | ``` 187 | 188 | ## Kotlin JVM Library 189 | 190 | For projects using the `org.jetbrains.kotlin.jvm` plugin. 191 | 192 | === "build.gradle" 193 | 194 | ```groovy 195 | import com.vanniktech.maven.publish.KotlinJvm 196 | import com.vanniktech.maven.publish.JavadocJar 197 | 198 | mavenPublishing { 199 | // the first parameter configures the -javadoc artifact, possible values: 200 | // - `JavadocJar.None()` don't publish this artifact 201 | // - `JavadocJar.Empty()` publish an empty jar 202 | // - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input 203 | // the second whether to publish a sources jar 204 | configure(new KotlinJvm(new JavadocJar.Dokka("dokkaHtml"), true)) 205 | } 206 | ``` 207 | 208 | === "build.gradle.kts" 209 | 210 | ```kotlin 211 | import com.vanniktech.maven.publish.KotlinJvm 212 | import com.vanniktech.maven.publish.JavadocJar 213 | 214 | mavenPublishing { 215 | configure(KotlinJvm( 216 | // configures the -javadoc artifact, possible values: 217 | // - `JavadocJar.None()` don't publish this artifact 218 | // - `JavadocJar.Empty()` publish an empty jar 219 | // - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input 220 | javadocJar = JavadocJar.Dokka("dokkaHtml"), 221 | // whether to publish a sources jar 222 | sourcesJar = true, 223 | )) 224 | } 225 | ``` 226 | 227 | ## Kotlin Multiplatform Library 228 | 229 | For projects using the `org.jetbrains.kotlin.multiplatform` plugin. 230 | 231 | === "build.gradle" 232 | 233 | ```groovy 234 | import com.vanniktech.maven.publish.KotlinMultiplatform 235 | import com.vanniktech.maven.publish.JavadocJar 236 | 237 | mavenPublishing { 238 | // the parameter configures the -javadoc artifact, possible values: 239 | // - `JavadocJar.None()` don't publish this artifact 240 | // - `JavadocJar.Empty()` publish an empty jar 241 | // - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input 242 | // the second whether to publish a sources jar 243 | // the third parameters configures which Android library variants to publish if this project has an Android target 244 | // defaults to "release" when using the main plugin and nothing for the base plugin 245 | configure(new KotlinMultiplatform(new JavadocJar.Dokka("dokkaHtml"), true, ["debug", "release"])) 246 | } 247 | ``` 248 | 249 | === "build.gradle.kts" 250 | 251 | ```kotlin 252 | import com.vanniktech.maven.publish.KotlinMultiplatform 253 | import com.vanniktech.maven.publish.JavadocJar 254 | 255 | mavenPublishing { 256 | // sources publishing is always enabled by the Kotlin Multiplatform plugin 257 | configure(KotlinMultiplatform( 258 | // configures the -javadoc artifact, possible values: 259 | // - `JavadocJar.None()` don't publish this artifact 260 | // - `JavadocJar.Empty()` publish an empty jar 261 | // - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input 262 | javadocJar = JavadocJar.Dokka("dokkaHtml"), 263 | // whether to publish a sources jar 264 | sourcesJar = true, 265 | // configure which Android library variants to publish if this project has an Android target 266 | // defaults to "release" when using the main plugin and nothing for the base plugin 267 | androidVariantsToPublish = listOf("debug", "release"), 268 | )) 269 | } 270 | ``` 271 | 272 | !!! info 273 | 274 | The `com.android.kotlin.multiplatform.library` plugin does not need any special configuration. When using it 275 | leave out the third parameter (`androidVariantsToPublish`) of `KotlinMultiplatform`. 276 | 277 | ## Gradle Plugin 278 | 279 | For projects using the `java-gradle-plugin` plugin. When also using `com.gradle.plugin-publish` please 280 | use [GradlePublishPlugin](#gradle-publish-plugin) 281 | 282 | === "build.gradle" 283 | 284 | ```groovy 285 | import com.vanniktech.maven.publish.GradlePlugin 286 | import com.vanniktech.maven.publish.JavadocJar 287 | 288 | mavenPublishing { 289 | // the first parameter configures the -javadoc artifact, possible values: 290 | // - `JavadocJar.None()` don't publish this artifact 291 | // - `JavadocJar.Empty()` publish an empty jar 292 | // - `JavadocJar.Javadoc()` to publish standard javadocs 293 | // the second whether to publish a sources jar 294 | configure(new GradlePlugin(new JavadocJar.Javadoc(), true)) 295 | } 296 | ``` 297 | 298 | === "build.gradle.kts" 299 | 300 | ```kotlin 301 | import com.vanniktech.maven.publish.GradlePlugin 302 | import com.vanniktech.maven.publish.JavadocJar 303 | 304 | mavenPublishing { 305 | configure(GradlePlugin( 306 | // configures the -javadoc artifact, possible values: 307 | // - `JavadocJar.None()` don't publish this artifact 308 | // - `JavadocJar.Empty()` publish an empty jar 309 | // - `JavadocJar.Javadoc()` to publish standard javadocs 310 | // - `JavadocJar.Dokka("dokkaHtml")` when using Kotlin with Dokka, where `dokkaHtml` is the name of the Dokka task that should be used as input 311 | javadocJar = JavadocJar.Javadoc(), 312 | // whether to publish a sources jar 313 | sourcesJar = true, 314 | )) 315 | } 316 | ``` 317 | 318 | ## Gradle Publish Plugin 319 | 320 | For projects using the `com.gradle.plugin-publish` plugin. This will always publish a sources jar 321 | and a javadoc jar. 322 | 323 | === "build.gradle" 324 | 325 | ```groovy 326 | import com.vanniktech.maven.publish.GradlePublishPlugin 327 | 328 | mavenPublishing { 329 | configure(new GradlePublishPlugin()) 330 | } 331 | ``` 332 | 333 | === "build.gradle.kts" 334 | 335 | ```kotlin 336 | import com.vanniktech.maven.publish.GradlePublishPlugin 337 | 338 | mavenPublishing { 339 | configure(GradlePublishPlugin()) 340 | } 341 | ``` 342 | 343 | ## Java Platform 344 | 345 | 346 | For projects using the `java-platform` plugin. 347 | 348 | === "build.gradle" 349 | 350 | ```groovy 351 | import com.vanniktech.maven.publish.JavaPlatform 352 | 353 | mavenPublishing { 354 | configure(new JavaPlatform()) 355 | } 356 | ``` 357 | 358 | === "build.gradle.kts" 359 | 360 | ```kotlin 361 | import com.vanniktech.maven.publish.JavaPlatform 362 | 363 | mavenPublishing { 364 | configure(JavaPlatform()) 365 | } 366 | ``` 367 | 368 | 369 | ## Version Catalog 370 | 371 | 372 | For projects using the `version-catalog` plugin. 373 | 374 | === "build.gradle" 375 | 376 | ```groovy 377 | import com.vanniktech.maven.publish.VersionCatalog 378 | 379 | mavenPublishing { 380 | configure(new VersionCatalog()) 381 | } 382 | ``` 383 | 384 | === "build.gradle.kts" 385 | 386 | ```kotlin 387 | import com.vanniktech.maven.publish.VersionCatalog 388 | 389 | mavenPublishing { 390 | configure(VersionCatalog()) 391 | } 392 | ``` 393 | --------------------------------------------------------------------------------