Gradle Play Publisher is a Gradle plugin that allows you to upload your App Bundle or APK and other app details to the Google Play Store.
- Quickstart guide
- Prerequisites
- Basic setup
- Task organization
- Managing artifacts
- Managing Play Store metadata
- Advanced topics
- Upload the first version of your APK or App Bundle using the Google Play Console
- Create a Google Play Service Account
- Sign your release builds
with a valid
signingConfig
- Add and apply the plugin
- Authenticate GPP
The first APK or App Bundle needs to be uploaded via the Google Play Console because registering the app with the Play Store cannot be done using the Play Developer API. For all subsequent uploads and changes, GPP may be used.
If no publishing tasks were created, you most likely haven't added a valid signing configuration to your release builds. Be sure to add one.
To use GPP, you must create a service account with access to the Play Developer API. You'll find a guide to setting up the service account here. Once that's done, you'll need to grant at least the following permissions to your service account for GPP to work (go to Settings -> Developer account -> Users & permissions):
Apply the plugin to each individual com.android.application
module where you want to use GPP
through the plugins {}
DSL:
Kotlin
plugins {
id("com.android.application")
id("com.github.triplet.play") version "2.0.0"
}
Groovy
plugins {
id 'com.android.application'
id 'com.github.triplet.play' version '2.0.0'
}
If you're prepared to cut yourself on the bleeding edge of GPP development, snapshot builds are
available from
Sonatype's snapshots
repository:
Kotlin
buildscript {
repositories {
// ...
maven("https://oss.sonatype.org/content/repositories/snapshots")
}
dependencies {
// ...
classpath("com.github.triplet.gradle:play-publisher:2.1.0-SNAPSHOT")
}
}
Groovy
buildscript {
repositories {
// ...
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
dependencies {
// ...
classpath 'com.github.triplet.gradle:play-publisher:2.1.0-SNAPSHOT'
}
}
After you've gone through the Service Account setup, you should have a JSON file
with your private key. Add a play
block alongside your android
one with the file's location:
android { ... }
play {
serviceAccountCredentials = file("your-key.json")
}
play {
serviceAccountEmail = "service-account-name@project-id.iam.gserviceaccount.com"
serviceAccountCredentials = file("your-key.p12")
}
GPP follows the Android Gradle Plugin's naming convention: [action][Variant][Thing]
. For example,
publishPaidReleaseBundle
will be generated if have a paid
product flavor.
Lifecycle tasks to publish multiple product flavors at once are also available. For example,
publishBundle
publishes all variants.
To find available tasks, run ./gradlew tasks
and look under the publishing section.
GPP supports uploading both the App Bundle and APK. Once uploaded, GPP also supports promoting those artifacts.
Several options are available to customize how your artifacts are published:
track
is the target stage for an artifact, i.e. alpha/beta/prodreleaseStatus
is the type of release, i.e. draft/completed/in progressuserFraction
is the percentage of users who will received a staged release
Example configuration:
play {
// ...
track = "production"
userFraction = 0.5
releaseStatus = "inProgress"
}
While GPP can automatically build and find your artifact, you'll need to tell the plugin about your release notes.
Add a file under src/[sourceSet]/play/release-notes/[language]/[track].txt
where sourceSet
is a full variant name,
language
is one of the
Play Store supported codes,
and track
is the channel you want these release notes to apply to (or default
if unspecified).
As an example, let's assume you have these two different release notes:
src/main/play/release-notes/en-US/default.txt
.../beta.txt
When you publish to the beta channel, the beta.txt
release notes will be uploaded. For any other
channel, default.txt
will be uploaded.
Note: the Play Store limits your release notes to a maximum of 500 characters.
Run ./gradlew publishBundle
.
You'll notice that if you run ./gradlew publish
, it uploads an APK by default. To change this,
default to the App Bundle:
play {
// ...
defaultToAppBundles = true
}
Run ./gradlew publishApk
. Splits will be uploaded if available.
Existing releases can be promoted and/or updated to the configured track
with ./gradlew promoteArtifact
.
By default, the track from which to promote a release will be determined by the most unstable
channel that contains a release. Example: if the alpha channel has no releases, but the beta and
prod channels do, the beta channel will be picked. To configure this manually, use the fromTrack
property:
play {
// ...
fromTrack = "alpha"
}
If an artifact already exists with a version code greater than or equal to the one you're trying to upload, an error will be thrown when attempting to publish the new artifact. You have two options:
- Ignore the error and continue (
ignore
) - Automatically pick the correct version code so you don't have to manually update it (
auto
)
Example configuration:
play {
// ...
resolutionStrategy = "ignore"
}
For example, you could update you app's version name based on the new version code:
play {
// ...
resolutionStrategy = "auto"
outputProcessor { // this: ApkVariantOutput
versionNameOverride = "$versionNameOverride.$versionCode"
}
}
GPP supports uploading any metadata you might want to change with each release, from screenshots and descriptions to in-app purchases and subscriptions.
GPP includes a bootstrap task that pulls down your existing listing and initializes everything for
you. To use it, run ./gradlew bootstrap
.
GPP follows the Android Gradle Plugin's source set
guidelines and priorities.
src/[sourceSet]/play
is the base directory for Play Store metadata. Since main
is the most
common source set, it will be assumed in all following examples.
In addition to merging metadata across variants, GPP merges translations. That is, if a resources is
provided in a default language such as en-US
but not in fr-FR
, the resource will be copied over
when uploading French metadata.
Run ./gradlew publishListing
.
Base directory: play
File | Description |
---|---|
contact-email.txt |
Developer email |
contact-phone.txt |
Developer phone |
contact-website.txt |
Developer website |
default-language.txt |
The default language for both your Play Store listing and translation merging as described above |
Base directory: play/listings/[language]
where language
is one of the
Play Store supported codes
File | Description | Character limit |
---|---|---|
title.txt |
App title | 50 |
short-description.txt |
Tagline | 80 |
full-description.txt |
Full description | 4000 |
video-url.txt |
Youtube product video | N/A |
Directory: play/listings/[language]/graphics
where language
is defined as in the previous
section
Image files are organized a bit differently than in previous sections. Instead of the file name, the
parent directory's name is used as the media type. This is because multiple images may be provided
for the same media type. While file names are arbitrary, they will be uploaded in alphabetical order
and presented on the Play Store as such. Therefore, we recommend using a number as the file name
(1.png
for example). Both PNG and JPEG images are supported.
Directory | Max # of images |
---|---|
icon |
1 |
feature-graphic |
1 |
promo-graphic |
1 |
phone-screenshots |
8 |
tablet-screenshots |
8 |
large-tablet-screenshots |
8 |
tv-banner |
1 |
tv-screenshots |
8 |
wear-screenshots |
8 |
Run ./gradlew publishProducts
.
Manually setting up in-app purchase files is not recommended. Bootstrap them instead
with ./gradlew bootstrap --products
.
All configuration options available in the play
block are also available as CLI options so you
don't have to update your build file when making one-time changes. For example, to configure
play.track
on demand, use the --track
option. camelCase
options are converted to
kebab-case
ones.
To get a list of options and their quick documentation, use ./gradlew help --task [task]
where
task
is something like publishBundle
.
If you commit unencrypted Service Account keys to source, you run the risk of letting anyone access your Google Play account. To circumvent this issue, many CI servers support encrypting files while keeping fake versions in public source control. Here is a set of common fake files you might need and ways to encrypt your real keys for a few common CI servers:
If you need to publish each build flavor to a separate Play Store account, GPP supports flavor
specific play
configurations through the playConfigs
block:
Kotlin
android {
// ...
flavorDimensions("customer", "version")
productFlavors {
register("firstCustomer") {
setDimension("customer")
// ...
}
register("secondCustomer") {
setDimension("customer")
// ...
}
register("demo") {
setDimension("version")
// ...
}
register("full") {
setDimension("version")
// ...
}
}
playConfigs {
register("firstCustomer") {
serviceAccountCredentials = file("customer-one-key.json")
}
register("secondCustomer") {
serviceAccountCredentials = file("customer-two-key.json")
}
}
}
play {
// Defaults
}
Groovy
android {
// ...
flavorDimensions 'customer', 'version'
productFlavors {
firstCustomer {
dimension 'customer'
// ...
}
secondCustomer {
dimension 'customer'
// ...
}
demo {
dimension 'version'
// ...
}
full {
dimension 'version'
// ...
}
}
playConfigs {
firstCustomer {
serviceAccountCredentials = file('customer-one-key.json')
}
secondCustomer {
serviceAccountCredentials = file('customer-two-key.json')
}
}
}
play {
// Defaults
}