Skip to content

Commit

Permalink
Remove support for the amazon store
Browse files Browse the repository at this point in the history
This hasn't been used by mozilla in a few years, and support was already
removed in pushapkscript.
  • Loading branch information
jcristau committed Oct 30, 2023
1 parent 0affd28 commit ac16cc0
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 496 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ source venv/bin/activate
```

* Note `beta` track on Google Play, that's our way to show to people on Play Store that it's not a finished product. We don't use the "production" track for Nightly, unlike beta and release.
1. If all goes well, add `--commit` (or `--keep` for Amazon) to the command line and rerun it.
1. If all goes well, add `--commit` to the command line and rerun it.
127 changes: 0 additions & 127 deletions mozapkpublisher/common/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,10 @@
from unittest.mock import MagicMock

from mozapkpublisher.common.exceptions import WrongArgumentGiven
import requests

logger = logging.getLogger(__name__)


BASE_AMAZON_URL = 'https://developer.amazon.com/api/appstore/v1'
AMAZON_RECENT_CHANGES = {
'en-US': 'Bug fixes and technical improvements.',
'de-DE': 'Fehlerkorrekturen und Technische Verbesserungen.',
'fr-FR': 'Correction de bugs et amélioration des techniques.',
}


def add_general_google_play_arguments(parser):
parser.add_argument('--service-account', help='The service account email', required=True)
parser.add_argument('--credentials', dest='google_play_credentials_filename',
Expand All @@ -45,124 +36,6 @@ def execute(self):
return self._return_value


def http(expected_status, method, url, **kwargs):
response = requests.request(
method=method,
url=url,
**kwargs,
)
if response.status_code != expected_status:
raise RuntimeError(f'Expected "{method}" to "{url}" to have status code '
f'"{expected_status}", but received "{response.status_code}" '
f'("{response.text}") instead')
return response


class AmazonAuth(requests.auth.AuthBase):
def __init__(self, access_token):
self._access_token = access_token

def __call__(self, request: requests.Request):
request.headers['Authorization'] = f'Bearer {self._access_token}'
return request


class AmazonStoreEdit:
def __init__(self, auth, edit_it, package_name):
self._auth = auth
self._edit_id = edit_it
self._package_name = package_name

def _http(self, expected_status, method, endpoint, **kwargs):
url = f'{BASE_AMAZON_URL}/applications/{self._package_name}/edits/{self._edit_id}' + endpoint

return http(expected_status, method, url, auth=self._auth, **kwargs)

def update_app(self, extracted_apks):
body = self._http(200, 'get', '/apks').json()
existing_apk_ids = [apk['id'] for apk in body]

for apk_id in existing_apk_ids:
response = self._http(200, 'get', f'/apks/{apk_id}')
etag = response.headers['ETag']
self._http(204, 'delete', f'/apks/{apk_id}', headers={'If-Match': etag})

# TODO: simplify update_app(...) so it just takes [apks], rather than [(apk, _unused)]
for apk, _ in extracted_apks:
self._http(200, 'post', '/apks/upload', data=apk,
headers={'Content-Type': 'application/octet-stream'})

response = self._http(200, 'get', '/listings')
languages = response.json()['listings'].keys()
for locale in languages:
response = self._http(200, 'get', f'/listings/{locale}')
etag = response.headers['ETag']
listing = response.json()
listing['recentChanges'] = AMAZON_RECENT_CHANGES.get(locale, '✔')

self._http(200, 'put', f'/listings/{locale}', headers={'If-Match': etag}, json=listing)

def validate(self):
self._http(200, 'post', '/validate')

def cancel(self):
response = self._http(200, 'get', '')
etag = response.headers['ETag']

self._http(204, 'delete', '', headers={'If-Match': etag})

@staticmethod
@contextmanager
def transaction(client_id, client_secret, package_name, *, contact_server, dry_run):
if contact_server:
response = http(200, 'post', 'https://api.amazon.com/auth/o2/token', data={
'client_id': client_id,
'client_secret': client_secret,
'grant_type': 'client_credentials',
'scope': 'appstore::apps:readwrite',
})
auth = AmazonAuth(response.json()['access_token'])

response = http(200, 'get', f'{BASE_AMAZON_URL}/applications/{package_name}/edits', auth=auth)
if response.text != '{}':
# Only one "upcoming version" is allowed at a time. We could automatically delete
# the existing one and create a new one, but that could cause loss of data (e.g.:
# if someone is manually creating a release)
raise RuntimeError(f'The app "{package_name}" already has an "upcoming '
f'version". Please submit or delete that upcoming '
f'version from the Amazon Developer Console.')

response = http(200, 'post', f'{BASE_AMAZON_URL}/applications/{package_name}/edits', auth=auth)
edit_id = response.json()['id']
edit = AmazonStoreEdit(auth, edit_id, package_name)
else:
logger.warning('Not a single request to Amazon will be made, since `contact_server` '
'was set to `False`')
edit = MockAmazonStoreEdit()

try:
yield edit
edit.validate()
if dry_run:
logger.warning('`try_run` was `True`. Cancelling the edit...')
edit.cancel()
except BaseException:
logger.warning('An error was encountered, cancelling the edit...')
edit.cancel()
raise


class MockAmazonStoreEdit:
def update_app(self, apks):
pass

def validate(self):
pass

def cancel(self):
pass


class GooglePlayEdit:
"""Represents an "edit" to an app on the Google Play store
Expand Down
17 changes: 13 additions & 4 deletions mozapkpublisher/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,24 @@ def is_firefox_version_nightly(firefox_version):

def add_push_arguments(parser):
parser.add_argument('--username', required=True,
help='Either the amazon client id or the google service account')
help='Google service account')
parser.add_argument('--secret', required=True,
help='Either the amazon client secret or the file that contains '
'google credentials')
help='File that contains google credentials')
parser.add_argument('--do-not-contact-server', action='store_false', dest='contact_server',
help='''Prevent any request to reach the APK server. Use this option if
you want to run the script without any valid credentials nor valid APKs. --service-account and
--credentials must still be provided (you can just fill them with random string and file).''')
parser.add_argument('track', help='Track on which to upload')
parser.add_argument(
'--rollout-percentage',
type=int,
choices=range(0, 101),
metavar='[0-100]',
default=None,
help='The percentage of user who will get the update. Specify only if track is rollout'
)
parser.add_argument('--commit', action='store_false', dest='dry_run',
help='Commit new release on Google Play. This action cannot be reverted')


def metadata_by_package_name(metadata_dict):
Expand All @@ -67,4 +77,3 @@ def metadata_by_package_name(metadata_dict):
package_names[package_name].append((file, metadata))

return package_names

47 changes: 10 additions & 37 deletions mozapkpublisher/push_aab.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from mozapkpublisher.common import main_logging
from mozapkpublisher.common.aab import add_aab_checks_arguments, extract_aabs_metadata
from mozapkpublisher.common.exceptions import WrongArgumentGiven
from mozapkpublisher.common.store import GooglePlayEdit
from mozapkpublisher.common.utils import add_push_arguments, metadata_by_package_name

Expand Down Expand Up @@ -34,11 +33,6 @@ def push_aab(
contact_server (bool): `False` to avoid communicating with the Google Play server.
Useful if you're using mock credentials.
"""
if track is None:
# The Google store allows multiple stability "tracks" to exist for a single app, so it
# requires you to disambiguate which track you'd like to publish to.
raise WrongArgumentGiven('The track must be provided')

# We want to tune down some logs, even when push_aab() isn't called from the command line
main_logging.init()

Expand All @@ -58,46 +52,25 @@ def push_aab(
aabs_by_package_name = metadata_by_package_name(aabs_metadata_per_paths)
for package_name, extracted_aabs in aabs_by_package_name.items():
with GooglePlayEdit.transaction(username, secret, package_name, contact_server=contact_server,
dry_run=dry_run) as edit:
dry_run=dry_run) as edit:
edit.update_aab(extracted_aabs, **update_aab_kwargs)


def main():
parser = argparse.ArgumentParser(description='Upload AABs on the Google Play Store.')

# TODO: move these to add_push_arguments when Amazon support is removed
parser.add_argument('track', help='Track on which to upload')
parser.add_argument(
'--rollout-percentage',
type=int,
choices=range(0, 101),
metavar='[0-100]',
default=None,
help='The percentage of users who will get the update. Specify only if track is rollout'
)
parser.add_argument('--commit', action='store_false', dest='dry_run',
help='Commit new release on Google Play. This action cannot be '
'reverted')

add_push_arguments(parser)
add_aab_checks_arguments(parser)
config = parser.parse_args()

track = config.track
rollout_percentage = config.rollout_percentage

try:
push_aab(
config.aabs,
config.username,
config.secret,
track,
rollout_percentage,
config.dry_run,
config.contact_server,
)
except WrongArgumentGiven as e:
parser.error(e)
push_aab(
config.aabs,
config.username,
config.secret,
config.track,
config.rollout_percentage,
config.dry_run,
config.contact_server,
)


__name__ == '__main__' and main()
Loading

0 comments on commit ac16cc0

Please sign in to comment.