зеркало из https://github.com/mozilla/gecko-dev.git
337 строки
13 KiB
Python
Executable File
337 строки
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# ***** BEGIN LICENSE BLOCK *****
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
# ***** END LICENSE BLOCK *****
|
|
"""mobile_partner_repack.py
|
|
|
|
"""
|
|
|
|
from copy import deepcopy
|
|
import os
|
|
import sys
|
|
|
|
# load modules from parent dir
|
|
sys.path.insert(1, os.path.dirname(sys.path[0]))
|
|
|
|
from mozharness.base.errors import ZipErrorList
|
|
from mozharness.base.log import FATAL
|
|
from mozharness.base.transfer import TransferMixin
|
|
from mozharness.base.vcs.vcsbase import MercurialScript
|
|
from mozharness.mozilla.l10n.locales import LocalesMixin
|
|
from mozharness.mozilla.release import ReleaseMixin
|
|
from mozharness.mozilla.signing import MobileSigningMixin
|
|
|
|
SUPPORTED_PLATFORMS = ["android"]
|
|
|
|
|
|
# MobilePartnerRepack {{{1
|
|
class MobilePartnerRepack(LocalesMixin, ReleaseMixin, MobileSigningMixin,
|
|
TransferMixin, MercurialScript):
|
|
config_options = [[
|
|
['--locale', ],
|
|
{"action": "extend",
|
|
"dest": "locales",
|
|
"type": "string",
|
|
"help": "Specify the locale(s) to repack"
|
|
}
|
|
], [
|
|
['--partner', ],
|
|
{"action": "extend",
|
|
"dest": "partners",
|
|
"type": "string",
|
|
"help": "Specify the partner(s) to repack"
|
|
}
|
|
], [
|
|
['--locales-file', ],
|
|
{"action": "store",
|
|
"dest": "locales_file",
|
|
"type": "string",
|
|
"help": "Specify a json file to determine which locales to repack"
|
|
}
|
|
], [
|
|
['--tag-override', ],
|
|
{"action": "store",
|
|
"dest": "tag_override",
|
|
"type": "string",
|
|
"help": "Override the tags set for all repos"
|
|
}
|
|
], [
|
|
['--platform', ],
|
|
{"action": "extend",
|
|
"dest": "platforms",
|
|
"type": "choice",
|
|
"choices": SUPPORTED_PLATFORMS,
|
|
"help": "Specify the platform(s) to repack"
|
|
}
|
|
], [
|
|
['--user-repo-override', ],
|
|
{"action": "store",
|
|
"dest": "user_repo_override",
|
|
"type": "string",
|
|
"help": "Override the user repo path for all repos"
|
|
}
|
|
], [
|
|
['--release-config-file', ],
|
|
{"action": "store",
|
|
"dest": "release_config_file",
|
|
"type": "string",
|
|
"help": "Specify the release config file to use"
|
|
}
|
|
], [
|
|
['--version', ],
|
|
{"action": "store",
|
|
"dest": "version",
|
|
"type": "string",
|
|
"help": "Specify the current version"
|
|
}
|
|
], [
|
|
['--buildnum', ],
|
|
{"action": "store",
|
|
"dest": "buildnum",
|
|
"type": "int",
|
|
"default": 1,
|
|
"metavar": "INT",
|
|
"help": "Specify the current release build num (e.g. build1, build2)"
|
|
}
|
|
]]
|
|
|
|
def __init__(self, require_config_file=True):
|
|
self.release_config = {}
|
|
LocalesMixin.__init__(self)
|
|
MercurialScript.__init__(
|
|
self,
|
|
config_options=self.config_options,
|
|
all_actions=[
|
|
"passphrase",
|
|
"clobber",
|
|
"pull",
|
|
"download",
|
|
"repack",
|
|
"upload-unsigned-bits",
|
|
"sign",
|
|
"upload-signed-bits",
|
|
"summary",
|
|
],
|
|
require_config_file=require_config_file
|
|
)
|
|
|
|
# Helper methods {{{2
|
|
def add_failure(self, platform, locale, **kwargs):
|
|
s = "%s:%s" % (platform, locale)
|
|
if 'message' in kwargs:
|
|
kwargs['message'] = kwargs['message'] % {'platform': platform, 'locale': locale}
|
|
super(MobilePartnerRepack, self).add_failure(s, **kwargs)
|
|
|
|
def query_failure(self, platform, locale):
|
|
s = "%s:%s" % (platform, locale)
|
|
return super(MobilePartnerRepack, self).query_failure(s)
|
|
|
|
# Actions {{{2
|
|
|
|
def pull(self):
|
|
c = self.config
|
|
dirs = self.query_abs_dirs()
|
|
repos = []
|
|
replace_dict = {}
|
|
if c.get("user_repo_override"):
|
|
replace_dict['user_repo_override'] = c['user_repo_override']
|
|
# deepcopy() needed because of self.config lock bug :(
|
|
for repo_dict in deepcopy(c['repos']):
|
|
repo_dict['repo'] = repo_dict['repo'] % replace_dict
|
|
repos.append(repo_dict)
|
|
else:
|
|
repos = c['repos']
|
|
self.vcs_checkout_repos(repos, parent_dir=dirs['abs_work_dir'],
|
|
tag_override=c.get('tag_override'))
|
|
|
|
def download(self):
|
|
c = self.config
|
|
rc = self.query_release_config()
|
|
dirs = self.query_abs_dirs()
|
|
locales = self.query_locales()
|
|
replace_dict = {
|
|
'buildnum': rc['buildnum'],
|
|
'version': rc['version'],
|
|
}
|
|
success_count = total_count = 0
|
|
for platform in c['platforms']:
|
|
base_installer_name = c['installer_base_names'][platform]
|
|
base_url = c['download_base_url'] + '/' + \
|
|
c['download_unsigned_base_subdir'] + '/' + \
|
|
base_installer_name
|
|
replace_dict['platform'] = platform
|
|
for locale in locales:
|
|
replace_dict['locale'] = locale
|
|
url = base_url % replace_dict
|
|
installer_name = base_installer_name % replace_dict
|
|
parent_dir = '%s/original/%s/%s' % (dirs['abs_work_dir'],
|
|
platform, locale)
|
|
file_path = '%s/%s' % (parent_dir, installer_name)
|
|
self.mkdir_p(parent_dir)
|
|
total_count += 1
|
|
if not self.download_file(url, file_path):
|
|
self.add_failure(platform, locale,
|
|
message="Unable to "
|
|
"download %(platform)s:%(locale)s installer!")
|
|
else:
|
|
success_count += 1
|
|
self.summarize_success_count(success_count, total_count,
|
|
message="Downloaded %d of %d installers successfully.")
|
|
|
|
def _repack_apk(self, partner, orig_path, repack_path):
|
|
""" Repack the apk with a partner update channel.
|
|
Returns True for success, None for failure
|
|
"""
|
|
dirs = self.query_abs_dirs()
|
|
zip_bin = self.query_exe("zip")
|
|
unzip_bin = self.query_exe("unzip")
|
|
file_name = os.path.basename(orig_path)
|
|
tmp_dir = os.path.join(dirs['abs_work_dir'], 'tmp')
|
|
tmp_file = os.path.join(tmp_dir, file_name)
|
|
tmp_prefs_dir = os.path.join(tmp_dir, 'defaults', 'pref')
|
|
# Error checking for each step.
|
|
# Ignoring the mkdir_p()s since the subsequent copyfile()s will
|
|
# error out if unsuccessful.
|
|
if self.rmtree(tmp_dir):
|
|
return
|
|
self.mkdir_p(tmp_prefs_dir)
|
|
if self.copyfile(orig_path, tmp_file):
|
|
return
|
|
if self.write_to_file(os.path.join(tmp_prefs_dir, 'partner.js'),
|
|
'pref("app.partner.%s", "%s");' % (partner, partner)
|
|
) is None:
|
|
return
|
|
if self.run_command([unzip_bin, '-q', file_name, 'omni.ja'],
|
|
error_list=ZipErrorList,
|
|
return_type='num_errors',
|
|
cwd=tmp_dir):
|
|
self.error("Can't extract omni.ja from %s!" % file_name)
|
|
return
|
|
if self.run_command([zip_bin, '-9r', 'omni.ja',
|
|
'defaults/pref/partner.js'],
|
|
error_list=ZipErrorList,
|
|
return_type='num_errors',
|
|
cwd=tmp_dir):
|
|
self.error("Can't add partner.js to omni.ja!")
|
|
return
|
|
if self.run_command([zip_bin, '-9r', file_name, 'omni.ja'],
|
|
error_list=ZipErrorList,
|
|
return_type='num_errors',
|
|
cwd=tmp_dir):
|
|
self.error("Can't re-add omni.ja to %s!" % file_name)
|
|
return
|
|
if self.unsign_apk(tmp_file):
|
|
return
|
|
repack_dir = os.path.dirname(repack_path)
|
|
self.mkdir_p(repack_dir)
|
|
if self.copyfile(tmp_file, repack_path):
|
|
return
|
|
return True
|
|
|
|
def repack(self):
|
|
c = self.config
|
|
rc = self.query_release_config()
|
|
dirs = self.query_abs_dirs()
|
|
locales = self.query_locales()
|
|
success_count = total_count = 0
|
|
for platform in c['platforms']:
|
|
for locale in locales:
|
|
installer_name = c['installer_base_names'][platform] % \
|
|
{'version': rc['version'], 'locale': locale}
|
|
if self.query_failure(platform, locale):
|
|
self.warning("%s:%s had previous issues; skipping!" % (platform, locale))
|
|
continue
|
|
original_path = '%s/original/%s/%s/%s' % \
|
|
(dirs['abs_work_dir'], platform, locale, installer_name)
|
|
for partner in c['partner_config'].keys():
|
|
repack_path = '%s/unsigned/partner-repacks/%s/%s/%s/%s' % \
|
|
(dirs['abs_work_dir'], partner, platform, locale, installer_name)
|
|
total_count += 1
|
|
if self._repack_apk(partner, original_path, repack_path):
|
|
success_count += 1
|
|
else:
|
|
self.add_failure(platform, locale,
|
|
message="Unable to repack %(platform)s:%(locale)s "
|
|
"installer!")
|
|
self.summarize_success_count(success_count, total_count,
|
|
message="Repacked %d of %d installers successfully.")
|
|
|
|
def _upload(self, dir_name="unsigned/partner-repacks"):
|
|
c = self.config
|
|
dirs = self.query_abs_dirs()
|
|
local_path = os.path.join(dirs['abs_work_dir'], dir_name)
|
|
rc = self.query_release_config()
|
|
replace_dict = {
|
|
'buildnum': rc['buildnum'],
|
|
'version': rc['version'],
|
|
}
|
|
remote_path = '%s/%s' % (c['ftp_upload_base_dir'] % replace_dict, dir_name)
|
|
if self.rsync_upload_directory(local_path, c['ftp_ssh_key'],
|
|
c['ftp_user'], c['ftp_server'],
|
|
remote_path):
|
|
self.return_code += 1
|
|
|
|
def upload_unsigned_bits(self):
|
|
self._upload()
|
|
|
|
# passphrase() in AndroidSigningMixin
|
|
# verify_passphrases() in AndroidSigningMixin
|
|
|
|
def preflight_sign(self):
|
|
if 'passphrase' not in self.actions:
|
|
self.passphrase()
|
|
self.verify_passphrases()
|
|
|
|
def sign(self):
|
|
c = self.config
|
|
rc = self.query_release_config()
|
|
dirs = self.query_abs_dirs()
|
|
locales = self.query_locales()
|
|
success_count = total_count = 0
|
|
for platform in c['platforms']:
|
|
for locale in locales:
|
|
installer_name = c['installer_base_names'][platform] % \
|
|
{'version': rc['version'], 'locale': locale}
|
|
if self.query_failure(platform, locale):
|
|
self.warning("%s:%s had previous issues; skipping!" % (platform, locale))
|
|
continue
|
|
for partner in c['partner_config'].keys():
|
|
unsigned_path = '%s/unsigned/partner-repacks/%s/%s/%s/%s' % \
|
|
(dirs['abs_work_dir'], partner, platform, locale, installer_name)
|
|
signed_dir = '%s/partner-repacks/%s/%s/%s' % \
|
|
(dirs['abs_work_dir'], partner, platform, locale)
|
|
signed_path = "%s/%s" % (signed_dir, installer_name)
|
|
total_count += 1
|
|
self.info("Signing %s %s." % (platform, locale))
|
|
if not os.path.exists(unsigned_path):
|
|
self.error("Missing apk %s!" % unsigned_path)
|
|
continue
|
|
if self.sign_apk(unsigned_path, c['keystore'],
|
|
self.store_passphrase, self.key_passphrase,
|
|
c['key_alias']) != 0:
|
|
self.add_summary("Unable to sign %s:%s apk!" % (platform, locale),
|
|
level=FATAL)
|
|
else:
|
|
self.mkdir_p(signed_dir)
|
|
if self.align_apk(unsigned_path, signed_path):
|
|
self.add_failure(platform, locale,
|
|
message="Unable to align %(platform)s%(locale)s apk!")
|
|
self.rmtree(signed_dir)
|
|
else:
|
|
success_count += 1
|
|
self.summarize_success_count(success_count, total_count,
|
|
message="Signed %d of %d apks successfully.")
|
|
|
|
# TODO verify signatures.
|
|
|
|
def upload_signed_bits(self):
|
|
self._upload(dir_name="partner-repacks")
|
|
|
|
|
|
# main {{{1
|
|
if __name__ == '__main__':
|
|
mobile_partner_repack = MobilePartnerRepack()
|
|
mobile_partner_repack.run_and_exit()
|