Coder Social home page Coder Social logo

triple-t / gradle-play-publisher Goto Github PK

View Code? Open in Web Editor NEW
4.1K 4.1K 338.0 4.03 MB

GPP is Android's unofficial release automation Gradle Plugin. It can do anything from building, uploading, and then promoting your App Bundle or APK to publishing app listings and other metadata.

License: MIT License

Kotlin 99.91% HTML 0.09%
android android-development apps automation deployment gpp gradle-play-publisher gradle-plugin mobile play-store publishing

gradle-play-publisher's Introduction

Logo

Gradle Play Publisher

Gradle Play Publisher (GPP) is Android's unofficial release automation Gradle Plugin. It can do anything from building, uploading, and then promoting your App Bundle or APK to publishing app listings and other metadata.

Table of contents

  1. Quickstart guide
  2. Prerequisites
    1. Initial Play Store upload
    2. Signing configuration
    3. Service Account
  3. Basic setup
    1. Installation
    2. Authenticating Gradle Play Publisher
  4. Task organization
  5. Managing artifacts
    1. Common configuration
    2. Publishing an App Bundle
    3. Publishing APKs
    4. Uploading an Internal Sharing artifact
    5. Promoting artifacts
    6. Handling version conflicts
  6. Managing Play Store metadata
    1. Quickstart
    2. Directory structure
    3. Publishing listings
    4. Publishing in-app products
  7. Working with product flavors
    1. Disabling publishing
    2. Combining artifacts into a single release
    3. Using multiple Service Accounts
  8. Advanced topics
    1. Using CLI options
    2. Using HTTPS proxies

Quickstart guide

  1. Upload the first version of your APK or App Bundle using the Google Play Console
  2. Create a Google Play Service Account
  3. Sign your release builds with a valid signingConfig
  4. Add and apply the plugin
  5. Authenticate GPP

Prerequisites

Initial Play Store upload

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.

Signing configuration

To successfully upload apps to the Play Store, they must be signed with your developer key. Make sure you have a valid signing configuration.

Service Account

To use GPP, you must create a service account with access to the Play Developer API:

  1. If you don't already have one, create a GCP project for your app(s)
  2. Enable the AndroidPublisher API for that GCP project
  3. Create a service account and key
    1. Make sure you're in the GCP project you used above (check the project query param in the URL)
    2. Select New service account
    3. Give it a name and the Project Owner role (don't worry, we'll remove this later)
    4. After creating the service account, find it in the list of all service accounts and use the 3 dots menu to Manage keys
    5. From there, create a new key using the Add key menu (leave JSON selected)
  4. Move the downloaded JSON credentials into your project and tell GPP about it
  5. Give your service account permissions to publish apps on your behalf
    1. Click Invite new user
    2. Copy/paste the service account email (you can find it in the JSON credentials)
    3. Don't touch the roles
    4. Specify which apps the service account should have access to. In this example, GPP has full access to testing tracks and app listings, but will be unable to make production releases: Minimum Service Account permissions
  6. Run ./gradlew bootstrapListing or some other GPP task to validate your setup
  7. Now that you've successfully created the connection between GCP and Google Play, you can remove the Project Owner permissions
    1. Go to your IAM settings
    2. Search for the service account you created
    3. Click the edit icon (found at the end of the row)
    4. In the permission selection panel that opens, click the trash icon to remove the owner role
    5. Click save

Basic setup

Installation

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 "3.9.1"
}
Groovy
plugins {
    id 'com.android.application'
    id 'com.github.triplet.play' version '3.9.1'
}

Snapshot builds

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:4.0.0-SNAPSHOT")
    }
}
Groovy
buildscript {
    repositories {
        // ...
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }

    dependencies {
        // ...
        classpath 'com.github.triplet.gradle:play-publisher:4.0.0-SNAPSHOT'
    }
}

Authenticating Gradle Play Publisher

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.set(file("your-key.json"))
}

Note: 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, put the contents of your JSON file in the ANDROID_PUBLISHER_CREDENTIALS environment variable and don't specify the serviceAccountCredentials property.

Task organization

GPP follows the Android Gradle Plugin's (AGP) naming convention: [action][Variant][Thing]. For example, publishPaidReleaseBundle will be generated if you 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 --group publishing and use ./gradlew help --task [task] where task is something like publishBundle to get more detailed documentation for a specific task.

Managing artifacts

GPP supports uploading both the App Bundle and APK. Once uploaded, GPP also supports promoting those artifacts to different tracks.

Common configuration

Several options are available to customize how your artifacts are published:

  • track is the target stage for an artifact, i.e. internal/alpha/beta/production or any custom track
    • Defaults to internal
  • releaseStatus is the type of release, i.e. ReleaseStatus.[COMPLETED/DRAFT/HALTED/IN_PROGRESS]
    • Defaults to ReleaseStatus.COMPLETED
  • userFraction is the percentage of users who will receive a staged release
    • Defaults to 0.1 aka 10%
    • Note: the userFraction is only applicable where releaseStatus=[IN_PROGRESS/HALTED]
  • updatePriority sets the update priority for a new release. See Google's documentation on consuming this value.
    • Defaults to the API value

Example configuration:

import com.github.triplet.gradle.androidpublisher.ReleaseStatus

play {
    // Overrides defaults
    track.set("production")
    userFraction.set(0.5)
    updatePriority.set(2)
    releaseStatus.set(ReleaseStatus.IN_PROGRESS)

    // ...
}

Uploading release notes

While GPP can automatically build and find your artifact, you'll need to tell the plugin where to find 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.

If no release notes are found, GPP will try to copy release notes from the previous release.

Note: the Play Store limits your release notes to a maximum of 500 characters.

Uploading developer facing release names

The Play Console supports customizing release names. These aren't visible to users, but may be useful for internal processes. Similar to release notes, release names may be specified by placing a [track].txt file in the release-names directory under your play folder. For example, here's a custom release name for the alpha track in the play/release-names/alpha.txt file:

My custom release name

If it makes more sense to specify the release name in your build script, the releaseName property is available:

play {
    // ...
    releaseName.set("My custom release name")
}

Note: the play.releaseName property takes precedence over the resource files.

There is also a --release-name CLI option for quick access. For example, ./gradlew publishBundle --release-name "Hello World!".

Note: the Play Store limits your release names to a maximum of 50 characters.

Uploading a pre-existing artifact

By default, GPP will build your artifact from source. In advanced use cases, this might not be the desired behavior. For example, if you need to inject translations into your APK or App Bundle after building it but before publishing it. Or perhaps you simply already have an artifact you wish to publish. GPP supports this class of use cases by letting you specify a directory in which publishable artifacts may be found:

play {
    // ...
    artifactDir.set(file("path/to/apk-or-app-bundle/dir"))
}

For quick access, you can also use the --artifact-dir CLI option:

./gradlew publishBundle --artifact-dir path/to/app-bundle/dir

Note: all artifacts in the specified directory will be published.

Uploading mapping files

Note: mapping files aren't applicable to App Bundles since the mapping file is contained within the bundle.

By default, GPP will look for a file called mapping.txt in your artifact directory. If you need more granularity, you can prefix mapping.txt with your APK file name. For example:

artifact-dir/
├── mapping.txt
├── my-first-app.apk
├── my-second-app.apk
└── my-second-app.mapping.txt

my-second-app.apk will use my-second-app.mapping.txt and my-first-app.apk will use the default mapping.txt because no specific mapping file was specified.

Retaining artifacts

GPP supports keeping around old artifacts such as OBB files or WearOS APKs:

play {
    // ...
    retain {
        artifacts.set(listOf(123)) // Old APK version code
        mainObb.set(123) // Old main OBB version code
        patchObb.set(123) // Old patch OBB version code
    }
}

Publishing an App Bundle

Run ./gradlew publishBundle.

Defaulting to the App Bundle

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.set(true)
}

Publishing APKs

Run ./gradlew publishApk. Splits will be uploaded if available.

Uploading an Internal Sharing artifact

Run ./gradlew uploadReleasePrivateBundle for App Bundles and ./gradlew uploadReleasePrivateApk for APKs. To upload an existing artifact, read about how to do so.

Retrieving the download URL

After running an Internal Sharing task, the output of the API response will be stored in the following directory: build/outputs/internal-sharing/[bundle/apk]/[variant]/. Each file will be named [apk/aab name].json.

For example, here are the contents of app/build/outputs/internal-sharing/bundle/release/app-release.json:

{
    "certificateFingerprint": "...",
    "downloadUrl": "...",
    "sha256": "..."
}

Installing Internal Sharing artifacts

To accelerate development, GPP supports uploading and then immediately installing Internal Sharing artifacts. This is similar to the AGP's install[Variant] task.

Run ./gradlew installReleasePrivateArtifact to install an artifact built on-the-fly and ./gradlew uploadReleasePrivateBundle --artifact-dir path/to/artifact installReleasePrivateArtifact to install an existing artifact.

Promoting artifacts

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 is 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.set("alpha")
}

Similarly, the track to which to promote a release defaults to the promoteTrack property. If unspecified, the resolved fromTrack property will be used instead and an in-place update will be performed. Example configuration:

play {
    // ...
    promoteTrack.set("beta")
}

If you need to execute a one-time promotion, you can use the CLI args. For example, this is how you would promote an artifact from the alpha ➡️ beta track with only 25% of users getting the release:

./gradlew promoteArtifact \
  --from-track alpha --promote-track beta \
  --release-status inProgress --user-fraction .25

If you only need to update the rollout percentage of an existing in-progress release, you can do so with the update flag. For example, this is how you would increase the production track rollout percentage to 50% of users.

./gradlew promoteArtifact --update production --user-fraction .5

Finishing a rollout

If you have an ongoing inProgress release and would like to perform a full rollout, simply change the release status to completed. A user fraction of 1.0 is invalid and will be rejected.

Handling version conflicts

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 (ResolutionStrategy.IGNORE)
  • Automatically pick the correct version code so you don't have to manually update it (ResolutionStrategy.AUTO)

Example configuration:

import com.github.triplet.gradle.androidpublisher.ResolutionStrategy

play {
    // ...
    resolutionStrategy.set(ResolutionStrategy.IGNORE)
}

Post-processing outputs sanitized by auto resolution

For example, you could update your app's version name based on the new version code:

import com.github.triplet.gradle.androidpublisher.ResolutionStrategy

play {
    // ...
    resolutionStrategy.set(ResolutionStrategy.AUTO)
}

androidComponents {
    onVariants { variant ->
        for (output in variant.outputs) {
            val processedVersionCode = output.versionCode.map { playVersionCode ->
                // Do something to the version code...
                // In this example, version names will look like `myCustomVersionName.123`
                "myCustomVersionName.$playVersionCode"
            }

            output.versionName.set(processedVersionCode)
        }
    }
}

Managing Play Store metadata

GPP supports uploading any metadata you might want to change with each release, from screenshots and descriptions to in-app purchases and subscriptions.

Quickstart

GPP includes a bootstrap task that pulls down your existing listing and initializes everything for you. To use it, run ./gradlew bootstrapListing.

Note: if you have a pre-existing play folder, it will be reset.

Directory structure

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.

Publishing listings

Run ./gradlew publishListing.

Uploading global app metadata

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

Uploading text based listings

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

Uploading graphic based listings

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 Image dimension constraints (px)
icon 1 512x512
feature-graphic 1 1024x500
phone-screenshots 8 [320..3840]x[320..3840]
tablet-screenshots 8 [320..3840]x[320..3840]
large-tablet-screenshots 8 [320..3840]x[320..3840]
tv-banner 1 1280x720
tv-screenshots 8 [320..3840]x[320..3840]
wear-screenshots 8 [320..3840]x[320..3840]

Publishing in-app products

Run ./gradlew publishProducts.

Manually setting up in-app purchase files is not recommended. Bootstrap them instead with ./gradlew bootstrapListing --products.

Working with product flavors

When working with product flavors, granular configuration is key. GPP provides varying levels of granularity to best support your needs, all through the playConfigs block:

Kotlin
play {
    // In a simple app, this play block is all you'll need. However, in an app with product flavors,
    // the play block becomes a place to store default configurations. Anything configured in here
    // will apply to all product flavors, that is, unless an override is supplied in the playConfigs
    // block.
}

android {
    // Suppose we have the following flavors
    flavorDimensions("customer", "type")
    productFlavors {
        register("firstCustomer") { setDimension("customer") }
        register("secondCustomer") { setDimension("customer") }

        register("demo") { setDimension("type") }
        register("full") { setDimension("type") }
    }

    playConfigs {
        // Now, we can configure GPP however precisely is required.

        // Configuration overrides occur in a cascading manner from most to least specific. That is,
        // a property configured in a build type + flavor combo overrides that same property
        // configured in a flavor combo, which overrides a build type combo, which in turn overrides
        // the play block. Properties not configured are inherited.
        register("firstCustomerFullRelease") { ... } // Build type + flavor
        register("firstCustomer") { ... } // Flavor
        register("release") { ... } // Build type
    }
}
Groovy
play {
    // In a simple app, this play block is all you'll need. However, in an app with product flavors,
    // the play block becomes a place to store default configurations. Anything configured in here
    // will apply to all product flavors, that is, unless an override is supplied in the playConfigs
    // block.
}

android {
    // Suppose we have the following flavors
    flavorDimensions 'customer', 'type'
    productFlavors {
        firstCustomer { dimension 'customer' }
        secondCustomer { dimension 'customer' }

        demo { dimension 'type' }
        full { dimension 'type' }
    }

    playConfigs {
        // Now, we can configure GPP however precisely is required.

        // Configuration overrides occur in a cascading manner from most to least specific. That is,
        // a property configured in a build type + flavor combo overrides that same property
        // configured in a flavor combo, which overrides a build type combo, which in turn overrides
        // the play block. Properties not configured are inherited.
        firstCustomerFullRelease { ... } // Build type + flavor
        firstCustomer { ... } // Flavor
        release { ... } // Build type
    }
}

Disabling publishing

Sometimes, you may not want to publish all variants of your app. Or maybe you don't want publishing enabled on CI or local dev machines. Whatever the case may be, GPP can be disabled with the enabled property:

Kotlin
android {
    // ...

    playConfigs {
        register("myCustomVariantOrProductFlavor") {
            enabled.set(true)
        }

        // ...
    }
}

play {
    enabled.set(false) // This disables GPP by default. It could be the other way around.
    // ...
}
Groovy
android {
    // ...

    playConfigs {
        myCustomVariantOrProductFlavor {
            enabled.set(true)
        }

        // ...
    }
}

play {
    enabled.set(false) // This disables GPP by default. It could be the other way around.
    // ...
}

Combining artifacts into a single release

By default, GPP assumes every product flavor consists of a separate, independent app. To tell GPP this isn't the case, you must use the commit property:

Kotlin
// Don't commit any changes by default
play.commit.set(false)

android {
    // ...

    playConfigs {
        register("someFlavorThatWillCommit") {
            // Pick a flavor that will commit the changes prepared by all flavors
            commit.set(true)
        }
    }
}

afterEvaluate {
    // Now make sure the tasks execute in the right order
    val intermediateTasks = listOf(
            "publishSomeFlavor2Release[Apk/Bundle]",
            "publishSomeFlavor3Release[Apk/Bundle]",
            ...
    )

    // The commit flavor task must run last so the other tasks can build up changes to commit
    tasks.named("someFlavorThatWillCommitRelease[Apk/Bundle]").configure {
        mustRunAfter(intermediateTasks)
    }
}
Groovy
// Don't commit any changes by default
play.commit.set(false)

android {
    // ...

    playConfigs {
        someFlavorThatWillCommit {
            // Pick a flavor that will commit the changes prepared by all flavors
            commit.set(true)
        }
    }
}

afterEvaluate {
    // Now make sure the tasks execute in the right order
    def intermediateTasks = [
            "publishSomeFlavor2Release[Apk/Bundle]",
            "publishSomeFlavor3Release[Apk/Bundle]",
            ...
    ]

    // The commit flavor task must run last so the other tasks can build up changes to commit
    tasks.named("someFlavorThatWillCommitRelease[Apk/Bundle]").configure {
        mustRunAfter(intermediateTasks)
    }
}

Using multiple Service Accounts

If you need to publish each build flavor to a separate Play Store account, simply provide separate credentials per product flavor.

Kotlin
android {
    // ...

    playConfigs {
        register("firstCustomer") {
            serviceAccountCredentials.set(file("customer-one-key.json"))
        }

        register("secondCustomer") {
            serviceAccountCredentials.set(file("customer-two-key.json"))
        }
    }
}
Groovy
android {
    // ...

    playConfigs {
        firstCustomer {
            serviceAccountCredentials.set(file('customer-one-key.json'))
        }

        secondCustomer {
            serviceAccountCredentials.set(file('customer-two-key.json'))
        }
    }
}

Advanced topics

Using CLI options

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.

Using HTTPS proxies

If you need to use GPP behind an HTTPS-proxy, but it fails with an SSLHandshakeException, you can provide your own truststore via the javax.net.ssl.trustStore property in your project's gradle.properties:

systemProp.javax.net.ssl.trustStore=/path/to/your/truststore.ks
systemProp.javax.net.ssl.trustStorePassword=YourTruststorePassword

GPP will automatically pick it up and use your proxy.

gradle-play-publisher's People

Contributors

alextrotsenko avatar bhurling avatar caerbannog avatar carlitosdroid avatar chrissd23 avatar christiankatzmann avatar clhols avatar geckour avatar gmazzo avatar goooler avatar gtcompscientist avatar johnjohndoe avatar masudr4n4 avatar mikehardy avatar minakov avatar mirland avatar nicolas-raoul avatar nrudenko avatar panpeter avatar poeschl avatar rocboronat avatar ryogak avatar shiraji avatar stefma avatar supercilex avatar technoir42 avatar vekexasia avatar wenterline-godaddy avatar wojtek-kalicinski avatar zacsweers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gradle-play-publisher's Issues

Language not supported

Hi there,

I translated my descriptions and all in many languages,
Now, when I want to upload the translations, I got this error :

com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "androidpublisher",
"message" : "Language not supported.",
"reason" : "unsupportedListingLanguage"
} ],
"message" : "Language not supported."
}

Is that possible to get more info, like which language is concerned ?

Thank you, this plugin is just awesome ;)

Description

Hi guys!

Thanks for your plugin, it's awesome !
I would like to know if you are going to add the description as for the what's new summary ?
If so, when ?

Best

multiple productFlavors

Hi,

Is this possible to have multiple productFlavors, and so multiple 'play directory' ? I would like to have something like this :

  • play
    • productflavor1
      • fr-FR
        • listing
          • description
          • shortdescription
          • title
        • whatsnew
    • productflavor2
      • fr-FR
        • listing
          • description
          • shortdescription
          • title
        • whatsnew

No images uploaded to Google Play

Script do not upload screenshots to Google Play, text listing is updated without any problem. No error message is thrown.

Tested version: 0.14.1

My setup is following:

  1. Screenshots are in correct directories:
  • en-US
    • phoneScreenshots
      • mainscreenshot.png
    • sevenInchScreenshots
      • mainscreenshot.png
    • tenInchScreenshots
      • mainscreenshot.png
  1. Upload images flag is set to true
play {
    serviceAccountEmail = 'acount'
    pk12File = file('certificate.p12')
    uploadImages = true
}

Is there any other settings which I missed?

Gradle tasks are not available

Simply put, none of the gradle tasks are available in either command line or Android Studio (1.1.0 and 1.2 P4) and I do not know what to look for. I'm working with a barebones Android Studio project and have followed the Readme. When I get a chance, I'm going to try this process again on a separate PC.

I'm using:
Gradle 2.2.1
Android Tools Gradle 1.1.3
Play-Publisher 1.0.2
on OSX 10.10

Encoding of listing texts

The encoding of characters outside the ascii range are not correct when using the bootstrap-task.
After a quick look I think simply supplying the encoding to FileUtils.writeStringToFile in BootstrapTask should do it (I don't have the possibility to test or pull-request currently)

Metadata for all flavors being packaged into the apk

Hi,

I have noticed that when I have metadata for multiple flavors as shown below, the publishrelease command packages the metadata for all flavors into an apk irrespective of the flavor we try to build. This is causing the apk to exceed size restrictions. Any thoughts why it would not package only the metadata for the flavor that is being built?

  • [src]
    |
    • - [Flavor1]
      |
      • - [play]
        |
        | ....[folders like screenshots and so on]
        |
    • - [Flavor2]
      |
      • - [play]
        |
        | ....[folders like screenshots and so on]
        |

GoogleJsonResponseException: 500 Internal Server Error

Hi,

First of all thanks for plugin. I am using your plugin to upload apk to google play and faced a problem. I run publishApk task with debug mode. In log I see that all of the request are returned with Http.Ok response (200). But when the plugin tries to commit the changes in Edit by sending request https://www.googleapis.com/androidpublisher/v2/applications/...:commit it gets the response

[com.google.api.client.http.HttpTransport] {
"error": {
"code": 500,
"message": null
}
}

I do not understand what can be the reason of it? Maybe you have some ideas?

Thanks in advance.

Add support for updating app's details

Google Publishing API provides ability to set:

  • Default language of the app
  • Contact Phone
  • Contact mail
  • Contact website

Necessity of setting the language from script is obvious. Meanwhile phone, main and website are required information in Google Play Dev Console and could speed up 1st publishing of the app.

Here is example of passing this data to Google API:

            final AppDetails newDetails = new AppDetails();
            newDetails.setContactEmail("[email protected]")
                    .setContactPhone("+49777777777")
                    .setContactWebsite("http://somesite.com")
                    .setDefaultLanguage(Locale.GERMANY.toString());

            Update updateAppsDetailsRequest = edits
                    .details()
                    .update(ApplicationConfig.PACKAGE_NAME,
                            editId,
                            newDetails);
            AppDetails updatedAppsDetails = updateAppsDetailsRequest.execute();

404 Not Found

Hi,

I have followed the documentation on the gradle-play-publisher and Google API but seem to be getting this error.

com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"location" : "packageName",
"locationType" : "parameter",
"message" : "No application was found for the given package name.",
"reason" : "applicationNotFound"
} ],
"message" : "No application was found for the given package name."
}

There was a similar issue reported before and the suggestion was to link a package name to the project but I did not find such a step in the documentation. Clearly, I am missing something. Can you point me in the right direction?

Thanks!

Folder path

The play folder must be in src/${flavor}/play but there is any way to configure another path?
Perhaps something like:

play {
    track 'production'
    path 'src/customer/${flavor}/play'
}

Support hooking into publish tasks

It would be nice to have some mechanism to hook custom code into the publisher tasks. The main object to expose here would be the current Edits instance. That way we allow developers to perform additional work on the API without implementing every feature request in the main plugin.

Who's interested in this feature?

Perhaps we can collect some use cases here.

FileNotFound Exception

Hi,

This seems to be the same issue reported here - #40.

The plugin still seems to looking for the apk in the "play/{variant}release folder instead of apk folder in the outputs directory and fails with an exception. The workaround suggested says add a "play" folder next to a "java" directory. I do not see a "java" directory either.

Any suggestions?

Thanks!

How to publish to alpha and beta channesl

Hi,

Could you explain publishPaidRelease , would it go to production..
What should we do to publish to alpha and beta channels..

When I use this plugin

  • What went wrong:
    Execution failed for task ':publishRelease'.

    com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidd
    en
    {
    "code" : 403,
    "errors" : [ {
    "domain" : "androidpublisher",
    "message" : "Testing-track (alpha/beta) APK appears in another track, code
    1",
    "reason" : "testingTrackApkInMultipleTracks"
    } ],
    "message" : "Testing-track (alpha/beta) APK appears in another track, code 1
    "
    }

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug
    option to get more log output.

BUILD FAILED

Regards,
Nagendra

build error use gradle 2.3

now i use gradle version 2.3
android build gradle version 1.1.3
play-publisher version 1.0.2

the error is

  • What went wrong:
    A problem occurred evaluating project ':app'.

    Could not find method play() for arguments [build_b6dp6swbwb3sfm4ofhfloa9zh$_run_closure4@477c7b19] on project ':app'.

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

but use gradle version 2.2.1 it's can work perfact!

(Idea) Lazily read APK path from variant

Using our setup as an example, we do some processing in applicationVariants.all{...} to format the APK name with some extra information. This however breaks the plugin's ability to find the APK (due to it looking for the default name), so for now we've been using the solution you suggested in #17. This solution isn't really ideal though, as it makes the code depend on an internal field that could change in another release.

What would you think of allowing passing in a map of apkNames, where the key-value schema is defaultApkName -> customApkName. This way, we can inform the plugin what the new name is, and it can find it because it already has the old name.

I would be happy to take a crack at the idea in a pull request if you'd be amenable to it, let me know!

Unable to update listing details

Hi,

I am trying to upload listing details such as title, fulldescription and shortdescription with a folder structure as mentioned in the documentation. For some reason, only "whatsnew" is updated and everything under the listing directory is ignored. Any thoughts or suggestions on this?

screen shot 2015-03-12 at 9 24 29 am

google-api-java-client's Guava dependency conflicts with Android Gradle Plugin's more recent one

More of an FYI that you might want to include in the README.

I'm not sure why it was working fine before, but I ran into this tonight. Basically the google-api-java-client jar that the publisher API depends on uses an old version of Guava (issue here). The Android gradle plugin uses a newer version of Guava, and having these two on the same classpath causes issues. Specifically, builds would fail due to the plugin throwing a NoSuchMethodError exception for asCharSink(...), which was added in Guava 14 and doesn't exist in the older version that the api client uses.

I'll paste an example stacktrace from Gradle at the bottom, but for now here is a workaround. When declaring the plugin classpath, exclude Guava like so:

classpath ('com.github.triplet.gradle:play-publisher:0.0.4') {
    exclude group: 'com.google.guava'
}

This prevents the plugin from accidentally using the older guava version during builds.

Here's an example stacktrace of what would happen:

<...gradle tasks...>
:apps:AppName:collectFlavorReleaseMultiDexComponents UP-TO-DATE
:apps:AppName:packageAllFlavorReleaseReleaseClassesForMultiDex UP-TO-DATE
:apps:AppName:shrinkFlavorReleaseMultiDexComponents UP-TO-DATE
:apps:AppName:createFlavorReleaseMainDexClassList UP-TO-DATE
:apps:AppName:dexFlavorRelease FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':apps:AppName:dexFlavorRelease'.
> com.google.common.io.Files.asCharSink(Ljava/io/File;Ljava/nio/charset/Charset;[Lcom/google/common/io/FileWriteMode;)Lcom/google/common/io/CharSink;

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':apps:AppName:dexFlavorRelease'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:305)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:80)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:47)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:35)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:24)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.StartStopIfBuildAndStop.execute(StartStopIfBuildAndStop.java:33)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.ReturnResult.execute(ReturnResult.java:34)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:71)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:69)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:69)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:70)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:45)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator.runCommand(DaemonStateCoordinator.java:258)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy.doBuild(StartBuildOrRespondWithBusy.java:49)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.HandleCancel.execute(HandleCancel.java:36)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.HandleStop.execute(HandleStop.java:30)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.DaemonHygieneAction.execute(DaemonHygieneAction.java:39)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.CatchAndForwardDaemonFailure.execute(CatchAndForwardDaemonFailure.java:32)
    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
    at org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter.executeCommand(DefaultDaemonCommandExecuter.java:52)
    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.handleCommand(DefaultIncomingConnectionHandler.java:154)
    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.receiveAndHandleCommand(DefaultIncomingConnectionHandler.java:128)
    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.run(DefaultIncomingConnectionHandler.java:116)
    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
Caused by: java.lang.NoSuchMethodError: com.google.common.io.Files.asCharSink(Ljava/io/File;Ljava/nio/charset/Charset;[Lcom/google/common/io/FileWriteMode;)Lcom/google/common/io/CharSink;
    at com.android.builder.core.AndroidBuilder.getFilesToAdd(AndroidBuilder.java:1429)
    at com.android.builder.core.AndroidBuilder.convertByteCode(AndroidBuilder.java:1408)
    at com.android.builder.core.AndroidBuilder$convertByteCode$7.call(Unknown Source)
    at com.android.build.gradle.tasks.Dex.doTaskAction(Dex.groovy:136)
    at com.android.build.gradle.tasks.Dex.this$5$doTaskAction(Dex.groovy)
    at com.android.build.gradle.tasks.Dex$this$5$doTaskAction.callCurrent(Unknown Source)
    at com.android.build.gradle.tasks.Dex.taskAction(Dex.groovy:79)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.doExecute(AnnotationProcessingTaskFactory.java:235)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:211)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.execute(AnnotationProcessingTaskFactory.java:222)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:200)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
    ... 69 more


BUILD FAILED

Total time: 5.648 secs

Exception with android gradle tools 1.1.2

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.2'
        classpath 'com.github.triplet.gradle:play-publisher:1.0.0'
    }
}
Error:(1, 0) Expecting a stack map frame
Exception Details:
  Location:
    com/android/build/gradle/internal/TaskManager.getExtension()Lcom/android/build/gradle/BaseExtension; @5: aconst_null
  Reason:
    Error exists in the bytecode
  Bytecode:
    0x0000000: 2ab4 005e b001 b0                      

also you can try

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.1.2'
        classpath 'com.google.apis:google-api-services-androidpublisher:v2-rev13-1.19.0'
    }
}

Multiple Service Accounts

I'm working on an application that has two flavours. Each flavour corresponds to a brand/client. Each brand has it's own developer account.

Is it possible to configure a per-flavour service account? do you have plans to add this functionality?

Do you guys know how can I accomplish this in the current state?
Maybe with a task that configures the play plugin for each flavour before actually running the upload?

Thanks

Use it with external signing config

Hi, is there a way to use it also with a external signing config?

In my project I have setup a gradle method that reads the signing details from a external file and inject them into the signingconfigs at runtime of a gradle build. I did this to hide my private signing keys and so on.
To help you understand how its done, I added the interesting spots of my gradle file.

    signingConfigs {
        googlePlay {
            //Placeholder which gets automatically filled during project sync. Needs the right setting file.
            //This settings is not in the source control, because it contains sensitive information.
        }
    }
afterEvaluate { project ->

    def props = getExternalProperties();

    setupSigning(props)
    setupFabricApiFile(props)
}
def setupSigning(properties) {
    if (properties == null) {
        System.err.println("Could not set signing config. No properties");
        return;
    }

    def configName = 'googlePlay'

    if (android.signingConfigs.hasProperty(configName)) {

        //Android signing information
        android.signingConfigs[configName].storeFile = file(properties['path'] + '/' + properties['storeFile'])
        android.signingConfigs[configName].storePassword = properties['storePassword']
        android.signingConfigs[configName].keyAlias = properties['keyAlias']
        android.signingConfigs[configName].keyPassword = properties['keyPassword']
        println("Setup keystore settings.")
    }
}

Full version of it -> https://github.com/Poeschl/TryAndRemove/blob/b62e0caf999c9bf2ba601d1c416cf3d95e7c8b79/app/build.gradle

As result of it the process works fine. Also the signing does the right thing. But the android plugin is not generating the zipAlign task at a project sync. I think thats why your plugin complain about the missing zipalign task.
Is it possible to bypass this requirement or replace it with a other test? Like check if you have all of the four nessasary signing parameter?

Cannot upload new apks

Is it possible to upload brand new apks? And I don't mean updates, but the initial apk?

Can't find title, fulldescription etc.

I'm not sure I am doing it right, I could do publishApkRelease task, upload a new apk, but I could't update the listing, I have the descriptions and title files in structure <project_name\app\src\main\play<de-DE and en-US>\listing<title.txt fulldescription.txt shortdescription.txt>, I got error The app has no title/short descripts. Below is the screenshots. Can anyone tell me what I am missing.

1

2

4

Thanks.

Manual publish

Hi,

I have noticed that the plugin uploads all the content - like APK and metadata. But it only gets it to the "Read to Publish" state. I had to manually publish the app to get it to the play store. Is this not supported by the plugin or am I missing something here?

Thanks!

Promo Video

Every time a listing update gets pushed, the manually added YouTube-Promo-Video-Link gets deleted.

Mention whatsnew character limit in README

Just a suggestion, as the API error response isn't very descriptive. The "what's new" text can be a max of 80 characters (as it's replacing the "Promo Text" field in the store listing").

The response if you exceed this looks like this:

> com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
  {
    "code" : 403,
    "errors" : [ {
      "domain" : "androidpublisher",
      "message" : "The recent changes text for APK version 2565 is too long for language .",
      "reason" : "apkRecentChangesTooLong"
    } ],
    "message" : "The recent changes text for APK version 2565 is too long for language ."
  }

Support new splits API

Bear in mind, that said API allows developer substantial degree of freedom, so implementation must be flexible as well.

(Idea) Set applicationID from variant rather than parsing manifest

Instead of using ManifestParser at all, simply set applicationId with variant.applicationId. Not sure there would be any noticeable performance improvement, but just an idea I thought I'd throw out there. Takes out a parsing step and lets you set it while looping over the application variants anyway.

If it's something you think would be worthwhile, I'd be happy to pull request with the change.

Deferring applicationId retrieval to when the publishApkTask runs

Right now, the applicationId is captured at configuration and persisted throughout the PlayPublishTask's lifetime, even though a build might dynamically change the applicationId after that. Retrieving the APK file would run into the same issue if the developer set custom APK names, but it doesn't run until the PlayPublishApkTask runs, and thus retrieves the correct final name after the app has been assembled. My proposal is to do the same with applicationId. I think it would be an easy change, since the variant instance is held in the PlayPublishApkTask.

