Upload apk or bundle to Google Play via command line script

In short:

  • Create a service account and get its authorization data json file.
  • Make a script uses above json and Google Play Developer API to perform actions on your account.

Prerequisites:

  • You can make Kotlin code. Android Studio is good enough. You can create a pure Kotlin non-Android module and then run it.

Step 1. Create a service account and get access keys json file

  • In “Service Accounts” section, choose “Create Service Account”.
  • You will be sent to “Google Cloud Plaform” console. Continue creating account there.
  • Give it a chosen name and ID.
  • Set “Service Account User” role.
  • Create private key and download it as a json file. We’ll use it later.
  • No need to grant any additional access here.
  • Copy e-mail of a created user. Should look like some_id@api-12345–12345.iam.gserviceaccount.com
  • Return to “Service Accounts” in Google Play. Created account should be visible there. If not, invite it using saved e-mail.
  • Press “Grant Access” button next to your service account and allow it to upload apks. When finished choosing permissions, press “add”.

Another description of this process (“Create Google service account” section):
https://plugins.jenkins.io/google-play-android-publisher

Step 2. Create a Kotlin code that can log you in and upload apk/bundle

Full example:
https://github.com/stasheq/google-play-apk-upload

Dependencies

plugins {
id("kotlin")
id("application")
}

application {
mainClassName = "me.szymanski.apkupload.Main"
}

dependencies {
implementation("com.google.auth:google-auth-library-oauth2-http:0.22.0")
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20201022-1.30.10")
}

Uploading function

private fun setHttpTimeout(requestInitializer: HttpRequestInitializer): HttpRequestInitializer {
return HttpRequestInitializer { request ->
requestInitializer.initialize(request)
request.connectTimeout = 3 * 60000
request.readTimeout = 3 * 60000
}
}

private fun uploadApk(appId: String, apkPath: String, credentialsPath: String) {
val credentials = ServiceAccountCredentials
.fromStream(FileInputStream(credentialsPath))
.createScoped(AndroidPublisherScopes.all())

val publisher = AndroidPublisher.Builder(
GoogleNetHttpTransport.newTrustedTransport(),
JacksonFactory.getDefaultInstance(),
setHttpTimeout(HttpCredentialsAdapter(credentials))
).setApplicationName("Google Play APK upload").build()

val edit: AppEdit = publisher.edits().insert(appId, null).execute()

println("Created edit: ${edit.id}")

// can use publisher.edits().apks()
val apk: Bundle = publisher.edits().bundles().upload(
appId,
edit.id,
FileContent("application/octet-stream", File(apkPath))
).execute()

println("Uploaded apk versionCode: ${apk.versionCode}")

publisher.edits().commit(appId, edit.id).execute()
println("Committed edit with a new apk")
}

Sources of my knowledge:
https://github.com/googleapis/google-api-java-client-services
https://github.com/googleapis/google-auth-library-java

Step 3. Use above code to upload apk/bundle from console

cd ./google-play-apk-upload/
./gradlew apk_upload:installDist
cd apk_upload/build/install/apk_upload/bin

installDist is a command building this app.

Run with your params: app id, apk path, json credentials file created before. In below example all these files are in the same folder as executable file.

./apk_upload my.test.app.id app-release.aab api-12345.json

Success! App is now visible in Artifact Library.

My use case was to upload it there, without publishing it. It can be now used to create a release etc.
For your specific use case, check API reference for all available actions.

Full API reference, including above commands:
https://developers.google.com/android-publisher/api-ref/

Android developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store