플러터 앱을 만들었으니, 배포 / 출시 를 자동화 해보자. (feat. androidpublisher)
알아둘일
2024. 7. 1. 08:22
플러터 앱을 만들었고, 이제 플레이 콘솔에서 수동으로 해보니 별다른 문제없이 배포되었다.
내부테스트 까지 해봤고, 조금 정리후에는 실제 플레이 스토어에 출시 할 수 있을 듯 하다.
그전에 수동 업로드 과정을 자동화 하고자 한다.
관련 API 를 제공하는 듯 하다.

Google Play Android Developer API 가 세팅되어 있어야 한다.
관련 부분은 필요하면 따로 정리.
CI/CD 로 주로 fastlane 과 연동하는 방법을 많이 쓰는 것 같다.
난 좀 단순화(?) 시켜서 반자동 으로 처리하고자 한다.
업로드 부분은 python 코드로 다음과 같다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
위와 같은 코드를 사용했다.
본인 프로젝트에 맞는 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 를 구성하는 방법도 좋은 팁이다.(이 부분도 추후 필요시 정리)
반응형
'알아둘일' 카테고리의 다른 글
안드로이드 앱 출시는 힘겹구나! (feat. 비공개테스트) (0) | 2024.07.14 |
---|---|
플러터 앱 - 자동배포 iOS 쪽도 해보자 (feat. xcrun altool) (0) | 2024.07.02 |
플러터 - 앱로빈 광고 연동시 iOS native 광고 노출 이상 현상 (feat. bundle id) (0) | 2024.06.11 |
우분투 <=> 윈도우 - smb 연결 시 캐시가 문제가 되나? (feat. ubuntu 22.04) (0) | 2024.06.05 |
(미해결)플러터 - 안드로이드 에뮬레이터 이상 현상 (feat. emulator) (0) | 2024.05.29 |
WRITTEN BY
- 1day1
하루하루 즐거운일 하나씩, 행복한일 하나씩 만들어 가요.
,