In PlayPublishApkTask, instead of using the saved applicationId field, we can simply call variant.applicationId. I think this should work, though it might have to be variant.mergedFlavor.applicationId (I'm not entirely sure if they can ever be different).

If you're open to it, I'd be happy to send a pull request your way.

Error message on texts which are to long.

It would be nice to have a error message when you exceed the char limit of the play store fields.
If you like that idea, I will invest some time and do a pull request.

Different gradle tasks for pushing to different Play Store channels

Hi there, is it possible to make the plugin create more than one gradle task so that a different task is used to push to a different Play Store channel?

E.g. I want to push an APK to different channels from different GitHub branches on my CI server, and the only way to do that I am aware of is to have a different config for gradle-play-publisher in every branch of my project. Is that the only way?

Thanks!

Using custom APK names causes errors

I am using this code in my build.gradle to generate an APK with the version name inside the filename.

applicationVariants.all { variant ->
        def file = variant.outputFile
        variant.outputFile = new File(file.parent, file.name.replace(".apk", "-" + defaultConfig.versionName + ".apk"))
      }

This causes an error with the publish task:

/app/build/outputs/apk/app-galway-release.apk (No such file or directory)

Here is the stack trace also

10:22:18.238 [ERROR] [org.gradle.BuildExceptionReporter] 
10:22:18.238 [ERROR] [org.gradle.BuildExceptionReporter] FAILURE: Build failed with an exception.
10:22:18.238 [ERROR] [org.gradle.BuildExceptionReporter] 
10:22:18.238 [ERROR] [org.gradle.BuildExceptionReporter] * What went wrong:
10:22:18.239 [ERROR] [org.gradle.BuildExceptionReporter] Execution failed for task ':app:publishGalwayRelease'.
10:22:18.239 [ERROR] [org.gradle.BuildExceptionReporter] > java.io.FileNotFoundException: /Users/darko/Android/Workspace/dictation-recorder-android/app/build/outputs/apk/app-galway-release.apk (No such file or directory)
10:22:18.239 [ERROR] [org.gradle.BuildExceptionReporter] 
10:22:18.240 [ERROR] [org.gradle.BuildExceptionReporter] * Exception is:
10:22:18.241 [ERROR] [org.gradle.BuildExceptionReporter] org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:publishGalwayRelease'.
10:22:18.241 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:69)
10:22:18.241 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
10:22:18.241 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
10:22:18.242 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
10:22:18.242 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
10:22:18.242 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
10:22:18.242 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
10:22:18.243 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
10:22:18.243 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
10:22:18.243 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:289)
10:22:18.243 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
10:22:18.243 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
10:22:18.244 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
10:22:18.244 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
10:22:18.244 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:86)
10:22:18.244 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
10:22:18.245 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
10:22:18.245 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
10:22:18.245 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:67)
10:22:18.245 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
10:22:18.245 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:61)
10:22:18.246 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:54)
10:22:18.246 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:166)
10:22:18.246 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:113)
10:22:18.246 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:81)
10:22:18.246 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:64)
10:22:18.247 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.tooling.internal.provider.BuildModelAction.run(BuildModelAction.java:76)
10:22:18.247 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.tooling.internal.provider.BuildModelAction.run(BuildModelAction.java:31)
10:22:18.247 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.tooling.internal.provider.ConfiguringBuildAction.run(ConfiguringBuildAction.java:150)
10:22:18.247 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:35)
10:22:18.248 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:45)
10:22:18.248 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
10:22:18.248 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.248 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:42)
10:22:18.248 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.249 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:24)
10:22:18.249 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.249 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.StartStopIfBuildAndStop.execute(StartStopIfBuildAndStop.java:33)
10:22:18.249 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.249 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ReturnResult.execute(ReturnResult.java:34)
10:22:18.250 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.250 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:71)
10:22:18.250 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:69)
10:22:18.250 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.util.Swapper.swap(Swapper.java:38)
10:22:18.251 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:69)
10:22:18.251 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.251 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
10:22:18.251 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
10:22:18.252 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.252 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:60)
10:22:18.252 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
10:22:18.252 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.252 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:45)
10:22:18.253 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.DaemonStateCoordinator.runCommand(DaemonStateCoordinator.java:186)
10:22:18.253 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy.doBuild(StartBuildOrRespondWithBusy.java:49)
10:22:18.253 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:34)
10:22:18.253 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.254 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.HandleStop.execute(HandleStop.java:36)
10:22:18.254 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.254 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonHygieneAction.execute(DaemonHygieneAction.java:39)
10:22:18.254 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.254 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.CatchAndForwardDaemonFailure.execute(CatchAndForwardDaemonFailure.java:32)
10:22:18.255 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DaemonCommandExecution.proceed(DaemonCommandExecution.java:125)
10:22:18.255 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.exec.DefaultDaemonCommandExecuter.executeCommand(DefaultDaemonCommandExecuter.java:51)
10:22:18.255 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.handleCommand(DefaultIncomingConnectionHandler.java:155)
10:22:18.255 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.receiveAndHandleCommand(DefaultIncomingConnectionHandler.java:128)
10:22:18.256 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.launcher.daemon.server.DefaultIncomingConnectionHandler$ConnectionWorker.run(DefaultIncomingConnectionHandler.java:116)
10:22:18.256 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
10:22:18.256 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: org.gradle.internal.UncheckedException: java.io.FileNotFoundException: /Users/darko/Android/Workspace/dictation-recorder-android/app/build/outputs/apk/app-galway-release.apk (No such file or directory)
10:22:18.256 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.internal.UncheckedException.throwAsUncheckedException(UncheckedException.java:39)
10:22:18.256 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:66)
10:22:18.257 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.doExecute(AnnotationProcessingTaskFactory.java:219)
10:22:18.257 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:212)
10:22:18.257 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:201)
10:22:18.257 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:533)
10:22:18.258 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:516)
10:22:18.258 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
10:22:18.258 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
10:22:18.258 [ERROR] [org.gradle.BuildExceptionReporter]    ... 67 more
10:22:18.258 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.io.FileNotFoundException: /Users/darko/Android/Workspace/dictation-recorder-android/app/build/outputs/apk/app-galway-release.apk (No such file or directory)
10:22:18.259 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.http.FileContent.getInputStream(FileContent.java:71)
10:22:18.259 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.media.MediaHttpUploader.resumableUpload(MediaHttpUploader.java:397)
10:22:18.259 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.media.MediaHttpUploader.upload(MediaHttpUploader.java:336)
10:22:18.259 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:418)
10:22:18.260 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:343)
10:22:18.260 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:460)
10:22:18.260 [ERROR] [org.gradle.BuildExceptionReporter]    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$execute.call(Unknown Source)
10:22:18.260 [ERROR] [org.gradle.BuildExceptionReporter]    at de.triplet.gradle.play.PlayPublishTask.publish(PlayPublishTask.groovy:50)
10:22:18.260 [ERROR] [org.gradle.BuildExceptionReporter]    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
10:22:18.261 [ERROR] [org.gradle.BuildExceptionReporter]    ... 74 more
10:22:18.261 [ERROR] [org.gradle.BuildExceptionReporter] 
10:22:18.261 [LIFECYCLE] [org.gradle.BuildResultLogger] 
10:22:18.261 [LIFECYCLE] [org.gradle.BuildResultLogger] BUILD FAILED
10:22:18.262 [LIFECYCLE] [org.gradle.BuildResultLogger] 

images are not downloaded

I have this gradle file to manage my upload:

apply plugin: 'play'

