{"id":281658,"date":"2016-11-28T15:15:04","date_gmt":"2016-11-28T12:15:04","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=281658"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=281658","title":{"rendered":"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Google Play \u2014 \u043f\u0440\u044f\u043c\u043e \u0438\u0437 Android Studio"},"content":{"rendered":"<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041f\u043e\u043b \u0433\u043e\u0434\u0430 \u043d\u0430\u0437\u0430\u0434 \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 <a href=\"https:\/\/habrahabr.ru\/post\/281557\/\">\u0431\u044b\u043b\u0430 \u0441\u0442\u0430\u0442\u044c\u044f<\/a> \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Google Play. \u041f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u043a \u0441\u0442\u0430\u0442\u044c\u0435 \u0438 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043d\u0435\u0433\u043e \u0433\u043b\u0430\u0441\u0438\u043b \u043e\u0434\u043d\u0443 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u0443\u044e \u0432\u0435\u0449\u044c:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/c7c\/eee\/fd6\/c7ceeefd6a5043beb9af1ba2ac028383.png\"\/><\/p>\n<p>  \u041d\u043e \u044f \u0441 \u0440\u0430\u0434\u043e\u0441\u0442\u044c\u044e \u0433\u043e\u0442\u043e\u0432 \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u043e \u2014 \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430. \u041f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u044f\u043c\u043e \u0438\u0437 Android Studio \u043c\u043e\u0436\u043d\u043e! \u0411\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u0431\u0435\u0437 Android Studio \u043d\u0430 \u0432\u0430\u0448\u0435\u043c CI \u2014 \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0435\u043b\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e Gradle task.<br \/>  <a name=\"habracut\"><\/a><br \/>  \u041c\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u043d\u043e \u0432\u043c\u0435\u0441\u0442\u043e java \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b groovy-\u0441\u043a\u0440\u0438\u043f\u0442.<\/p>\n<p>  \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0437 \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u043c \u0434\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c .json-\u0444\u0430\u0439\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u0434\u0435 \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041a\u0430\u043a \u0435\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0434\u043b\u044f \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a Google Play Developer API \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 <a href=\"https:\/\/habrahabr.ru\/post\/281557\/\">\u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>, \u0438\u043b\u0438 \u0436\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/habrahabr.ru\/post\/313416\/\">\u043c\u043e\u044e \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e<\/a> \u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 Google Play Billing \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0433\u0434\u0435 \u0432 \u0447\u0430\u0441\u0442\u0438 3 \u043e\u043f\u0438\u0441\u0430\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 Service-account-\u043e\u0432 \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a Google Play.<\/p>\n<p>  \u0421 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u0431\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c, \u0447\u0442\u043e \u0443 \u0432\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0437\u0430\u0432\u0435\u0442\u043d\u044b\u0439 .json \u0444\u0430\u0439\u043b\u0438\u043a \u0441 service account secret.<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u043c \u043d\u0430\u043c \u043f\u0440\u043e\u0435\u043a\u0442. \u0411\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 build.gradle \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u0430 \u043d\u0435 app. \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c <i>root\/build.gradle<\/i> \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443:<\/p>\n<pre><code>\/\/ \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0433\u0440\u0443\u0432\u0438 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 apply plugin: 'groovy'  buildscript {     repositories {         jcenter()     }     dependencies {         classpath 'com.android.tools.build:gradle:2.2.2'     } }  allprojects {     repositories {         jcenter()     } }  dependencies {     \/\/ \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0433\u0440\u0443\u0432\u0438 \u0432 \u043d\u0430\u0448 \u043f\u0440\u043e\u0435\u043a\u0442     compile 'org.codehaus.groovy:groovy-all:2.4.7'     \/\/ \u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0435\u043c \u0434\u0435\u043b\u0430\u0442\u044c publish     compile 'com.google.apis:google-api-services-androidpublisher:v2-rev38-1.22.0' } <\/code><\/pre>\n<p>  \u0427\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043d\u043e:<br \/>  1. <i>apply plugin: &#8216;groovy&#8217;<\/i><br \/>  \u0410\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c groovy-\u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<br \/>  2. <i>dependencies \u2014 compile &#8216;org.codehaus.groovy:groovy-all:2.4.7&#8217;<\/i><br \/>  \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044e\u044e \u0432\u0435\u0440\u0441\u0438\u044e groovy \u0432 \u043d\u0430\u0448\u0435 \u043f\u0440\u043e\u0435\u043a\u0442<br \/>  3. <i>dependencies \u2014 compile &#8216;com.google.apis:google-api-services-androidpublisher:v2-rev38-1.22.0&#8217;<\/i><br \/>  \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043e\u0442 Google, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f\u043c\u0438 \u0432 Google play (<a href=\"https:\/\/developers.google.com\/android-publisher\/api-ref\/\">\u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e<\/a>).<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c groovy-\u0441\u043a\u0440\u0438\u043f\u0442\u044b \u0438 groovy-\u043a\u043b\u0430\u0441\u0441\u044b \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435.<br \/>  \u041d\u043e \u0434\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c source dir \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 groovy-\u043a\u043b\u0430\u0441\u0441\u043e\u0432 \u0438 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0443\u0435\u043c \u0434\u0440\u0443\u0433\u0438\u0435 \u0444\u0430\u0439\u043b\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043c \u0431\u0443\u0434\u0443\u0442 \u043d\u0443\u0436\u043d\u044b:<\/p>\n<pre><code>root\/     app\/         ... \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435         .gitignore - \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b keystore.jks \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438         keystore.jks         build.gradle     gradle\/     iam\/         .gitignore - \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b publisher.json \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438         publisher.json - \u0444\u0430\u0439\u043b \u0441 service account secret     src\/         main\/             groovy\/                 ... \u0442\u0443\u0442 \u0431\u0443\u0434\u0435\u043c \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441\u044b \u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u044b     .gitignore - \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0430\u0439\u043b signing.properties \u0441\u044e\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438     build.gradle     gradle.properties     gradlew     gradlew.bat     local.properties     signing.properties - \u0442\u0443\u0442 \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043f\u0430\u0440\u043e\u043b\u0438 \u043d\u0430\u0448\u0435\u0433\u043e keystore     settings.gradle <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 Google Play, \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0433\u043e release \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u043c. \u041d\u043e \u0432\u0435\u0434\u044c \u043c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0448 keystore, \u044f\u0432\u043a\u0438 \u0438 \u043f\u0430\u0440\u043e\u043b\u0438 \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438? \u0418\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 .gitignore. \u0421\u0430\u043c\u0438 \u0436\u0435 \u043f\u0430\u0440\u043e\u043b\u0438 \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u043c \u0432 \u0444\u0430\u0439\u043b <i>root\/signing.properties<\/i>:<\/p>\n<pre><code>keystore.file=keystore.jks keystore.password=&lt;\u043f\u0430\u0440\u043e\u043b\u044c&gt; key.alias=&lt;\u0438\u043c\u044f_\u043a\u043b\u044e\u0447\u0430&gt; key.password=&lt;\u043f\u0430\u0440\u043e\u043b\u044c_\u043a\u043b\u044e\u0447\u0430&gt; <\/code><\/pre>\n<p>  \u041f\u0440\u043e\u0447\u0438\u0442\u0430\u0435\u043c \u044d\u0442\u0438 \u043f\u0430\u0440\u043e\u043b\u0438 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 \u0441 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0439 signing config \u0432 <i>root\/app\/build.gradle<\/i><\/p>\n<pre><code>... android {     ...     Properties signingProperties = new Properties()     def file = project.rootProject.file('signing.properties')     if (fixe.exists()) {         signingProperties.load(file.newDataInputStream())     }     def prodSigning_keystoreFile = properties.getProperty('keystore.file')     def prodSigning_keystorePassword = properties.getProperty('keystore.password')     def prodSigning_keyAlias = properties.getProperty('key.alias')     def prodSigning_keyPassword = properties.getProperty('key.password')     ...     signingConfigs {         ...         production {             storeFile file(prodSigning_keystoreFile )             storePassword prodSigning_keystorePassword              keyAlias prodSigning_keyAlias              keyPassword prodSigning_keyPassword          }     }     productFlavors {         ...         prod {             ...         }     }     buildTypes {         ...         release {             signingConfig production         }     } } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c gradle assembleProdRelease \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c apk-\u0444\u0430\u0439\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u0432 Google Play.<\/p>\n<p>  \u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u043d\u0430\u0448 apk. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b <i>root\/src\/main\/groovy\/ApkPublisher.groovy<\/i>:<\/p>\n<pre><code>import com.google.api.client.googleapis.auth.oauth2.GoogleCredential import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport import com.google.api.client.http.FileContent import com.google.api.client.json.jackson2.JacksonFactory import com.google.api.services.androidpublisher.AndroidPublisher import com.google.api.services.androidpublisher.AndroidPublisherScopes import com.google.api.services.androidpublisher.model.Track  class ApkPublisher {      \/\/ \u0438\u043c\u044f \u043f\u0430\u043a\u0435\u0442\u0430     String packageName;     \/\/ \u0438\u043c\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f (\u0432 \u0442\u0435\u043e\u0440\u0438\u0438 \u043e\u043d\u043e \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u043e \u0431\u0435\u0437 \u043d\u0435\u0433\u043e \u0431\u0443\u0434\u0443\u0442 warnings)     String name;     \/\/ \u0438\u043c\u044f apk.\u0444\u0430\u0439\u043b\u0430     String apkName;     \/\/ \u0438\u043c\u044f \u0444\u0430\u0439\u043b\u0430 proguard-mapping     String mappingName;      void publish() {         assert packageName != null         assert name != null         assert apkName != null         assert mappingName != null          println &quot;PUBLISHING [ ${packageName} \/ ${name} ]&quot;          def dir = new File(&quot;assemble&quot;)          \/\/ \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 service account secret \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438         def inputStream = new FileInputStream(&quot;iam\/publisher.json&quot;);         def transport = GoogleNetHttpTransport.newTrustedTransport();         def credential = GoogleCredential.fromStream(inputStream)             .createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));         def builder = new AndroidPublisher.Builder(transport,             JacksonFactory.getDefaultInstance(), credential);         builder.setApplicationName(name)         def androidPublisher = builder.build();         def edits = androidPublisher.edits();          \/\/ \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435         def editRequest = edits.insert(packageName, null);         def edit = editRequest.execute();         \/\/ \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 id, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0443\u0436\u0435\u043d \u043d\u0430\u043c \u0447\u0442\u043e\u0431\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f         final String editId = edit.getId();         println &quot; - edit_id = ${editId}&quot;          \/\/ \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 apk         def apkFilePath = new File(dir, apkName)         println &quot; - apk file = ${apkFilePath}&quot;         def apkFile = new FileContent(&quot;application\/vnd.android.package-archive&quot;, apkFilePath);         def apkUploadRequest = edits.apks().upload(packageName, editId, apkFile);         def apkUploadResult = apkUploadRequest.execute();         \/\/ \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 verfsionCode         int versionCode = apkUploadResult.getVersionCode()         println &quot; - version code ${versionCode} has been uploaded&quot;          \/\/ \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c proguard mapping         def mappingFilePath = new File(dir, mappingName)         println &quot; - mapping file = ${mappingFilePath}&quot;         def mappingFile = new FileContent(&quot;application\/octet-stream&quot;, mappingFilePath);         def mappingUploadRequest = edits.deobfuscationfiles()             .upload(packageName, editId, versionCode, &quot;proguard&quot;, mappingFile);         mappingUploadRequest.execute();         println &quot; - mapping for version ${versionCode} has been uploaded&quot;          \/\/ \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0439 apk         \/\/ \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043c\u044b \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0435\u0433\u043e \u0432 \u0430\u043b\u044c\u0444\u0430-\u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435         List apkVersionCodes = [versionCode]         def track = new Track().setVersionCodes(apkVersionCodes)         def updateTrackRequest = edits.tracks().update(packageName, editId, &quot;alpha&quot;, track);         def updatedTrack = updateTrackRequest.execute();         println &quot; - track code ${updatedTrack.getTrack()} has been updated&quot;          \/\/ \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u0441\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u044b         \/\/ \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c, \u0447\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0430 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d \u0438 &quot;\u0437\u0430\u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c&quot;         \/\/ \u0435\u0433\u043e, \u043a\u0430\u043a \u0442\u0440\u0430\u043d\u0437\u0430\u043a\u0446\u0438\u044e         def commitRequest = edits.commit(packageName, editId);         def appEdit = commitRequest.execute();         println &quot; - app edit with id ${appEdit.getId()} has been comitted&quot;         println &quot;APP [ ${packageName} \/ ${name} \/ v${versionCode} ] SUCCESSFULLY PUBLISHED&quot;     }  } <\/code><\/pre>\n<p>  \u0412\u0442\u043e\u0440\u043e\u0439 \u0444\u0430\u0439\u043b <i>root\/src\/main\/groovy\/PublishApk.groovy<\/i>:<\/p>\n<pre><code>def void moveToAssemble(String folder, String name, String newName) {     def from = new File(folder, name)     def to = new File(&quot;assemble&quot;, newName)     from.renameTo(to)     println &quot;moved ${from} to ${to}&quot; }  \/\/ \u043f\u0435\u0440\u0435\u043c\u0435\u0441\u0442\u0438\u043c \u0444\u0430\u0439\u043b\u044b \u0432 \u043f\u0430\u043f\u043a\u0443 root\/assemble \/\/ \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0435\u0435 \u0435\u0441\u043b\u0438 \u0435\u0435 \u043d\u0435 \u0431\u044b\u043b\u043e \/\/ \u0438 \u0443\u0434\u0430\u043b\u0438\u043c \u0441\u0442\u0430\u0440\u044b\u0435 \u0444\u0430\u0439\u043b\u044b, \u0435\u0441\u043b\u0438 \u043e\u043d\u0438 \u0442\u0430\u043c \u0431\u044b\u043b\u0438 def destDir = new File(&quot;assemble&quot;) destDir.mkdir() for (def item : destDir.listFiles()) {     item.delete() } moveToAssemble(&quot;app\/build\/outputs\/apk&quot;, &quot;app-prod-release.apk&quot;, &quot;myapp.apk&quot;) moveToAssemble(&quot;app\/build\/outputs\/mapping\/prod\/release&quot;, &quot;mapping.txt&quot;, &quot;myapp-mapping.txt&quot;)  \/\/ \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 new ApkPublisher(         packageName: &quot;com.example.myapp&quot;,         name: &quot;My app&quot;,         apkName: &quot;myapp.apk&quot;,         mappingName: &quot;myapp-mapping.txt&quot; ).publish() <\/code><\/pre>\n<p>  \u0421\u043a\u0440\u0438\u043f\u0442 \u0434\u043b\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0444\u0430\u0439\u043b\u0430 \u0433\u043e\u0442\u043e\u0432. \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e Gradle task:<\/p>\n<p>  <i>root\/build.gradle<\/i>  <\/p>\n<pre><code> \/\/ \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0441\u0431\u043e\u0440\u043a\u0443 apk task assembleApk(dependsOn: [         ':app:assembleProdRelease' ]) &lt;&lt; {     println(&quot;APK assembled&quot;) }  \/\/ \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u043c \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0441\u043a\u0440\u0438\u043f\u0442 task publishApk(dependsOn: 'classes', type: JavaExec) {     main = 'PublishApk'     classpath = sourceSets.main.runtimeClasspath }  task assembleAndPublishApk() {     dependsOn 'assembleApk'     dependsOn 'publishApk'     tasks.findByName('publishApk').mustRunAfter 'assembleApk'     doLast {         println(&quot;APK successfilly published, find it in \/assemble dir&quot;)     } } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <i>gradle assembleAndPublishApk<\/i> \u0434\u043b\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438 apk \u0432 \u0430\u043b\u044c\u0444\u0430-\u043a\u0430\u043d\u0430\u043b. \u0418 \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0445\u043e\u0442\u044c \u043f\u043e\u0441\u043b\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043c\u043c\u0438\u0442\u0430 \u0432 development. \u0412 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043c\u044b \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c proguard-mapping \u0444\u0430\u0439\u043b.<\/p>\n<p>  P. S. \u0427\u0442\u043e \u0435\u0449\u0435 \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c?<br \/>  1. \u041c\u043e\u044e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u2014 <a href=\"https:\/\/habrahabr.ru\/post\/313416\/\">Android In-app Billing: \u043e\u0442 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/a><br \/>  2. <a href=\"https:\/\/developers.google.com\/android-publisher\/api-ref\/\">Google Play Developer API reference<\/a><br \/>  3. <a href=\"https:\/\/github.com\/googlesamples\/android-play-publisher-api\/tree\/master\/v2\/java\">\u041f\u0440\u0438\u043c\u0435\u0440 \u043e\u0442 Google \u043d\u0430 GitHub<\/a><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habrahabr.ru\/post\/316302\/\"> https:\/\/habrahabr.ru\/post\/316302\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041f\u043e\u043b \u0433\u043e\u0434\u0430 \u043d\u0430\u0437\u0430\u0434 \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 <a href=\"https:\/\/habrahabr.ru\/post\/281557\/\">\u0431\u044b\u043b\u0430 \u0441\u0442\u0430\u0442\u044c\u044f<\/a> \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 Google Play. \u041f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439 \u043a \u0441\u0442\u0430\u0442\u044c\u0435 \u0438 \u043e\u0442\u0432\u0435\u0442 \u043d\u0430 \u043d\u0435\u0433\u043e \u0433\u043b\u0430\u0441\u0438\u043b \u043e\u0434\u043d\u0443 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u0443\u044e \u0432\u0435\u0449\u044c:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/c7c\/eee\/fd6\/c7ceeefd6a5043beb9af1ba2ac028383.png\"\/><\/p>\n<p>  \u041d\u043e \u044f \u0441 \u0440\u0430\u0434\u043e\u0441\u0442\u044c\u044e \u0433\u043e\u0442\u043e\u0432 \u0441\u043e\u043e\u0431\u0449\u0438\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u043e \u2014 \u043d\u0435 \u043f\u0440\u0430\u0432\u0434\u0430. \u041f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u0440\u044f\u043c\u043e \u0438\u0437 Android Studio \u043c\u043e\u0436\u043d\u043e! \u0411\u043e\u043b\u0435\u0435 \u0442\u043e\u0433\u043e, \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u0431\u0435\u0437 Android Studio \u043d\u0430 \u0432\u0430\u0448\u0435\u043c CI \u2014 \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0435\u043b\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e Gradle task.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-281658","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/281658","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=281658"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/281658\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=281658"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=281658"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=281658"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}