├── .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 |
--------------------------------------------------------------------------------