How do I publish my plugin to the Plugin Portal?

Note: if you are using Gradle 8 (or newer) or you are using version 1.0 of the plugin (or newer), please switch to the newer version of this page.

Note: if you have previously published Gradle plugins via Bintray, please read this information.

Portal Setup

Create an account.

You can quickly and easily create a new account via the registration page.

You can use the same account to publish as many plugins as you like.

Create an API key.

Once logged in, you can get your API key via the “API Keys” tab of your profile page.

You can use the same API key to publish as many plugins as you like.

Local Setup

Once you've created an API key, the “API Keys” tab of your profile page will provide a snippet that you can copy & paste into your Gradle user properties file.

The default location for the Gradle user properties file is $USER_HOME/.gradle/gradle.properties, where $USER_HOME refers to your user home directory.

NOTE: There is also an experimental task, “login“, that is part of the plugin publishing plugin, which automates this step.

Publication

The Plugin Publishing plugin provides tasks that upload your plugin to the Plugin Portal. It can also be used to publish updated versions of your plugin in the future. This plugin is not part of the Gradle distribution but is itself hosted on the Plugin Portal.

Once configured, your plugin can be published by simply running the publishPlugins task of your plugin's build. Detailed instructions can be found in the Gradle docs for plugin publishing. Take care to select the version matching the actual Gradle version you are using, but keep in mind that starting from v7.6 the Gradle manual focuses on more modern versions of the plugin, ie. 1.0 or higher.

Metadata

Historically there have been multiple ways of generating the publication metadata (think Maven GAV coordinates, i.e. group, artifact, version) for Gradle plugins:

  • the Plugin Publish Plugin has a default way of computing the GAV from the plugin ID and version
  • a special mavenCoordinates block can be used to override the above-mentioned defaults
  • the whole process can be delegated to the Maven Publish Plugin

In future versions of the Plugin Publish Plugin, delegating the publication metadata generation to the Maven Publish Plugin will be the preferred (and enforced) option, so we will focus on it for the purposes of this documentation and its examples.

Example

build.gradle.kts (Kotlin)

// First, apply the publishing plugin
plugins {
  // Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins
  `java-gradle-plugin`
  // Alternatively apply other plugins, e.g. the kotlin plugin for a plugin written in Kotlin
  // or the groovy plugin if the plugin uses Groovy

  // Apply the Plugin Publish Plugin to make plugin publication possible
  id "com.gradle.plugin-publish" version "1.2.1"

  // Apply the Maven Publish Plugin to have it generate the publication metadata
  `maven-publish`
}

// If your plugin has any external dependencies, users might need to declare repositories in their
// build settings so that Gradle can download them when using the plugins DSL
repositories {
  mavenCentral()
}

// The project version will be used as your plugin version when publishing
version = "1.2"
group = "io.github.johndoe"

// Use java-gradle-plugin to generate plugin descriptors and specify plugin ids
gradlePlugin {
  plugins {
    create("greetingsPlugin") {
      id = "io.github.johndoe.greeting"
      implementationClass = "io.github.johndoe.gradle.GreetingPlugin"
    }
    create("goodbyePlugin") {
      id = "io.github.johndoe.goodbye"
      implementationClass = "io.github.johndoe.gradle.GoodbyePlugin"
    }
  }
}

pluginBundle {
  // These settings are set for the whole plugin bundle
  website = 'https://github.com/johndoe/GradlePlugins'
  vcsUrl = 'https://github.com/johndoe/GradlePlugins'

  // tags and description can be set for the whole bundle here, but can also
  // be set / overridden in the config for specific plugins
  description = "Greetings from here!"

  // The plugins block can contain multiple plugin entries. The name for
  // each plugin block below (greetingsPlugin, goodbyePlugin)does not
  // affect the plugin configuration, but they need to be unique for each plugin.

  // Plugin config blocks can set the displayName, description and tags for
  // each plugin. displayName is mandatory. If no tags or description are set
  // the tags or description from the pluginBundle block will be used,
  // but they must be set in one of the two places.

  (plugins) {
    "greetingsPlugin" {
      // id is captured from java-gradle-plugin configuration
      displayName = "Gradle Greeting plugin"
      tags = listOf("individual", "tags", "per", "plugin")
      version = "1.2"
    }

    "goodbyePlugin" {
      // id is captured from java-gradle-plugin configuration
      displayName = "Gradle Goodbye plugin"
      description = "Override description for this plugin"
      tags = listOf("different", "for", "this", "one")
      version = "1.3"
    }
  }
}

