플러터 앱을 만들었고, 이제 플레이 콘솔에서 수동으로 해보니 별다른 문제없이 배포되었다.
내부테스트 까지 해봤고, 조금 정리후에는 실제 플레이 스토어에 출시 할 수 있을 듯 하다.

그전에 수동 업로드 과정을 자동화 하고자 한다.

관련 API 를 제공하는 듯 하다.

Google Play Android Developer API  가 세팅되어 있어야 한다.
관련 부분은 필요하면 따로 정리.

CI/CD 로 주로 fastlane 과 연동하는 방법을 많이 쓰는 것 같다.

난 좀 단순화(?) 시켜서 반자동 으로 처리하고자 한다.
업로드 부분은 python 코드로 다음과 같다.

{
"package_name": "{your-package-name}",
"service_account_file": "../{your-google-play-developer-api-key.json}",
"aab_file": "build/app/outputs/bundle/release/app-release.aab",
"mapping_file": "build/app/outputs/mapping/release/mapping.txt",
"debug_symbols_dir": "build/app/intermediates/merged_native_libs/release/out/lib"
}
view raw config.json hosted with ❤ by GitHub
import sys
import os
import json
import zipfile
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
import logging
#logging.basicConfig(level=logging.DEBUG)
def load_config(config_file):
with open(config_file, 'r') as f:
return json.load(f)
def create_zip_from_directory(zip_filename, directory):
with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(root, file)
zipf.write(file_path, os.path.relpath(file_path, directory))
def upload_aab_and_symbols(config, release_notes):
credentials = service_account.Credentials.from_service_account_file(
config['service_account_file'],
scopes=['https://www.googleapis.com/auth/androidpublisher']
)
service = build('androidpublisher', 'v3', credentials=credentials)
package_name = config['package_name']
aab_file = config['aab_file']
mapping_file = config['mapping_file']
debug_symbols_dir = config['debug_symbols_dir']
try:
edit_request = service.edits().insert(body={}, packageName=package_name)
result = edit_request.execute()
edit_id = result['id']
# AAB 파일 업로드
print(f'AAB 파일 업로드 : start')
aab_response = service.edits().bundles().upload(
editId=edit_id,
packageName=package_name,
media_body=MediaFileUpload(aab_file, mimetype='application/octet-stream')
).execute()
print(f'AAB 파일이 성공적으로 업로드되었습니다. 버전 코드: {aab_response["versionCode"]}')
versionCode = aab_response["versionCode"]
debugUploadDone = 1
print(f'디버그 심볼 파일 업로드 : start')
# 디버그 심볼 ZIP 파일 생성 및 업로드
zip_filename = 'native_debug_symbols.zip'
create_zip_from_directory(zip_filename, debug_symbols_dir)
print(f'ZIP 파일 생성됨: {zip_filename}')
try:
deobfuscation_file = service.edits().deobfuscationfiles().upload(
editId=edit_id,
packageName=package_name,
apkVersionCode=versionCode,
deobfuscationFileType='nativeCode',
media_body=MediaFileUpload(
zip_filename,
mimetype='application/octet-stream',
resumable=True,
chunksize=262144
)
).execute()
print('디버그 심볼 ZIP 파일이 업로드되었습니다.')
except Exception as upload_error:
print(f'ZIP 파일 업로드 중 오류 발생: {upload_error}')
debugUploadDone = 0 # fail
# proguard
print(f'ReTrace 매핑 파일 업로드 : start')
try:
mapping_file_response = service.edits().deobfuscationfiles().upload(
editId=edit_id,
packageName=package_name,
apkVersionCode=versionCode,
deobfuscationFileType='proguard',
media_body=MediaFileUpload(mapping_file, mimetype='application/octet-stream')
).execute()
print('ReTrace 매핑 파일이 성공적으로 업로드되었습니다.')
except Exception as e:
print(f'ReTrace 매핑 파일 업로드 실패: 오류 내용: {str(e)}')
debugUploadDone = 0 # fail
# all process done.
if debugUploadDone == 1 :
# 릴리즈 노트 업데이트 및 트랙에 추가
service.edits().tracks().update(
editId=edit_id,
track='internal',
packageName=package_name,
body={
'releases': [{
'versionCodes': [versionCode],
'status': 'draft', # draft => only / statusUnspecified / inProgress / halted / - completed(X)
'releaseNotes': [
{
'language': 'ko-KR',
'text': release_notes
}
]
}]
}
).execute()
# 변경사항 커밋
commit_request = service.edits().commit(editId=edit_id, packageName=package_name)
commit_request.execute()
print('AAB 파일, 네이티브 디버그 기호, 그리고 ReTrace 매핑 파일이 성공적으로 Google Play Console에 업로드되었습니다.')
else :
print('\n===== Fail release upload')
except Exception as e:
print(f'오류 발생: {str(e)}')
finally:
# 임시로 생성한 ZIP 파일 삭제
if os.path.exists(zip_filename):
os.remove(zip_filename)
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"사용법: python {sys.argv[0]} '릴리즈 노트' [config.json-another]")
sys.exit(1)
release_notes = sys.argv[1]
config_file = 'config.json'
if len(sys.argv) > 2 and sys.argv[2]:
config_file = sys.argv[2]
try:
config = load_config(config_file)
except Exception as e:
print(f'config load 오류 발생: {str(e)}')
upload_aab_and_symbols(config, release_notes)
view raw play-release.py hosted with ❤ by GitHub

위와 같은 코드를 사용했다.

본인 프로젝트에 맞는 config.json 의 내용을 수정하고, python 환경에서 실행하면 된다.

flutter build appbundle 으로 aab 파일을 생성한 후에 업로드

프로젝트 폴더에서 실행

python3 play-release.py 'feature: auto relase code'

 

python 환경이 구성되어 있지 않다면.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install python

pip3 install google-auth google-auth-httplib2 google-api-python-client

python  venv 를 구성하는 방법도 좋은 팁이다.(이 부분도 추후 필요시 정리)

반응형

WRITTEN BY
1day1
하루하루 즐거운일 하나씩, 행복한일 하나씩 만들어 가요.

,