Computer >> คอมพิวเตอร์ >  >> ระบบ >> Android

วิธีอัปโหลด Android App Bundle ไปยัง Play Store โดยอัตโนมัติ

ในบทความนี้ ฉันจะอธิบายวิธีอัปโหลด Android App Bundle (ไฟล์ .aab) ไปยังแทร็กเบต้าของ Play Store โดยอัตโนมัติ เราจะใช้ Android Studio และ AWS เป็นผู้ให้บริการโครงสร้างพื้นฐานระบบคลาวด์

เมื่อเราอัปโหลด App Bundle แล้ว เราจะเรียกใช้การแจ้งเตือน Slack

นี่เป็นการใช้เวลาอย่างคุ้มค่าด้วยเหตุผลหลายประการ เช่น การสร้างความสามารถในการสังเกตและจัดลำดับความสำคัญของกระบวนการ

เทคโนโลยีที่เราจะใช้

นี่คือแหล่งข้อมูลที่เราจะใช้สำหรับบทช่วยสอนนี้:

  1. Android Studio
  2. AWS CodeBuild
  3. AWS แลมบ์ดา
  4. S3
  5. หย่อน

ภาพรวมระดับสูงของโครงการ

วิธีอัปโหลด Android App Bundle ไปยัง Play Store โดยอัตโนมัติ

ภาพด้านบนแสดงให้คุณเห็นภาพรวมทั่วไปเกี่ยวกับวิธีที่เราจะจัดโครงสร้างสิ่งทั้งหมด

โดยพื้นฐานแล้ว จำเป็นต้องมี Code Pipeline ที่ตั้งค่าไว้บน AWS สำหรับที่เก็บ Android ของคุณ Code Pipeline นี้จะมี Code Build เป็นหนึ่งในขั้นตอนของมัน

การพุชไปยังมาสเตอร์แบรนช์ของที่เก็บแอป Android ของคุณจะเป็นการทริกเกอร์ Code Build โปรเจ็กต์ Code Build จะลงนามแอป Android จากบรรทัดคำสั่งและอัปโหลดอาร์ติแฟกต์ไปยังบัคเก็ต S3

การอัปโหลดบันเดิลไปยัง S3 จะทริกเกอร์ Lambda ซึ่งจะดาวน์โหลดบันเดิลและอัปโหลดไปยัง Play Store โดยใช้ Google Publishing API เมื่อได้รับการตอบกลับ 200 ครั้ง จากนั้น Lambda จะทริกเกอร์การแจ้งเตือน Slack

วิธีรับรหัสบัญชีบริการ Google Play ของคุณ

หากต้องการใช้ Google Play Publisher API คุณจะต้องมีรหัสบัญชีบริการ Google Play

บัญชีบริการคือบัญชีที่สามารถดำเนินการแทนคุณได้เมื่อเซิร์ฟเวอร์กำลังสื่อสารกัน คุณสามารถอ่านเพิ่มเติมเกี่ยวกับวิธีที่ Google ใช้ OAuth2.0 สำหรับการสื่อสารระหว่างเซิร์ฟเวอร์กับเซิร์ฟเวอร์ได้ที่นี่

ดูวิธีสร้างบัญชีบริการและให้สิทธิ์เข้าถึง Google Play Publisher API ได้ที่นี่

เมื่อคุณสร้างบัญชีบริการและให้สิทธิ์ที่เหมาะสมแล้ว อย่าลืมดาวน์โหลดรหัสบัญชีบริการและเก็บไว้ให้ปลอดภัย คุณจะอัปโหลดสิ่งนี้ไปยังที่เก็บข้อมูล S3 ในไม่ช้า

วิธีการลงชื่อใน Android Bundle

สิ่งสำคัญที่ควรทราบคือวิธีลงชื่อใน Android App Bundle Google มีเอกสารประกอบที่ดีพอสมควรซึ่งคุณสามารถหาได้ที่นี่

ฉันจะสรุปลิงก์ด้านล่าง

สร้างคีย์ส่วนตัวโดยใช้ keytool แบบนี้:

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

คุณสามารถเรียกคีย์ของคุณอะไรก็ได้ที่คุณต้องการ ผมเรียกมันว่า my-release-key.jks . คุณยังสามารถเลือกนามแฝงใดก็ได้ที่คุณต้องการ ตลอดบทแนะนำนี้ โปรดใช้ชื่อและนามแฝงที่ถูกต้องสำหรับคีย์ของคุณ