build.gradle (Groovy)

// First, apply the publishing plugin
plugins {
  // Apply the Java Gradle plugin development plugin to add support for developing Gradle plugins
  id 'java-gradle-plugin'
  // Alternatively apply other plugins, e.g. the kotlin plugin for a plugin written in Kotlin
  // or the groovy plugin if the plugin uses Groovy

  // Apply the Plugin Publish Plugin to make plugin publication possible
  id "com.gradle.plugin-publish" version "1.2.1"

  // Apply the Maven Publish Plugin to have it generate the publication metadata
  id 'maven-publish'
}

// If your plugin has any external dependencies, users might need to declare repositories in their
// build settings so that Gradle can download them when using the plugins DSL
repositories {
  mavenCentral()
}

// The project version will be used as your plugin version when publishing
version = '1.2'
group = 'io.github.johndoe'

// Use java-gradle-plugin to generate plugin descriptors and specify plugin ids
gradlePlugin {
  plugins {
    greetingsPlugin {
      id = 'io.github.johndoe.greeting'
      implementationClass = 'io.github.johndoe.gradle.GreetingPlugin'
    }
    goodbyePlugin {
      id = 'io.github.johndoe.goodbye'
      implementationClass = 'io.github.johndoe.gradle.GoodbyePlugin'
    }
  }
}

pluginBundle {
  // These settings are set for the whole plugin bundle
  website = 'https://github.com/johndoe/GradlePlugins'
  vcsUrl = 'https://github.com/johndoe/GradlePlugins'

  // tags and description can be set for the whole bundle here, but can also
  // be set / overridden in the config for specific plugins
  description = 'Greetings from here!'

  // The plugins block can contain multiple plugin entries. The name for
  // each plugin block below (greetingsPlugin, goodbyePlugin)does not
  // affect the plugin configuration, but they need to be unique for each plugin.

  // Plugin config blocks can set the displayName, description and tags for
  // each plugin. displayName is mandatory. If no tags or description are set
  // the tags or description from the pluginBundle block will be used,
  // but they must be set in one of the two places.

  plugins {
    greetingsPlugin {
      // id is captured from java-gradle-plugin configuration
      displayName = 'Gradle Greeting plugin'
      tags = ['individual', 'tags', 'per', 'plugin']
    }

    goodbyePlugin {
      // id is captured from java-gradle-plugin configuration
      displayName = 'Gradle Goodbye plugin'
      description = 'Override description for this plugin'
      tags = ['different', 'for', 'this', 'one']
    }
  }
}

Approval

When a new plugin is published it will go through a manual review process. This usually happens only for the initial version. A Gradle engineer will inspect the metadata and decide whether to make it visible on the Portal or request changes to it. Note that any change of Maven group or plugin ID will cause the approval process to be triggered again.

All plugins must adhere to the following rules:

Plugins must have some functionality

"Hello world" type submissions, or any other plugin not having some minimal useful functionality will be rejected.

Plugins should be documented

There should be a description specifying the intent of the plugin.

Tags should be set to describe the categories the plugin covers.

The project URL should point to the documentation, to the project sources, or both.

The VCL URL should point to the plugin sources. Plugins should ideally be open-source, unless there is a good reason to do otherwise. Proprietary plugin authors should reach out to us.

Plugin IDs should trace back to the author

If a GitHub user johndoe publishes a plugin called myplugin, then an acceptable plugin ID would be io.github.johndoe.myplugin. Note:com.github is no longer accepted as a prefix, in order to align with the Maven Central policy.

Another example would be a corporate plugin, let's say from mybusiness.com; then the plugin ID should look like com.mybusiness.pluginname. In this case, the author should also submit the plugin via a corporate account/email address. We prefer group (as opposed to individual) addresses, to highlight that the organization (or a sub-group of it) is the owner of the plugin.

If we cannot link to the domain, we will ask for proof, in the form of a TXT record added to the DNS entry.

Only final versions should be published