play {
    serviceAccountEmail = System.getenv("PLAYSTORE_EMAIL")
    pk12File = file(System.getenv("PLAYSTORE_PK12_KEYSTORE"))
    track = 'alpha'
}

When I run bootstrapShowcaseReleasePlayResources(I have a flavor called "Showcase") The images are not beeing downloaded.

Am I doing something wrong or is this feature broken? Is there a way I can tell the plugin to be verbose?

FileNotFoundException

Did everything as described in readme

after running gradle publishApkRelease I get

java.io.FileNotFoundException: .... /app/build/outputs/play/release

why it is looking by default inside play folder instead off apk?
I'm not running any other plugins, custom tasks

Check to make sure an app exists first

It seems that you must upload an initial APK:

com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"location" : "packageName",
"locationType" : "parameter",
"message" : "No application was found for the given package name.",
"reason" : "applicationNotFound"
} ],
"message" : "No application was found for the given package name."
}

Specify in README which permissions must be given in API access

Preferably we would like to only use the permissions we need, but currently we only ever get 401 Unauthorized failures. Could you provide any insights regarding to this? Specifically which permissions are necessary and maybe consider specifying them in the README for future users.

Support image downloading in bootstrap task

This is a leftover from #14. Currently the API only supports downloading thumbnail versions of the play store graphics. We should file a feature request somewhere (maybe here).

As soon as we get the original-sized images via the API we can enable this feature.

Custom APK path/name

For anyone that sets custom APK names, it would be nice to be able to set the APK name to look for rather than the default

use other buildTypes

I find it currently not possible to specify another buildType to be uploaded to play.
Is that correct?
Would a pull-request for such a feature be considered?

We have a buildType "playStore" that is used for the playStore and release is used for hockeyApp releases...

FAILURE: Build failed with an exception.

Hello,

I have include your library and try to upload apk file to my developer account. But i am not able to get it updated. I got error message with log like below:

D:\someDirectory\myProject>gradlew publishApkRelease
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes
:buildSrc:jar
:buildSrc:assemble
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build

FAILURE: Build failed with an exception.

  • What went wrong:
    Failed to notify task execution graph listener.

    com.google.common.hash.HashCode.fromString(Ljava/lang/String;)Lcom/google/comm
    on/hash/HashCode;
    com.google.common.hash.HashCode.fromString(Ljava/lang/String;)Lcom/google/comm
    on/hash/HashCode;
    com.google.common.hash.HashCode.fromString(Ljava/lang/String;)Lcom/google/comm
    on/hash/HashCode;
    com.google.common.hash.HashCode.fromString(Ljava/lang/String;)Lcom/google/comm
    on/hash/HashCode;
    com.google.common.hash.HashCode.fromString(Ljava/lang/String;)Lcom/google/comm
    on/hash/HashCode;

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug
    option to get more log output.

BUILD FAILED

Total time: 6.929 secs

Am i doing something wrong or there is problem with the library. Please help me to resolved this issue.

401 Unauthorized

Hi,

I am trying to upload an existing app. I have a service account setup (with proper permissions) with a p12 file and changes to gradle file as mentioned in the documentation but end up with this error.

-------------- RESPONSE --------------
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="https://accounts.google.com/AuthSubRequest", error=invalid_token
X-XSS-Protection: 1; mode=block
Expires: Thu, 05 Mar 2015 17:07:09 GMT
Alternate-Protocol: 443:quic,p=0.08
Server: GSE
X-Content-Type-Options: nosniff
Cache-Control: private, max-age=0
X-Frame-Options: SAMEORIGIN
Vary: X-Origin
Vary: Origin
Transfer-Encoding: chunked
Date: Thu, 05 Mar 2015 17:07:09 GMT
Content-Encoding: gzip
Content-Type: application/json; charset=UTF-8

Any suggestions?

Thanks!

noFullDescription

Hi,
I just tested the plugin for publishing descriptions , title, shortdescription and whatsnew.
I created 2 folders :

  • fr-FR
    • listing
      • description
      • shortdescription
      • title
    • whatsnew
  • en-US
    • listing
      • description
      • shortdescription
      • title
    • whatsnew

And I'm getting this error:

Error:Execution failed for task ':app:publishListingMyAPKPackage'.
> com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
  {
    "code" : 403,
    "errors" : [ {
      "domain" : "androidpublisher",
      "message" : "This app has no full description for language fr_FR.",
      "reason" : "noFullDescription"
    }, {
      "domain" : "androidpublisher",
      "message" : "This app has no full description for language en_US.",
      "reason" : "noFullDescription"
    } ],
    "message" : "This app has no full description for language fr_FR."
  }```

Add dependency for gradle-play-publisher's tasks from client's app script.

We have our store data stored separately from our source code.

I want to have task loadStoreListing, that loads resources from remote server and put them inside 'src/release/play' (as it's required by gradle-play-publisher plug-ing).
I want to make this task executing before generateReleasePlayResources (GeneratePlayResourcesTask).

Here is part of my script to build my project called app

...
preBuild.dependsOn applySettings

/**
 * Loads listing data and resources
 */
task loadStoreListing << {
     //load store listing and images and put them to 'src/release/play' for "play" plug-ing
}

generateReleasePlayResources.dependsOn loadStoreListing

...

First dependency (preBuild.dependsOn applySettings) works fine for me. But when I add 2nd dependency for the task from "play" plug-in I am getting error:

* What went wrong:
A problem occurred evaluating project ':app'.
> Could not find property 'generateReleasePlayResources' on project ':app'.

How can I achieve my goal? Any ideas what I am doing wrong?

Use with Cordova?

Hello there, looks like a great tool. I am a cordova dev which means I know JS and not a ton about gradle (even though my build process relies upon it). I'm wondering if you know of anyone using this in the cordova android build process. Specifically I am interested in using this plugin to upload the apks that cordova spits out. Unfortunately I don't even know where to start. Do you have any gradle resources that might help me to shoehorn this into the cordova build pipeline? If not, no worries, but I figured I'd ask.

Thank you!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.