Автоматизация публикации приложения в Google Play

Если у вас есть андроид приложение, которые вы собираетесь опубликовать в Google Play или оно уже опубликовано, а так же если вы его только разрабатываете, и оно находится в закрытом бета тестировании, а заказчику\тестерам периодически нужно собирать и передавать сборку руками, возможно лучше автоматизировать этот процесс
Вы можете самостоятельно разобраться с документацией на английском языке, но если процесс вызывает сложности или что-то не получается, надеюсь публикация окажется полезной

Перед началом работы нужно вручную опубликовать первую версию приложения

Настройка доступа


Откройте Google Play Developer Console и перейдите в меню SettingsAPI access
Консоль нужно связать c Google API проектом. Если у вас нет проекта, то на данный момент из интерфейса будет доступна только кнопка Create new project
333fbc3959d449a3afba1170b0c1e47e.jpg

Если же есть, то будет доступен список этих проектов. Кнопка link понятное дело их связывает
e14cbf2fd25b4a058094d2d8c4094a01.jpg

Но давайте рассмотрим пример с созданием нового проекта. Его можно создать из Google API Console или прямо из Google Play Developer Console, который у вас уже открыт, нажмите кнопку Create new project

Если вы первый раз создаете Google API проект, появится popup-окошко, тут Accept All
92738f30489a44ea8e82d26b270a7153.jpg

Отлично, теперь у вас есть проект и service account, который в данном случае создается автоматически
3458cee80bcd47bbb022c836737b120f.jpg

Сейчас нужно разрешить этому аккаунту работать с Google Play и дать ему роль. Это так же можно сделать прямо отсюда, нажмите Grant access. Вас перекинет в меню User accounts & right (видно на заднем плане) и откроется popup-окошко, где email и остальные обязательные поля уже будут заполнены. Нажмите Add user
66b895ec8af34c9d88c6f23d71be69bf.jpg

Сейчас вы видите, что ваш пользователь успешно добавлен
6405755ffa084992a196719efe2645a7.jpg

Теперь нужно сгенерировать пару публичный и приватный ключи, чтобы можно было работать с библиотеками и публиковать новые версии приложения. Для этого откройте список проектов в Google API Console и кликните по нужному, по умолчанию он называется Google Play Android Developer
c72a34cf3f8f42259dd0ebb869852b72.jpg

Далее нажмите пункт меню CredentialsCreate credentials, выберите Service account key
c899775c945e4f2ea37fa924c35315d0.jpg

Выберите из списка Compute Engine default service account и перекиньте radio button на P12. Нажмите Create
44390482187443429b3dbc4722eeb770.jpg

С настройками все, пара публичный и приватный ключи с названием Google Play Android Developer-%кусочек id%.p12 была сохранена на компе
166b540dc6864115b47660aeff46887e.jpg
996f28da7dd940cfa78e20d823abb049.jpg

Приступим к программированию


Сейчас Google предоставляет библиотеки на java и python для работы с Google Play Developer API. Мы рассмотрим вариант на java, для этого скачайте zip-архив с библиотеками и примерами
Откройте вашу IDE и создайте проект, свой я назвал ProductionManager, подключите следующие библиотеки:

  • google-api-client-1.19.0.jar
  • google-api-services-androidpublisher-v2-rev20141111–1.19.0.jar
  • google-http-client-1.19.0.jar
  • google-http-client-jackson2–1.19.0.jar
  • google-oauth-client-1.19.0.jar
  • jackson-core-2.1.3.jar


Скопируйте файл Google Play Android Developer-%кусочек id%.p12 в resource папку вашего проекта, я его переименовал чтобы не было пробелов, кажется у меня было исключение при попытке его прочитать. Если вы будете держать исходники этого проекта в VCS куда есть доступ у многих людей, наверное лучше хранить файл на сервере непрерывной интеграции, тут на ваше усмотрение.

Перед сборкой и соответственно публикацией приложения я делаю запрос, чтобы узнать последнюю версию кода (versionCode) и обновить ее, потому что мне нравится чтобы они шли в арифметической прогрессии с шагом 1(те просто 1, 2, 3…), вы можете просто подставить buildNumber из CI сервера или revesionNumber из вашей VCS, тоже на ваше усмотрение

Обновление версии
private Edits getApplicationEdits() throws Exception {
        String applicationName = "ApplicationName";
        String serviceAccountId = "и-го-го@developer.gserviceaccount.com"
        
        HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
        
        KeyStore keyStore = SecurityUtils.getPkcs12KeyStore();
        InputStream inputStream = ProductionManager.class.getResourceAsStream("/resources/key.p12");
        PrivateKey privateKey = SecurityUtils.loadPrivateKeyFromKeyStore(keyStore, inputStream, "notasecret", "privatekey", "notasecret");
        inputStream.close();
        
        GoogleCredential credential = new GoogleCredential
                        .Builder()
                        .setTransport(httpTransport)
                        .setJsonFactory(jsonFactory)
                        .setServiceAccountId(serviceAccountId)
                        .setServiceAccountScopes(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER))
                        .setServiceAccountPrivateKey(privateKey)
                        .build();
        
        AndroidPublisher androidPublisher = new AndroidPublisher
                        .Builder(httpTransport, jsonFactory, credential)
                        .setApplicationName(applicationName)
                        .build();
        
        return androidPublisher.edits();
}

private void updateVersionCode() throws Exception {
        String packageName = "com.package.name";
        
        Edits edits = getApplicationEdits();

        String editId = edits
                        .insert(packageName, null)
                        .execute()
                        .getId();
        
        ApksListResponse apksResponse = edits
                        .apks()
                        .list(packageName, editId)
                        .execute();
        
        List versions = new ArrayList();
        List apks = apksResponse.getApks();
        if (apks == null) {
                throw new Exception("responsed list of apks is null");
        }
        
        for (Apk apk : apks) {
                versions.add(apk.getVersionCode());
        }
        
        if (versions.isEmpty()) {
                throw new Exception("previous versions are not detected");
        }
        
        int lastVersionCode = Collections.max(versions);
        if (lastVersionCode == 0) {
                throw new Exception("version is 0");
        }
        
        //TODO update version code
        //++lastVersionCode
}



Публикации приложения
private void publishApplication() throws Exception {
        String packageName = "com.package.name";
        String track = "beta";//release
        String setupFilePath = "/path/to/setup/file/app.apk";
        
        Edits edits = getApplicationEdits();

        String editId = edits
                        .insert(packageName, null)
                        .execute()
                        .getId();
        
        FileContent mediaContent = new FileContent("application/vnd.android.package-archive", new File(setupFilePath));
        Upload uploadRequest = edits
                        .apks()
                        .upload(packageName, editId, mediaContent);
        
        Apk apk = uploadRequest.execute();

        List versions = new ArrayList<>();
        versions.add(apk.getVersionCode());
        
        edits
                .tracks()
                .update(packageName, editId, track, new Track().setVersionCodes(versions))
                .execute();
        
        edits
                .commit(packageName, editId)
                .execute();
}



Скомпилируйте executable jar. Теперь нужно настроить создание инсталляционного файла и его публикацию на вашем CI сервере, я использую TeamCity. Получается, у нас будет 4 build steps:

  1. Обновление versionСode. Запустите jar-файл с параметром, который вы обработаете и вызовите метод updateVersionCode (). Вот так это выглядит на TeamCity
    0b5c5f0855874724a2ca1a024323c6dd.jpg
  2. Обновление versionName
  3. Генерация apk. Можно командной строкой, можно выбрать специальный step, есть gradle и IntelliJ IDEA, во втором случае нужно указать имя artifact-а
  4. Публикация приложения. Так же запускаете jar-файл с другим параметром и вызываете метод publishApplication ()

Спасибо за внимание

© Habrahabr.ru