เปิด build.gradle ภายใน app . ของคุณ ไดเร็กทอรีใน Android Studio และเพิ่มบล็อกโค้ดต่อไปนี้:

android {
    ...
    defaultConfig { ... }
    signingConfigs {
        release {
            // You need to specify either an absolute path or include the
            // keystore file in the same directory as the build.gradle file.
            storeFile file("my-release-key.jks")
            storePassword "password"
            keyAlias "my-alias"
            keyPassword "password"
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.release
            ...
        }
    }
}

หากคุณเปลี่ยนชื่อคีย์ปล่อยเป็นอย่างอื่นที่ไม่ใช่ค่าเริ่มต้น ตรวจสอบให้แน่ใจว่าได้ระบุชื่อใหม่ สิ่งเดียวกันสำหรับนามแฝง

รหัสผ่านร้านค้าของคุณจะเป็นรหัสผ่านใดก็ตามที่คุณสร้างขึ้นเมื่อคุณอัปโหลดแอปไปยัง Play Store เป็นครั้งแรก

ตอนนี้เมื่อคุณรันคำสั่ง ./gradlew :app:bundleRelease จากบรรทัดคำสั่งใน Android Studio คุณจะสังเกตเห็นว่าสร้าง App Bundle ที่มีการลงชื่อ

วิธีการขัดข้อมูลการเซ็นชื่อ

รหัสการคอมมิตพร้อมข้อมูลการลงนามที่มีให้ในรูปแบบข้อความธรรมดาใน build.gradle ไฟล์มีความเสี่ยงด้านความปลอดภัยและอาจเป็นเวกเตอร์โจมตี

Google มีเอกสารเกี่ยวกับเรื่องนี้ที่คุณสามารถพบได้ที่นี่

ขั้นแรก สร้าง keystore.properties ไฟล์ในรูทของไดเร็กทอรีโครงการของคุณ

เนื้อหาของไฟล์ควรเป็นดังนี้:

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

รหัสผ่านร้านค้าและรหัสผ่านคีย์จะเป็นรหัสผ่านที่คุณใช้เมื่อคุณอัปโหลด App Bundle ไปยัง App Store ในครั้งแรก

keyAliasของคุณ และ storeFile จะเป็นนามแฝงที่คุณกำหนดเมื่อสร้างคีย์ส่วนตัวและตำแหน่งของคีย์ส่วนตัวที่คุณสร้างขึ้นตามลำดับ

ตอนนี้เราต้องโหลดไฟล์นี้ไปที่ build.gradle . สิ่งนี้ทำให้ฉันประหลาดใจในตอนแรก แต่ Gradle ใช้งานได้จริงเป็น DSL ดังนั้นจึงทำให้เขียนการกำหนดค่าได้ง่ายขึ้นโดยใช้ Gradle

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

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

//  If keystorePropertiesFile exists, read from that, else set from build environment
if (keystorePropertiesFile.exists()) {
    //  Loading the keystoreProperties file
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
} else {
    //  Read all environment variables from the build environment
    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')}")
}

คุณจะสังเกตเห็นเงื่อนไข if ในนั้น – ไม่ต้องกังวลกับมันในตอนนี้ มีไว้เพื่อใช้สร้างโค้ดในภายหลังโดยเฉพาะ

เมื่อคุณทำสิ่งนี้แล้ว ให้เปลี่ยน signingConfigs . ของคุณ ส่วนใน build.gradle มีลักษณะดังนี้:

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

วิธีตั้งค่าไปป์ไลน์โค้ด AWS

ฉันจะไม่ลงรายละเอียดมากเกินไปในเรื่องนี้เพราะมันค่อนข้างตรงไปตรงมา

ตั้งค่า AWS Code Pipeline ด้วยสามขั้นตอนต่อไปนี้:

  1. ซอร์สสเตจที่เชื่อมต่อกับ master ของที่เก็บ GitHub ของคุณ สาขา
  2. สร้างสเตจที่เชื่อมต่อกับ AWS Code Build
  3. ขั้นตอนการปรับใช้ซึ่งจะปรับใช้กับบัคเก็ต S3