We will reject SNAPSHOT versions because it's not possible to override them anyways.

Plugins should be published from non-forked repositories

There shouldn't be multiple variants of the same plugin published on the Portal. Plugin developers should try to work with the original author(s) to get their changes/improvements integrated. Some exceptions are acceptable, for example when the original repository is abandoned, and it doesn't accept contributions.

Troubleshooting

Publication Failed

The problems described here happen during plugin publication and usually lead to the failure of the publication process.

Group ID can't start with gradle.plugin

The most likely scenario when this publication rejection happens is when the Plugin Publish Plugin is being used without relying on the Maven Publish Plugin for generating the publication metadata. In this scenario the Plugin Publish Plugin is itself adding this prefix to the M2 GAV of the plugin and the server no longer accepts it.

The preferred solution is to simply include the maven-publish plugin, as presented in the examples.

If for some reason you can't use this solution, then your other option is to use a mavenCoordinates block inside of your pluginBundle block to override the M2 GAV values, like so:

pluginBundle {
  mavenCoordinates {
    groupId = "org.myorg"
    artifactId = "greeting-plugins"
    version = "1.4"
  }
}

The GitHub group ID prefix can't be com.github

Github has introduced a new, dedicated domain (github.io) for it’s Pages service, back in 2013 (see here). This year they have stopped automatically forwarding from the old, legacy domain (github.com) to this new one.

Maven Central has then announced that they will react to this and that they will no longer allow com.github as a valid group ID prefix (see here).

The Gradle Plugin Portal has also adopted this convention, so please use the io.github prefix in your plugin configurations.

Plugin version can't be SNAPSHOT

The Plugin Portal doesn't allow plugins to be published with versions ending in SNAPSHOT, because they wouldn't behave as expected. All versions are considered fixed, so re-publishing of the same version wouldn't be possible, thus the whole concept of snapshots would be meaningless.

Please use fixed versions instead.

you have different artifacts with the same hash

A bug in the plugin portal caused it to incorrectly process publications with hash collisions between their artifacts. The fix required a protocol change and so using version 1.0+ is mandatory to fix this problem. Please upgrade.

Approval Denied

The problems described here happen after the successful publication of the first version of a plugin, if the plugin gets flagged for manual approval by the Portal administrators, and they reject the plugin for various reasons. They will send you an email with the reasons for the rejection.

Ownership can't be established

One typical scenario of this problem is when a plugin gets published from a GitHub (or similar) repository, but the GitHub account can't be linked to the Plugin Portal account doing the publishing. The solution to this is to either use a Plugin Portal account linked to a GitHub account (you can log into the Plugin Portal via your GitHub account) or to make the email address backing your Plugin Portal account publicly visible on your GitHub account.

Another typical scenario is when a plugin belonging to an organization is being published, but the publishing Plugin Portal account can't be linked to the organization. In such cases you might be asked to prove ownership of the organization's domain by adding some random TXT DNS record to your organization's domain host. Portal administrators will contact you and provide you with the exact record.

Plugin is based on a forked repository

Having multiple variants of the same plugin, published from forked, potentially diverging repositories could lead to a lot of confusion, so it's generally not encouraged. If you find yourself in this situation, we encourage you to submit your contributions as Pull Requests to the original repository.

The exception to this rule are abandoned repositories, where indeed forking and taking ownership is the only option.

Plugin is lacking public documentation

One typical situation when this problem arises is when the website or VCS URLs provided during plugin publication point to a broken or inaccessible destination. Another one is when the URLs point to, for example a public GitHub repository, but there is no documentation there \ (empty README file).

The problem with the lack of documentation is that it makes the adoption of the plugin by the community very unlikely.

The solution to this problem is to either make the documentation publicly available, or, if your plugin is indeed private, consider publishing it to a private artifact repository.

You're done!

Your plugin is now part of the Gradle Plugin Portal and can be put to use by Gradle users all over the world.

Thank you for contributing.

All plugins require manual acceptance by the Plugin Portal maintainers, and will not be made available immediately when they are published. This is only the case for the first version of a new plugin. The plugin publishing plugin will tell you if your plugin is pending acceptance when you publish.

If your plugin requires changes before it can be accepted, you will be contacted via the email address associated with your account. You will also receive an email when your plugin is accepted.