Android Dev

Learn how to Routinely Add an Android App Bundle to the Play Retailer

By Zaid Humayun

On this article, I will clarify routinely add an Android App Bundle (.aab file) to the Play Retailer’s beta monitor. We’ll use Android Studio and AWS as a cloud infrastructure supplier.

As soon as we have uploaded the app bundle, we’ll set off a Slack notification.

This can be a invaluable use of your time for various causes, like creating observability and prioritizing processes.

Tech We’ll Use

Listed below are the assets we’re going to be utilizing for this tutorial:

  1. Android Studio
  2. AWS CodeBuild
  3. AWS Lambda
  4. S3
  5. Slack

Excessive Stage Overview of the Challenge

beta_track_upload_flow-1

The above picture exhibits you a basic overview of how we’ll construction the entire thing.

Primarily, there must be a Code Pipeline arrange on AWS on your Android repository. This Code Pipeline may have Code Construct as certainly one of its phases.

Pushing to the grasp department of your Android app repository goes to set off Code Construct. The Code Construct venture will signal the Android app from the command line and add the artifact to an S3 bucket.

Importing the bundle to S3 will set off a Lambda, which is able to obtain the bundle and add it to the Play Retailer utilizing the Google Publishing API. As soon as it will get a 200 response, the Lambda will then set off a Slack notification.

Learn how to Get Your Google Play Service Account Key

To have the ability to use the Google Play Writer API, you will have a Google Play Service Account key.

A service account is an account that may act in your behalf when servers are speaking with one another. You’ll be able to learn extra about how Google makes use of OAuth2.0 for server to server communication here.

To see create a service account and provides it entry to the Google Play Writer API, look here.

As soon as you’ve got created your service account and given it the suitable permissions, make certain to obtain the service account key and hold it protected. You may be importing this to an S3 bucket quickly.

Learn how to Signal the Android Bundle

The principle factor to determine is signal the Android App Bundle. Google has pretty respectable documentation on it that you will discover here.

I am going to summarize the hyperlinks under.

Generate a non-public key utilizing keytool like this:

keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-alias

You’ll be able to name your key no matter you need. Right here, I known as it my-release-key.jks. You may also select no matter alias you need. All through this tutorial make certain to make use of the right identify and alias on your key.

Open construct.gradle inside your app listing in Android Studio and add the next code block to it:

android {
    ...
    defaultConfig { ... }
    signingConfigs {
        launch {
            // That you must specify both an absolute path or embrace the
            // keystore file in the identical listing because the construct.gradle file.
            storeFile file("my-release-key.jks")
            storePassword "password"
            keyAlias "my-alias"
            keyPassword "password"
        }
    }
    buildTypes {
        launch {
            signingConfig signingConfigs.launch
            ...
        }
    }
}

Should you modified the identify of your launch key to one thing aside from the default, make certain to specify the brand new identify. Similar factor for the alias.

Your retailer password will likely be no matter password you generated if you first uploaded your app to the Play Retailer.

Now, if you run the command ./gradlew :app:bundleRelease from the command line in Android Studio, you will discover that it generates a signed App Bundle.

Learn how to Scrub Signing Data

Committing code with the signing info accessible as plain textual content within the construct.gradle file is a safety danger and may very well be an assault vector.

Google has documentation round this that you will discover here.

First, create a keystore.properties file within the root of your venture listing.

The contents of the file needs to be as under:

storePassword=myStorePassword
keyPassword=myKeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation

Your retailer password and key password would be the password you used if you uploaded your app bundle to the App Retailer the primary time.

Your keyAlias and storeFile would be the alias you assigned when creating your non-public key and the situation of the non-public key you created, respectively.

Now, we have to load this file into construct.gradle. This got here as a shock to me initially, however Gradle really works as a DSL. So, it makes it simpler to put in writing configuration utilizing Gradle.

//  Load properties from keystore.properties
def keystorePropertiesFile = rootProject.file("keystore.properties")

//  Creating a brand new Properties() object
def keystoreProperties = new Properties()

//  If keystorePropertiesFile exists, learn from that, else set from construct surroundings
if (keystorePropertiesFile.exists()) {
    //  Loading the keystoreProperties file
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} else {
    //  Learn all surroundings variables from the construct surroundings
    keystoreProperties.setProperty("storeFile", "${System.getenv('STORE_FILE')}")
    keystoreProperties.setProperty("keyAlias", "${System.getenv('KEY_ALIAS')}")
    keystoreProperties.setProperty("keyPassword", "${System.getenv('KEY_PASSWORD')}")
    keystoreProperties.setProperty("storePassword", "${System.getenv('STORE_PASSWORD')}")
}

You may discover the if situation in there – don’t fret about it for now. It is there particularly to account for Code Construct later.

When you do that, change your signingConfigs part in construct.gradle to appear to be this:

signingConfigs {
        launch {
            storeFile file(keystoreProperties['storeFile'])
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storePassword keystoreProperties['storePassword']
        }
    }

Learn how to Set Up AWS Code Pipeline

I am not going to enter an excessive amount of element on this one since its comparatively simple.

Arrange an AWS Code Pipeline with the next three phases:

  1. Supply stage related to your GitHub repository’s grasp department
  2. Construct stage related to AWS Code Construct
  3. Deploy stage which is able to deploy to an S3 bucket.

You will discover extra documentation about establishing a Code Pipeline here.

Learn how to Set Up AWS S3

First, be sure you have a Code Pipeline arrange with Code Construct as one of many phases. Subsequent, arrange two S3 buckets:

  1. A bucket to retailer your launch key in. I am calling this bucket release-key.jks
  2. A bucket by which you’ll retailer your Google Play Service Account non-public key. (You must have downloaded this key whereas creating your service account.)

You’ll need to permit entry to those buckets out of your Code Construct service position. Your Code Construct service position ought to have been created if you arrange your Code Pipeline.

Head over to the IAM console and discover your Code Construct service position and seize the ARN.

Subsequent, use the console to get to the Permissions tab for the bucket release-key.jks and add the next coverage there:

{
    "Model": "2012-10-17",
    "Assertion": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::123456789:role/service-role/codebuild-service-role-dummy",
                ]
            },
            "Motion": "s3:GetObject",
            "Useful resource": "arn:aws:s3:::release-key-bucket/*"
        }
    ]
}

This coverage will permit entry to the S3 bucket from the machine the place your CodeBuild venture will execute.

You’ll need to exchange the ARNs talked about above with the ARNs on your account. Make sure that to specify the right ARN for the Code Construct service position if you’re updating the coverage.

You need not change the permissions coverage for the second bucket. We’ll add the related permissions to the AWS Lambda position to permit it to entry the bucket.

Learn how to Set Up AWS CodeBuild

Subsequent, create a buildspec.yml file in your venture root folder.

model: 0.2

phases:
  construct:
    instructions:
      - aws s3api get-object --bucket release-key.jks --key release-key.jks ./releaseKey.jks
      - cp ./releaseKey.jks ${CODEBUILD_SRC_DIR}/app/releaseKey.jks
      - export STORE_FILE=releaseKey.jks
      - export KEY_ALIAS=$keyAlias
      - export KEY_PASSWORD=$keyPassword
      - export STORE_PASSWORD=$storePassword
      - ./gradlew :app:bundleRelease

artifacts:
  information:
    - app/construct/outputs/bundle/launch/app-release.aab

This file is fairly easy. It fetches the discharge key from the bucket specified and saves it into a neighborhood file on the Code Construct server into the situation specified.

Subsequent, export all of the variables required for the construct.gradle configuration to work accurately. Lastly, run Gradle’s launch command from the command line.

Earlier than you may run this script in Code Construct, you will want so as to add the variables to the Code Construct surroundings. To do that, first go to the AWS Code Construct console and decide your construct venture on your Android app.

Subsequent, choose Edit > Atmosphere like within the screenshot under:

aws_code_build_menu_screenshot

On the display that pops up when you do that, choose the Extra Configuration dropdown. There you will see an possibility so as to add surroundings variables by key worth pairs.

Now when Code Construct runs the buildspec.yml file, will probably be capable of export the required variables.

As issues stand proper now, when your pipeline runs, Code Construct will have the ability to obtain the non-public key to signal and construct your Android app and add the signed bundle to an S3 bucket.

Learn how to Set Up the Slack App

Observability is a trademark of automation. You wish to know when your automation runs, whether or not it succeeds or fails, and if it fails, the explanation for failure.

The best way AWS sometimes handles observability is thru CloudWatch. However I believe a Slack integration serves the aim simply as properly.

The best solution to combine Slack into your automation workflows is to arrange a Slack app and ship a notification to that app out of your automation workflow.

To discover ways to arrange a Slack app, view the documentation here. The method is tremendous simple and you need to have an app up and operating in a couple of minutes.

As soon as you’ve got created the app, you’re going to get a WebHook URL you should use to name the app to put up into the related channel. Preserve monitor of this WebHook URL as a result of we’ll be utilizing this with the AWS Lambda operate.

Learn how to Set Up AWS Lambda

Up to now, we’ve got an Android App Bundle being signed, constructed, and uploaded to an S3 bucket. Subsequent, we have to work out add the bundle to the beta monitor on the Play Retailer.

The best way to do that is to arrange an AWS Lambda which will likely be triggered when the bundle is uploaded to the S3 bucket. When this set off happens, the Lambda will run, obtain the bundle, seize the service account key, and add the bundle to the Play Retailer beta monitor.

As soon as you’ve got created a Lambda and added a set off to run it when a file is uploaded to the bucket, take a look at the code under:

"""This Python3 script is used to add a brand new .aab bundle to the play retailer. The execution of this Python script
    happens by an AWS Lambda which is invoked when a brand new file is uploaded to the related S3 buckets"""

import json
import boto3
import os
from urllib import request, parse
from google.oauth2 import service_account
import googleapiclient.discovery


SCOPES = ['https://www.googleapis.com/auth/androidpublisher']


package_name = 'com.app.identify'


slack_webhook_url = os.environ['SLACK_WEBHOOK_URL']

def send_slack_message(message):
    information = json.dumps({ 'textual content': message })
    post_data = information.encode('utf-8')
    req = request.Request(slack_webhook_url, information=post_data, headers={ 'Content material-Sort': 'software/json' })
    request.urlopen(req)


def lambda_handler(occasion, context):
    
    s3 = boto3.shopper('s3')
    s3.download_file('service-account-bucket-key', 'service-account-bucket-key.json', '/tmp/service-account-key.json')
    SERVICE_ACCOUNT_FILE = '/tmp/service-account-key.json'

    
    bucket_name = occasion['Records'][0]['s3']['bucket']['name']
    file_key = occasion['Records'][0]['s3']['object']['key']
    s3.download_file(bucket_name, file_key, '/tmp/app-release.aab')
    APP_BUNDLE = '/tmp/app-release.aab'

    print(f"A bundle uploaded to {bucket_name} has triggered the Lambda")

    
    credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES
    )
    service = googleapiclient.discovery.construct('androidpublisher', 'v3', credentials=credentials, cache_discovery=False)

    
    edit_request = service.edits().insert(physique={}, packageName=package_name)
    end result = edit_request.execute()
    edit_id = end result['id']

    
    strive:
        bundle_response = service.edits().bundles().add(
            editId=edit_id,
            packageName=package_name,
            media_body=APP_BUNDLE,
            media_mime_type="software/octet-stream"
        ).execute()
    besides Exception as err:
        message = f"There was an error whereas importing a brand new model of {package_name}"
        send_slack_message(message)
        increase err

    print(f"Model code {bundle_response['versionCode']} has been uploaded")

    
    track_response = service.edits().tracks().replace(
        editId=edit_id,
        monitor='beta',
        packageName=package_name,
        physique={u'releases': [{
            u'versionCodes': [str(bundle_response['versionCode'])],
            u'standing': u'accomplished',
        }]}
    ).execute()

    print("The bundle has been dedicated to the beta monitor")

    
    commit_request = service.edits().commit(
        editId=edit_id,
        packageName=package_name
    ).execute()

    print(f"Edit {commit_request['id']} has been dedicated")

    message = f"Model code {bundle_response['versionCode']} has been uploaded from the bucket {bucket_name}.nEdit {commit_request['id']} has been dedicated"
    send_slack_message(message)

    return {
        'statusCode': 200,
        'physique': json.dumps('Efficiently executed the app bundle launch to beta')
    }

The Lambda above will use the googleapiclient library and its discovery module to construct the URL for Google Play’s Publishing API.

Subsequent, the Lambda will obtain the service account key from the bucket you arrange earlier. You may must be sure you specify the right bucket names.

Relying on whether or not the add succeeds or fails, we wish a Slack message to exit. Add the Slack WebHook URL from the earlier part into the surroundings variables for the Lambda. The operate above makes use of Python’s os module to get entry to the surroundings variable and put up the message to Slack.

In case your Lambda fails, it could be as a result of your Lambda doesn’t have permissions to entry the S3 bucket the place the important thing on your Google Play service account is saved. In that case, you will note an error message indicating this.

To repair this, you merely want so as to add the related permissions to your Lambda position.

Right here is the coverage you will have so as to add:

{
    "Model": "2012-10-07",
    "Assertion": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObjectVersion",
                "s3:GetBucketVersioning",
                "s3:GetBucketAcl",
                "s3:GetObject",
                "s3:GetBucketTagging",
                "s3:GetBucketLocation",
                "s3:GetObjectVersionAcl"
            ],
            "Useful resource": [
                "arn:aws:s3:::arn:aws:s3:::your-bucket-name-with-service-account-key"
            ]
        }
    ]
}

Change the ARN for the bucket with the related one on your account and you ought to be good to go.

Conclusion

So, there you have got it. It undoubtedly wasn’t simple and there is a variety of transferring elements, however that is an automation that can prevent a variety of effort and time.

Should you’re a part of a group that’s incessantly releasing new app updates, you do not wish to be hindered by the absence of 1 individual whose job it’s to launch the replace.

Constructing this type of automation makes your CI/CD workflow quite a bit smoother and extra sturdy.

Should you’re curious about blogs like this, you may learn extra at https://redixhumayun.github.io or observe me on Twitter.

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button

Adblock Detected

Please consider supporting us by disabling your ad blocker