คุณสามารถอ่านเอกสารเพิ่มเติมเกี่ยวกับการตั้งค่า Code Pipeline ได้ที่นี่

วิธีตั้งค่า AWS S3

ขั้นแรก ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่า Code Pipeline ด้วย Code Build เป็นหนึ่งในขั้นตอน ถัดไป ตั้งค่าที่เก็บข้อมูล S3 สองถัง:

  1. ที่เก็บข้อมูลสำหรับเก็บรหัสการเปิดตัวของคุณ ฉันเรียกที่เก็บข้อมูลนี้ว่า release-key.jks
  2. ถังที่คุณจะเก็บคีย์ส่วนตัวของบัญชีบริการ Google Play (คุณควรดาวน์โหลดคีย์นี้ขณะสร้างบัญชีบริการ)

คุณจะต้องอนุญาตให้เข้าถึงบัคเก็ตเหล่านี้จากบทบาทบริการ Code Build ของคุณ บทบาทบริการ Code Build ของคุณควรถูกสร้างขึ้นเมื่อคุณตั้งค่า Code Pipeline ของคุณ

ไปที่คอนโซล IAM และค้นหาบทบาทบริการ Code Build ของคุณและคว้า ARN

ถัดไป ใช้คอนโซลเพื่อไปที่แท็บสิทธิ์สำหรับที่เก็บข้อมูล release-key.jks และเพิ่มนโยบายต่อไปนี้ที่นั่น:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::123456789:role/service-role/codebuild-service-role-dummy",
                ]
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::release-key-bucket/*"
        }
    ]
}

นโยบายนี้จะอนุญาตให้เข้าถึงบัคเก็ต S3 จากเครื่องที่โปรเจ็กต์ CodeBuild ของคุณจะดำเนินการ

คุณจะต้องแทนที่ ARN ที่กล่าวถึงข้างต้นด้วย ARN สำหรับบัญชีของคุณ อย่าลืมระบุ ARN ที่ถูกต้องสำหรับบทบาทบริการ Code Build เมื่อคุณอัปเดตนโยบาย

คุณไม่จำเป็นต้องเปลี่ยนนโยบายการอนุญาตสำหรับที่เก็บข้อมูลที่สอง เราจะเพิ่มการอนุญาตที่เกี่ยวข้องให้กับบทบาท AWS Lambda เพื่อให้สามารถเข้าถึงบัคเก็ตได้

วิธีตั้งค่า AWS CodeBuild

ต่อไป สร้าง buildspec.yml ไฟล์ในโฟลเดอร์รูทของโปรเจ็กต์ของคุณ

version: 0.2

phases:
  build:
    commands:
      - 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:
  files:
    - app/build/outputs/bundle/release/app-release.aab

ไฟล์นี้ค่อนข้างง่าย โดยจะดึงรีลีสคีย์จากบัคเก็ตที่ระบุและบันทึกลงในไฟล์ในเครื่องบนเซิร์ฟเวอร์ Code Build ไปยังตำแหน่งที่ระบุ

ถัดไป ส่งออกตัวแปรทั้งหมดที่จำเป็นสำหรับ build.gradle การกำหนดค่าให้ทำงานได้อย่างถูกต้อง สุดท้าย ให้รันคำสั่ง release ของ Gradle จากบรรทัดคำสั่ง

ก่อนที่คุณจะสามารถเรียกใช้สคริปต์นี้ใน Code Build คุณจะต้องเพิ่มตัวแปรในสภาพแวดล้อม Code Build ในการดำเนินการนี้ ก่อนอื่น ให้ไปที่คอนโซล AWS Code Build แล้วเลือกโปรเจ็กต์บิลด์ของคุณสำหรับแอป Android

จากนั้นเลือก แก้ไข> สภาพแวดล้อม เหมือนในภาพหน้าจอด้านล่าง:

วิธีอัปโหลด Android App Bundle ไปยัง Play Store โดยอัตโนมัติ

บนหน้าจอที่ปรากฏขึ้นเมื่อคุณทำเช่นนี้ ให้เลือกดรอปดาวน์การกำหนดค่าเพิ่มเติม คุณจะเห็นตัวเลือกในการเพิ่มตัวแปรสภาพแวดล้อมผ่านคู่ค่าคีย์

ตอนนี้เมื่อ Code Build รัน buildspec.yml ไฟล์ก็จะสามารถส่งออกตัวแปรที่ระบุได้

ในตอนนี้ เมื่อไปป์ไลน์ของคุณทำงาน Code Build จะสามารถดาวน์โหลดคีย์ส่วนตัวเพื่อลงนามและสร้างแอป Android ของคุณและอัปโหลดบันเดิลที่ลงนามไปยังบัคเก็ต S3

วิธีตั้งค่าแอป Slack

การสังเกตเป็นจุดเด่นของระบบอัตโนมัติ คุณต้องการทราบว่าการทำงานอัตโนมัติของคุณทำงานเมื่อใด ไม่ว่าจะสำเร็จหรือล้มเหลว และล้มเหลว สาเหตุของความล้มเหลว

วิธีที่ AWS มักจะจัดการกับการสังเกตได้คือผ่าน CloudWatch แต่ฉันคิดว่าการผสานรวม Slack ก็มีจุดมุ่งหมายเช่นกัน

วิธีที่ง่ายที่สุดในการรวม Slack เข้ากับเวิร์กโฟลว์การทำงานอัตโนมัติของคุณคือการตั้งค่าแอป Slack และส่งการแจ้งเตือนไปยังแอปนั้นจากเวิร์กโฟลว์การทำงานอัตโนมัติของคุณ

หากต้องการเรียนรู้วิธีตั้งค่าแอป Slack โปรดดูเอกสารประกอบที่นี่ กระบวนการนี้ง่ายมาก และคุณควรมีแอปและทำงานภายในไม่กี่นาที

เมื่อคุณสร้างแอปแล้ว คุณจะได้รับ URL ของ WebHook ที่คุณสามารถใช้เพื่อเรียกแอปเพื่อโพสต์ลงในช่องที่เกี่ยวข้อง ติดตาม URL ของ WebHook นี้เพราะเราจะใช้กับฟังก์ชัน AWS Lambda

วิธีตั้งค่า AWS Lambda

จนถึงตอนนี้ เรามี Android App Bundle ที่ลงนาม สร้าง และอัปโหลดไปยังบัคเก็ต S3 ต่อไป เราต้องหาวิธีอัปโหลดบันเดิลไปยังแทร็กเบต้าบน Play Store

วิธีการทำเช่นนี้คือการตั้งค่า AWS Lambda ซึ่งจะถูกทริกเกอร์เมื่ออัปโหลดบันเดิลไปยังบัคเก็ต S3 เมื่อทริกเกอร์นี้เกิดขึ้น Lambda จะทำงาน ดาวน์โหลดบันเดิล คว้าคีย์บัญชีบริการ และอัปโหลดบันเดิลไปยังแทร็กเบต้าของ Play Store

เมื่อคุณสร้างแลมบ์ดาและเพิ่มทริกเกอร์เพื่อเรียกใช้เมื่อมีการอัปโหลดไฟล์ไปยังบัคเก็ตแล้ว ให้ดูโค้ดด้านล่าง:

"""This Python3 script is used to upload a new .aab bundle to the play store. The execution of this Python script
    occurs through an AWS Lambda which is invoked when a new file is uploaded to the relevant S3 buckets"""

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

#   Defining the scope of the authorization request
SCOPES = ['https://www.googleapis.com/auth/androidpublisher']

#   Package name for app
package_name = 'com.app.name'

#   Define the slack webhook url
slack_webhook_url = os.environ['SLACK_WEBHOOK_URL']

def send_slack_message(message):
    data = json.dumps({ 'text': message })
    post_data = data.encode('utf-8')
    req = request.Request(slack_webhook_url, data=post_data, headers={ 'Content-Type': 'application/json' })
    request.urlopen(req)

#   This is the main handler function
def lambda_handler(event, context):
    #   Create a new client S3 client and download the correct file from the bucket
    s3 = boto3.client('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'

    #   Download the app-release.aab file that triggered the Lambda
    bucket_name = event['Records'][0]['s3']['bucket']['name']
    file_key = event['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")

    #   Create a credentials object and create a service object using the credentials object
    credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES
    )
    service = googleapiclient.discovery.build('androidpublisher', 'v3', credentials=credentials, cache_discovery=False)
    
    #   Create an edit request using the service object and get the editId
    edit_request = service.edits().insert(body={}, packageName=package_name)
    result = edit_request.execute()
    edit_id = result['id']

    #   Create a request to upload the app bundle
    try:
        bundle_response = service.edits().bundles().upload(
            editId=edit_id,
            packageName=package_name,
            media_body=APP_BUNDLE,
            media_mime_type="application/octet-stream"
        ).execute()
    except Exception as err:
        message = f"There was an error while uploading a new version of {package_name}"
        send_slack_message(message)
        raise err

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

    #   Create a track request to upload the bundle to the beta track
    track_response = service.edits().tracks().update(
        editId=edit_id,
        track='beta',
        packageName=package_name,
        body={u'releases': [{
            u'versionCodes': [str(bundle_response['versionCode'])],
            u'status': u'completed',
        }]}
    ).execute()

    print("The bundle has been committed to the beta track")

    #   Create a commit request to commit the edit to BETA track
    commit_request = service.edits().commit(
        editId=edit_id,
        packageName=package_name
    ).execute()

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

    message = f"Version code {bundle_response['versionCode']} has been uploaded from the bucket {bucket_name}.\nEdit {commit_request['id']} has been committed"
    send_slack_message(message)
    
    return {
        'statusCode': 200,
        'body': json.dumps('Successfully executed the app bundle release to beta')
    }

Lambda ด้านบนจะใช้ googleapiclient ไลบรารี่และโมดูลการค้นพบเพื่อสร้าง URL สำหรับ Publishing API ของ Google Play

ถัดไป Lambda จะดาวน์โหลดคีย์บัญชีบริการจากบัคเก็ตที่คุณตั้งค่าไว้ก่อนหน้านี้ คุณจะต้องแน่ใจว่าคุณระบุชื่อถังที่ถูกต้อง

ขึ้นอยู่กับว่าการอัปโหลดสำเร็จหรือล้มเหลว เราต้องการให้ข้อความ Slack หายไป เพิ่ม Slack WebHook URL จากส่วนก่อนหน้าลงในตัวแปรสภาพแวดล้อมสำหรับ Lambda ฟังก์ชั่นด้านบนใช้ os . ของ Python โมดูลเพื่อเข้าถึงตัวแปรสภาพแวดล้อมและโพสต์ข้อความไปยัง Slack

หาก Lambda ของคุณล้มเหลว อาจเป็นเพราะ Lambda ของคุณไม่มีสิทธิ์เข้าถึงบัคเก็ต S3 ที่จัดเก็บคีย์สำหรับบัญชีบริการ Google Play ของคุณ ในกรณีนั้น คุณจะเห็นข้อความแสดงข้อผิดพลาดซึ่งระบุสิ่งนี้

ในการแก้ไขปัญหานี้ คุณเพียงแค่เพิ่มการอนุญาตที่เกี่ยวข้องให้กับบทบาท Lambda ของคุณ

นี่คือนโยบายที่คุณจะต้องเพิ่ม:

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

แทนที่ ARN สำหรับบัคเก็ตด้วยอันที่เกี่ยวข้องสำหรับบัญชีของคุณ และคุณน่าจะพร้อมแล้ว

บทสรุป

ดังนั้นคุณมีมัน แน่นอนว่ามันไม่ง่ายเลยและมีชิ้นส่วนที่เคลื่อนไหวได้มากมาย แต่นี่เป็นระบบอัตโนมัติที่จะช่วยให้คุณประหยัดเวลาและแรงได้มาก

หากคุณเป็นส่วนหนึ่งของทีมที่ออกอัปเดตแอปใหม่ๆ อยู่บ่อยๆ คุณไม่ต้องการที่จะถูกขัดขวางจากการไม่มีบุคคลที่มีหน้าที่เผยแพร่การอัปเดต

การสร้างระบบอัตโนมัติประเภทนี้ทำให้เวิร์กโฟลว์ CI/CD ของคุณราบรื่นและมีประสิทธิภาพมากขึ้น

หากคุณสนใจบล็อกแบบนี้ คุณสามารถอ่านเพิ่มเติมได้ที่ https://redixhumayun.github.io หรือติดตามฉันทาง Twitter