Skip to content

Instantly share code, notes, and snippets.

@domhel
Created October 27, 2024 22:57
Show Gist options
  • Select an option

  • Save domhel/a1d8d486ea575be8b2ac2cce74dde7dc to your computer and use it in GitHub Desktop.

Select an option

Save domhel/a1d8d486ea575be8b2ac2cce74dde7dc to your computer and use it in GitHub Desktop.
Flutter build and upload iOS and Android app
#!/usr/bin/env python3
# This script sits in the root of your project = <my_flutter_project>/build_stuff.py
# It requires a directory <my_flutter_project>/keys with the credentials below
import os
import subprocess
import sys
import glob
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
appleApiKeyId = "ABCDEFG123" # the AuthKey_<keyId>.p8 file needs to be in ./keys
appleApiIssuerId = "" # unique per apple id, find it at https://appstoreconnect.apple.com/access/integrations/api
package_name = "de.example.my_app" # used for android
googleCredentialsJson = "my-credentials.json"
def build_app(flavor, platform):
# flavor ignored
flutter_build_params_base = [
"--release",
"--obfuscate",
"--split-debug-info",
"/tmp/flutter-build-split-info",
]
dart_defines = []
if platform == "ios":
subprocess.run(
[
"fvm",
"flutter",
"build",
"ipa",
*flutter_build_params_base,
*dart_defines,
],
capture_output=False,
)
elif platform == "android":
for type in ["apk", "appbundle"]:
subprocess.run(
[
"fvm",
"flutter",
"build",
type,
*flutter_build_params_base,
*dart_defines,
],
capture_output=False,
)
else:
print("Invalid platform!")
sys.exit(1)
def upload_app(platform):
if platform == 'ios':
my_env = os.environ.copy()
my_env["API_PRIVATE_KEYS_DIR"] = "./keys"
ipa_files = glob.glob("build/ios/ipa/*.ipa")
if not ipa_files:
print('ipa file not found! Your need to build the project first')
sys.exit(1)
print('Uploading ipa file to App Store')
subprocess.run(
[
"xcrun",
"altool",
"--upload-app",
"--type",
"ios",
"-f",
ipa_files[0],
"--apiKey",
appleApiKeyId,
"--apiIssuer",
appleApiIssuerId,
],
capture_output=False,
env=my_env,
)
elif platform == 'android':
service_account_file = f"./keys/{googleCredentialsJson}"
aab_files = glob.glob("build/app/outputs/bundle/release/app-release.aab")
if not aab_files:
print('AAB file not found! You need to build the project first')
sys.exit(1)
aab_file = aab_files[0]
print('Uploading Android bundle')
# Authenticate and create the Android Publisher service
credentials = Credentials.from_service_account_file(
service_account_file,
scopes=['https://www.googleapis.com/auth/androidpublisher']
)
service = build('androidpublisher', 'v3', credentials=credentials)
try:
# Create an edit
edit_request = service.edits().insert(body={}, packageName=package_name)
result = edit_request.execute()
edit_id = result['id']
# Upload the AAB
aab_response = service.edits().bundles().upload(
editId=edit_id,
packageName=package_name,
media_body=MediaFileUpload(aab_file, mimetype='application/octet-stream')
).execute()
# Create a new track release
track_response = service.edits().tracks().update(
editId=edit_id,
track='internal', # or 'alpha', 'beta', 'production'
packageName=package_name,
body={
'releases': [{
'versionCodes': [aab_response['versionCode']],
'status': 'draft' # change to 'completed' when the app is out of draft mode
}]
}
).execute()
# Commit the edit
commit_request = service.edits().commit(
editId=edit_id,
packageName=package_name
).execute()
print(f'App uploaded successfully. Edit ID: {commit_request["id"]}')
except Exception as e:
print(f'Error uploading app: {str(e)}')
sys.exit(1)
else:
print(f'Platform {platform} upload not implemented')
sys.exit(1)
if __name__ == "__main__":
# arg parsing:
if len(sys.argv) < 2:
print("Usage: build_stuff.py {--upload, --skip-build}")
sys.exit(1)
extra_params = sys.argv[1:]
cwd = os.getcwd()
if not os.path.isfile(f'{cwd}/pubspec.yaml'):
print(f'Run this script from within a Flutter project, not from {cwd}')
sys.exit(1)
flavor = "default"
build_platforms = ["ios", "android"]
# build_platforms = ["android"]
upload_platforms = build_platforms[:]
if '--skip-build' not in extra_params:
for platform in build_platforms:
build_app(flavor, platform)
if "--upload" in extra_params:
for platform in upload_platforms:
upload_app(platform)
print('')
print('####################################################')
print('############### SCRIPT DONE ####################')
print('####################################################')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment