1
0
Fork 0

Configure either geckoview beta or nightly at compile-time (#4851)

* Remove "abi" product flavor and introduce "engine" product flavor.

This patch will allow us to build Fenix against GeckoView Nightly and GeckoView Beta by
introducing a new flavor dimension: engine = [geckoNightly, geckoBeta].

In addition to that it adds a "fenix"  prefix to the nightly, beta and production flavors
to reduce the ambiguity between fenix beta/nightly and GeckoView beta/nightly.

For now the build types have the following engine variants enabled:

**debug**: geckoNightly, geckoBeta
Both variants enabled for local development and testing.

**forPerformanceTest**: geckoNightly, geckoBeta
Both variants enabled unless the perf team only cares about Nightly (tbd)

**fenixNightlyLegacy**: geckoBeta
Uses GeckoView Beta for now - the same version we ship production builds with (same behavior
as before). This release type will eventualyl be decommissioned once we switch to a separate
Nightly app on Google Play.

**fenixNightly**: geckoBeta
Uses GeckoView Beta for now - the same version we ship production builds with (same behavior
as before). Changing this build to use GeckoView Nightly is currently being discussed.

**fenixBeta**: geckoBeta
Fenix Beta uses GeckoView Beta.

**fenixProduction**
Fenix Production uses GeckoView Beta (69) currently.

* gradle.py/variant.py: Replace "abi" with "engine".

* Disable enableUnitTestBinaryResources until we can switch to Android Gradle plugin 3.5.

* Fenix nightly should use both geckoview nightly and beta

* Updates automation to use apk splitting and support different engine
master
Mitchell Hentges 2019-08-21 08:32:01 -07:00 committed by Jeff Boek
parent 71a2478f4d
commit 6c020a0bc4
7 changed files with 205 additions and 207 deletions

View File

@ -14,6 +14,7 @@ import com.android.build.gradle.internal.tasks.AppPreBuildTask
import org.gradle.internal.logging.text.StyledTextOutput.Style import org.gradle.internal.logging.text.StyledTextOutput.Style
import org.gradle.internal.logging.text.StyledTextOutputFactory import org.gradle.internal.logging.text.StyledTextOutputFactory
import static org.gradle.api.tasks.testing.TestResult.ResultType import static org.gradle.api.tasks.testing.TestResult.ResultType
import com.android.build.OutputFile
android { android {
compileSdkVersion 28 compileSdkVersion 28
@ -52,18 +53,18 @@ android {
applicationIdSuffix ".performancetest" applicationIdSuffix ".performancetest"
debuggable true debuggable true
} }
nightlyLegacy releaseTemplate >> { fenixNightlyLegacy releaseTemplate >> {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
} }
nightly releaseTemplate >> { fenixNightly releaseTemplate >> {
applicationIdSuffix ".nightly" applicationIdSuffix ".nightly"
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
} }
beta releaseTemplate >> { fenixBeta releaseTemplate >> {
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
} }
production releaseTemplate >> { fenixProduction releaseTemplate >> {
buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true"
} }
} }
@ -72,6 +73,28 @@ android {
if (buildType.name == 'release') { if (buildType.name == 'release') {
setIgnore true setIgnore true
} }
// Current build variant setup:
//
// | geckoNightly | geckoBeta |
// |--------------------|---------------|-----------|
// | debug | | | Both variants for testing and development.
// | forPerformanceTest | | | Both variants unless the perf team only cares about Nightly (TBD).
// | fenixNightlyLegacy | | | Release type will be decommissioned soon.
// | fenixNightly | | | Built with both, but only the "geckoNightly" one is published to Google Play
// | fenixBeta | | | Fenix Beta ships with GV Beta
// | fenixProduction | | | Fenix Production ships with GV Beta
//
def flavors = flavors*.name.toString().toLowerCase()
if (buildType.name == 'fenixBeta' && flavors.contains("geckonightly")) {
setIgnore true
}
if (buildType.name == 'fenixProduction' && flavors.contains("geckonightly")) {
setIgnore true
}
} }
testOptions { testOptions {
@ -79,32 +102,25 @@ android {
unitTests.includeAndroidResources = true unitTests.includeAndroidResources = true
} }
flavorDimensions "abi" flavorDimensions "engine"
productFlavors { productFlavors {
arm { geckoNightly {
dimension "abi" dimension "engine"
ndk {
abiFilter "armeabi-v7a"
}
} }
aarch64 {
dimension "abi" geckoBeta {
ndk { dimension "engine"
abiFilter "arm64-v8a"
}
} }
x86 { }
dimension "abi"
ndk { splits {
abiFilter "x86" abi {
} enable true
}
x86_64 { reset()
dimension "abi"
ndk { include "x86", "armeabi-v7a", "arm64-v8a", "x86_64"
abiFilter "x86_64"
}
} }
} }
@ -126,6 +142,8 @@ android {
} }
} }
def baseVersionCode = generatedVersionCode
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
@ -144,14 +162,10 @@ android.applicationVariants.all { variant ->
// Generate version codes for builds // Generate version codes for builds
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
def buildType = variant.buildType.name
def versionCode = null
def isDebug = variant.buildType.resValues['IS_DEBUG']?.value ?: false def isDebug = variant.buildType.resValues['IS_DEBUG']?.value ?: false
def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false
if (useReleaseVersioning) { if (useReleaseVersioning) {
versionCode = generatedVersionCode
// The Google Play Store does not allow multiple APKs for the same app that all have the // The Google Play Store does not allow multiple APKs for the same app that all have the
// same version code. Therefore we need to have different version codes for our ARM and x86 // same version code. Therefore we need to have different version codes for our ARM and x86
// builds. // builds.
@ -160,17 +174,24 @@ android.applicationVariants.all { variant ->
// Our x86 builds need a higher version code to avoid installing ARM builds on an x86 device // Our x86 builds need a higher version code to avoid installing ARM builds on an x86 device
// with ARM compatibility mode. // with ARM compatibility mode.
if (variant.flavorName.contains("x86_64")) { variant.outputs.each { output ->
versionCode = versionCode + 3 def abi = output.getFilter(OutputFile.ABI)
} else if (variant.flavorName.contains("x86")) {
versionCode = versionCode + 2
} else if (variant.flavorName.contains("aarch64")) {
versionCode = versionCode + 1
}// else variant.flavorName.contains("Arm")) use generated version code
variant.outputs.all { def versionCodeOverride
versionCodeOverride = versionCode
versionNameOverride = Config.releaseVersionName(project) if (abi == "x86_64") {
versionCodeOverride = baseVersionCode + 3
} else if (abi == "x86") {
versionCodeOverride = baseVersionCode + 2
} else if (abi == "arm64-v8a") {
versionCodeOverride = baseVersionCode + 1
} else if (abi == "armeabi-v7a") {
versionCodeOverride = baseVersionCode
} else {
throw RuntimeException("Unknown ABI: $abi")
}
println("versionCode for $abi = $versionCodeOverride")
} }
// If this is a release build, validate that "versionName" is set // If this is a release build, validate that "versionName" is set
@ -277,6 +298,9 @@ androidExtensions {
dependencies { dependencies {
implementation project(':architecture') implementation project(':architecture')
geckoNightlyImplementation Deps.mozilla_browser_engine_gecko_nightly
geckoBetaImplementation Deps.mozilla_browser_engine_gecko_beta
implementation Deps.kotlin_stdlib implementation Deps.kotlin_stdlib
implementation Deps.kotlin_coroutines implementation Deps.kotlin_coroutines
implementation Deps.androidx_appcompat implementation Deps.androidx_appcompat
@ -309,7 +333,6 @@ dependencies {
implementation Deps.mozilla_browser_domains implementation Deps.mozilla_browser_domains
implementation Deps.mozilla_browser_icons implementation Deps.mozilla_browser_icons
implementation Deps.mozilla_browser_menu implementation Deps.mozilla_browser_menu
implementation Deps.mozilla_browser_engine_gecko_beta
implementation Deps.mozilla_browser_search implementation Deps.mozilla_browser_search
implementation Deps.mozilla_browser_session implementation Deps.mozilla_browser_session
implementation Deps.mozilla_browser_storage_sync implementation Deps.mozilla_browser_storage_sync
@ -483,17 +506,22 @@ if (project.hasProperty("coverage")) {
} }
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// Task for printing all build variants to build variants in parallel in automation // Task for printing APK information for the requested variant
// Usage: "./gradlew printVariant -PvariantBuildType=nightly -PvariantEngine=geckoNightly"
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
task printBuildVariants { task printVariant {
doLast { doLast {
def variantData = android.applicationVariants.collect { variant -> [ def rawVariant = android.applicationVariants.find {
name: variant.name, it.buildType.name == variantBuildType &&
buildType: variant.buildType.name, it.productFlavors.find { it.dimension == 'engine' }.name == variantEngine
abi: variant.productFlavors.find { it.dimension == 'abi' }.name, }
isSigned: variant.signingReady, println 'variant: ' + groovy.json.JsonOutput.toJson([
]} name: rawVariant.name,
println "variants: " + groovy.json.JsonOutput.toJson(variantData) apks: rawVariant.variantData.outputScope.apkDatas.collect { [
abi: it.filters.find { it.filterType == 'ABI' }.identifier,
fileName: it.outputFileName,
]}
])
} }
} }

View File

@ -15,7 +15,7 @@ import re
import taskcluster import taskcluster
from lib.gradle import get_variants_for_build_type from lib.gradle import get_variant
from lib.tasks import ( from lib.tasks import (
fetch_mozharness_task_id, fetch_mozharness_task_id,
schedule_task_graph, schedule_task_graph,
@ -25,7 +25,6 @@ from lib.chain_of_trust import (
populate_chain_of_trust_task_graph, populate_chain_of_trust_task_graph,
populate_chain_of_trust_required_but_unused_files populate_chain_of_trust_required_but_unused_files
) )
from lib.variant import Variant
REPO_URL = os.environ.get('MOBILE_HEAD_REPOSITORY') REPO_URL = os.environ.get('MOBILE_HEAD_REPOSITORY')
COMMIT = os.environ.get('MOBILE_HEAD_REV') COMMIT = os.environ.get('MOBILE_HEAD_REV')
@ -61,10 +60,10 @@ def pr():
signing_tasks = {} signing_tasks = {}
other_tasks = {} other_tasks = {}
for variant in get_variants_for_build_type('debug'): variant = get_variant('debug', 'geckoNightly')
assemble_task_id = taskcluster.slugId() assemble_task_id = taskcluster.slugId()
build_tasks[assemble_task_id] = BUILDER.craft_assemble_pr_task(variant) build_tasks[assemble_task_id] = BUILDER.craft_assemble_pr_task(variant)
build_tasks[taskcluster.slugId()] = BUILDER.craft_test_pr_task(variant, True) build_tasks[taskcluster.slugId()] = BUILDER.craft_test_pr_task(variant)
for craft_function in ( for craft_function in (
BUILDER.craft_detekt_task, BUILDER.craft_detekt_task,
@ -80,12 +79,12 @@ def pr():
def push(): def push():
all_tasks = pr() all_tasks = pr()
other_tasks = all_tasks[-1] other_tasks = all_tasks[-1]
other_tasks[taskcluster.slugId()] = BUILDER.craft_ui_tests_task() other_tasks[taskcluster.slugId()] = BUILDER.craft_ui_tests_task()
if SHORT_HEAD_BRANCH == 'master': if SHORT_HEAD_BRANCH == 'master':
other_tasks[taskcluster.slugId()] = BUILDER.craft_dependencies_task() other_tasks[taskcluster.slugId()] = BUILDER.craft_dependencies_task()
return all_tasks return all_tasks
def raptor(is_staging): def raptor(is_staging):
@ -96,41 +95,41 @@ def raptor(is_staging):
mozharness_task_id = fetch_mozharness_task_id() mozharness_task_id = fetch_mozharness_task_id()
gecko_revision = taskcluster.Queue().task(mozharness_task_id)['payload']['env']['GECKO_HEAD_REV'] gecko_revision = taskcluster.Queue().task(mozharness_task_id)['payload']['env']['GECKO_HEAD_REV']
for variant in [Variant.from_values(abi, False, 'forPerformanceTest') for abi in ('aarch64', 'arm')]: variant = get_variant('forPerformanceTest', 'geckoNightly')
assemble_task_id = taskcluster.slugId() assemble_task_id = taskcluster.slugId()
build_tasks[assemble_task_id] = BUILDER.craft_assemble_raptor_task(variant) build_tasks[assemble_task_id] = BUILDER.craft_assemble_raptor_task(variant)
signing_task_id = taskcluster.slugId() signing_task_id = taskcluster.slugId()
signing_tasks[signing_task_id] = BUILDER.craft_raptor_signing_task(assemble_task_id, variant, is_staging) signing_tasks[signing_task_id] = BUILDER.craft_raptor_signing_task(assemble_task_id, variant, is_staging)
for abi in ('aarch64', 'arm'):
all_raptor_craft_functions = [ all_raptor_craft_functions = [
BUILDER.craft_raptor_tp6m_cold_task(for_suite=i) BUILDER.craft_raptor_tp6m_cold_task(for_suite=i)
for i in range(1, 28) for i in range(1, 28)
] + [ ] + [
BUILDER.craft_raptor_youtube_playback_task, BUILDER.craft_raptor_youtube_playback_task,
] ]
for craft_function in all_raptor_craft_functions: for craft_function in all_raptor_craft_functions:
args = (signing_task_id, mozharness_task_id, variant, gecko_revision) args = (signing_task_id, mozharness_task_id, abi, gecko_revision)
other_tasks[taskcluster.slugId()] = craft_function(*args) other_tasks[taskcluster.slugId()] = craft_function(*args)
return (build_tasks, signing_tasks, other_tasks) return (build_tasks, signing_tasks, other_tasks)
def release(channel, is_staging, version_name): def release(channel, engine, is_staging, version_name):
variants = get_variants_for_build_type(channel) variant = get_variant('fenix' + channel.capitalize(), engine)
architectures = [variant.abi for variant in variants] taskcluster_apk_paths = variant.upstream_artifacts()
apk_paths = ["public/build/{}/target.apk".format(arch) for arch in architectures]
build_tasks = {} build_tasks = {}
signing_tasks = {} signing_tasks = {}
push_tasks = {} push_tasks = {}
build_task_id = taskcluster.slugId() build_task_id = taskcluster.slugId()
build_tasks[build_task_id] = BUILDER.craft_assemble_release_task(architectures, channel, is_staging, version_name) build_tasks[build_task_id] = BUILDER.craft_assemble_release_task(variant, is_staging, version_name)
signing_task_id = taskcluster.slugId() signing_task_id = taskcluster.slugId()
signing_tasks[signing_task_id] = BUILDER.craft_release_signing_task( signing_tasks[signing_task_id] = BUILDER.craft_release_signing_task(
build_task_id, build_task_id,
apk_paths=apk_paths, taskcluster_apk_paths,
channel=channel, channel=channel,
is_staging=is_staging, is_staging=is_staging,
) )
@ -138,7 +137,7 @@ def release(channel, is_staging, version_name):
push_task_id = taskcluster.slugId() push_task_id = taskcluster.slugId()
push_tasks[push_task_id] = BUILDER.craft_push_task( push_tasks[push_task_id] = BUILDER.craft_push_task(
signing_task_id, signing_task_id,
apks=apk_paths, taskcluster_apk_paths,
channel=channel, channel=channel,
# TODO until org.mozilla.fenix.nightly is made public, put it on the internally-testable track # TODO until org.mozilla.fenix.nightly is made public, put it on the internally-testable track
override_google_play_track=None if channel != "nightly" else "internal", override_google_play_track=None if channel != "nightly" else "internal",
@ -152,10 +151,8 @@ def nightly_to_production_app(is_staging, version_name):
# Since the Fenix nightly was launched, we've pushed it to the production app "org.mozilla.fenix" on the # Since the Fenix nightly was launched, we've pushed it to the production app "org.mozilla.fenix" on the
# "nightly" track. We're moving towards having each channel be published to its own app, but we need to # "nightly" track. We're moving towards having each channel be published to its own app, but we need to
# keep updating this "backwards-compatible" nightly for a while yet # keep updating this "backwards-compatible" nightly for a while yet
build_type = 'nightlyLegacy' variant = get_variant('fenixNightlyLegacy', 'geckoNightly')
variants = get_variants_for_build_type(build_type) taskcluster_apk_paths = variant.upstream_artifacts()
architectures = [variant.abi for variant in variants]
apk_paths = ["public/build/{}/target.apk".format(arch) for arch in architectures]
build_tasks = {} build_tasks = {}
signing_tasks = {} signing_tasks = {}
@ -163,12 +160,12 @@ def nightly_to_production_app(is_staging, version_name):
other_tasks = {} other_tasks = {}
build_task_id = taskcluster.slugId() build_task_id = taskcluster.slugId()
build_tasks[build_task_id] = BUILDER.craft_assemble_release_task(architectures, build_type, is_staging, version_name) build_tasks[build_task_id] = BUILDER.craft_assemble_release_task(variant, is_staging, version_name)
signing_task_id = taskcluster.slugId() signing_task_id = taskcluster.slugId()
signing_tasks[signing_task_id] = BUILDER.craft_release_signing_task( signing_tasks[signing_task_id] = BUILDER.craft_release_signing_task(
build_task_id, build_task_id,
apk_paths=apk_paths, taskcluster_apk_paths,
channel='production', # Since we're publishing to the "production" app, we need to sign for production channel='production', # Since we're publishing to the "production" app, we need to sign for production
index_channel='nightly', index_channel='nightly',
is_staging=is_staging, is_staging=is_staging,
@ -177,16 +174,17 @@ def nightly_to_production_app(is_staging, version_name):
push_task_id = taskcluster.slugId() push_task_id = taskcluster.slugId()
push_tasks[push_task_id] = BUILDER.craft_push_task( push_tasks[push_task_id] = BUILDER.craft_push_task(
signing_task_id, signing_task_id,
apks=apk_paths, taskcluster_apk_paths,
channel='production', # We're publishing to the "production" app on the "nightly" track channel='production', # We're publishing to the "production" app on the "nightly" track
override_google_play_track='nightly', override_google_play_track='nightly',
is_staging=is_staging, is_staging=is_staging,
) )
nimbledroid_task_id = taskcluster.slugId() if not is_staging:
other_tasks[nimbledroid_task_id] = BUILDER.craft_upload_apk_nimbledroid_task( nimbledroid_task_id = taskcluster.slugId()
build_task_id other_tasks[nimbledroid_task_id] = BUILDER.craft_upload_apk_nimbledroid_task(
) build_task_id
)
return (build_tasks, signing_tasks, push_tasks, other_tasks) return (build_tasks, signing_tasks, push_tasks, other_tasks)
@ -213,26 +211,25 @@ if __name__ == "__main__":
result = parser.parse_args() result = parser.parse_args()
command = result.command command = result.command
taskcluster_queue = taskcluster.Queue({'baseUrl': 'http://taskcluster/queue/v1'})
if command in ('pull-request'): if command == 'pull-request':
ordered_groups_of_tasks = pr() ordered_groups_of_tasks = pr()
elif command in ('push'): elif command == 'push':
ordered_groups_of_tasks = push() ordered_groups_of_tasks = push()
elif command == 'raptor': elif command == 'raptor':
ordered_groups_of_tasks = raptor(result.staging) ordered_groups_of_tasks = raptor(result.staging)
elif command == 'nightly': elif command == 'nightly':
nightly_version = datetime.datetime.now().strftime('Nightly %y%m%d %H:%M') nightly_version = datetime.datetime.now().strftime('Nightly %y%m%d %H:%M')
ordered_groups_of_tasks = release('nightly', result.staging, nightly_version) \ ordered_groups_of_tasks = release('nightly', 'geckoNightly', result.staging, nightly_version) \
+ nightly_to_production_app(result.staging, nightly_version) + nightly_to_production_app(result.staging, nightly_version)
elif command == 'github-release': elif command == 'github-release':
version = result.tag[1:] # remove prefixed "v" version = result.tag[1:] # remove prefixed "v"
beta_semver = re.compile(r'^v\d+\.\d+\.\d+-beta\.\d+$') beta_semver = re.compile(r'^v\d+\.\d+\.\d+-beta\.\d+$')
production_semver = re.compile(r'^v\d+\.\d+\.\d+(-rc\.\d+)?$') production_semver = re.compile(r'^v\d+\.\d+\.\d+(-rc\.\d+)?$')
if beta_semver.match(result.tag): if beta_semver.match(result.tag):
ordered_groups_of_tasks = release('beta', result.staging, version) ordered_groups_of_tasks = release('beta', 'geckoBeta', result.staging, version)
elif production_semver.match(result.tag): elif production_semver.match(result.tag):
ordered_groups_of_tasks = release('production', result.staging, version) ordered_groups_of_tasks = release('production', 'geckoBeta', result.staging, version)
else: else:
raise ValueError('Github tag must be in semver format and prefixed with a "v", ' raise ValueError('Github tag must be in semver format and prefixed with a "v", '
'e.g.: "v1.0.0-beta.0" (beta), "v1.0.0-rc.0" (production) or "v1.0.0" (production)') 'e.g.: "v1.0.0-beta.0" (beta), "v1.0.0-rc.0" (production) or "v1.0.0" (production)')

View File

@ -6,26 +6,28 @@ from __future__ import print_function
import json import json
import subprocess import subprocess
from lib.variant import Variant from lib.variant import Variant, VariantApk
def get_variants_for_build_type(build_type): def get_variant(build_type, engine):
print("Fetching build variants from gradle") print("Fetching variant information for build_type='{}', engine='{}'".format(build_type, engine))
output = _run_gradle_process('printBuildVariants') output = _run_gradle_process('printVariant', variantBuildType=build_type, variantEngine=engine)
content = _extract_content_from_command_output(output, prefix='variants: ') content = _extract_content_from_command_output(output, prefix='variant: ')
variants = json.loads(content) raw_variant = json.loads(content)
return Variant(
if len(variants) == 0: raw_variant['name'],
raise ValueError("Could not get build variants from gradle") build_type,
[VariantApk(build_type, raw_apk['abi'], engine, raw_apk['fileName']) for raw_apk in raw_variant['apks']]
print("Got variants: {}".format(variants)) )
return [Variant(variant_dict['name'], variant_dict['abi'], variant_dict['isSigned'], variant_dict['buildType'])
for variant_dict in variants
if variant_dict['buildType'] == build_type]
def _run_gradle_process(gradle_command): def _run_gradle_process(gradle_command, **kwargs):
process = subprocess.Popen(["./gradlew", "--no-daemon", "--quiet", gradle_command], stdout=subprocess.PIPE) gradle_properties = [
'-P{property_name}={value}'.format(property_name=property_name, value=value)
for property_name, value in kwargs.iteritems()
]
process = subprocess.Popen(["./gradlew", "--no-daemon", "--quiet", gradle_command] + gradle_properties, stdout=subprocess.PIPE)
output, err = process.communicate() output, err = process.communicate()
exit_code = process.wait() exit_code = process.wait()

View File

@ -46,21 +46,11 @@ class TaskBuilder(object):
self.date = arrow.get(date_string) self.date = arrow.get(date_string)
self.trust_level = trust_level self.trust_level = trust_level
def craft_assemble_release_task(self, architectures, build_type, is_staging, version_name): def craft_assemble_release_task(self, variant, is_staging, version_name):
artifacts = {
'public/build/{}/target.apk'.format(arch): {
"type": 'file',
"path": '/opt/fenix/app/build/outputs/apk/'
'{arch}/{build_type}/app-{arch}-{build_type}-unsigned.apk'.format(arch=arch, build_type=build_type),
"expires": taskcluster.stringDate(taskcluster.fromNow(DEFAULT_EXPIRES_IN)),
}
for arch in architectures
}
if is_staging: if is_staging:
secret_index = 'garbage/staging/project/mobile/fenix' secret_index = 'garbage/staging/project/mobile/fenix'
else: else:
secret_index = 'project/mobile/fenix/{}'.format(build_type) secret_index = 'project/mobile/fenix/{}'.format(variant.build_type)
pre_gradle_commands = ( pre_gradle_commands = (
'python automation/taskcluster/helper/get-secret.py -s {} -k {} -f {}'.format( 'python automation/taskcluster/helper/get-secret.py -s {} -k {} -f {}'.format(
@ -70,11 +60,11 @@ class TaskBuilder(object):
('sentry_dsn', '.sentry_token'), ('sentry_dsn', '.sentry_token'),
('leanplum', '.leanplum_token'), ('leanplum', '.leanplum_token'),
('adjust', '.adjust_token'), ('adjust', '.adjust_token'),
('firebase', 'app/src/{}/res/values/firebase.xml'.format(build_type)), ('firebase', 'app/src/{}/res/values/firebase.xml'.format(variant.build_type)),
) )
) )
capitalized_build_type = upper_case_first_letter(build_type) capitalized_build_type = upper_case_first_letter(variant.build_type)
gradle_commands = ( gradle_commands = (
'./gradlew --no-daemon -PversionName="{}" clean test assemble{}'.format( './gradlew --no-daemon -PversionName="{}" clean test assemble{}'.format(
version_name, capitalized_build_type), version_name, capitalized_build_type),
@ -98,14 +88,14 @@ class TaskBuilder(object):
scopes=[ scopes=[
"secrets:get:{}".format(secret_index) "secrets:get:{}".format(secret_index)
], ],
artifacts=artifacts, artifacts=variant.artifacts(),
routes=routes, routes=routes,
treeherder={ treeherder={
'jobKind': 'build', 'jobKind': 'build',
'machine': { 'machine': {
'platform': 'android-all', 'platform': 'android-all',
}, },
'symbol': '{}-A'.format(build_type), 'symbol': '{}-A'.format(variant.build_type),
'tier': 1, 'tier': 1,
}, },
) )
@ -115,19 +105,19 @@ class TaskBuilder(object):
'echo "https://fake@sentry.prod.mozaws.net/368" > .sentry_token', 'echo "https://fake@sentry.prod.mozaws.net/368" > .sentry_token',
'echo "--" > .adjust_token', 'echo "--" > .adjust_token',
'echo "-:-" > .leanplum_token', 'echo "-:-" > .leanplum_token',
'./gradlew --no-daemon clean assemble{}'.format(variant.for_gradle_command), './gradlew --no-daemon clean assemble{}'.format(variant.name),
)) ))
return self._craft_build_ish_task( return self._craft_build_ish_task(
name='assemble: {}'.format(variant.raw), name='assemble: {}'.format(variant.name),
description='Building and testing variant {}'.format(variant.raw), description='Building and testing variant {}'.format(variant.name),
command=command, command=command,
artifacts=_craft_artifacts_from_variant(variant), artifacts=variant.artifacts(),
treeherder={ treeherder={
'groupSymbol': variant.build_type, 'groupSymbol': variant.build_type,
'jobKind': 'build', 'jobKind': 'build',
'machine': { 'machine': {
'platform': variant.platform, 'platform': 'android-all',
}, },
'symbol': 'A', 'symbol': 'A',
'tier': 1, 'tier': 1,
@ -135,49 +125,33 @@ class TaskBuilder(object):
) )
def craft_assemble_pr_task(self, variant): def craft_assemble_pr_task(self, variant):
assemble_gradle_command = 'assemble{}'.format(variant.for_gradle_command)
return self._craft_clean_gradle_task( return self._craft_clean_gradle_task(
name='assemble: {}'.format(variant.raw), name='assemble: {}'.format(variant.name),
description='Building and testing variant {}'.format(variant.raw), description='Building and testing variant {}'.format(variant.name),
gradle_task=assemble_gradle_command, gradle_task='assemble{}'.format(variant.name),
artifacts=_craft_artifacts_from_variant(variant), artifacts=variant.artifacts(),
treeherder={ treeherder={
'groupSymbol': variant.build_type, 'groupSymbol': variant.build_type,
'jobKind': 'build', 'jobKind': 'build',
'machine': { 'machine': {
'platform': variant.platform, 'platform': 'android-all',
}, },
'symbol': 'A', 'symbol': 'A',
'tier': 1, 'tier': 1,
} }
) )
def craft_test_pr_task(self, variant, run_coverage=False): def craft_test_pr_task(self, variant):
test_gradle_command = '-Pcoverage jacoco{}TestReport'.format(variant.for_gradle_command) \ command = 'test{}UnitTest'.format(variant.name)
if (run_coverage and variant.abi == 'aarch64') \
else 'test{}UnitTest'.format(variant.for_gradle_command)
post_gradle_command = ('automation/taskcluster/upload_coverage_report.sh' if run_coverage else '',)
if variant.abi == 'aarch64':
command = ' && '.join(
cmd
for commands in ((test_gradle_command,), post_gradle_command)
for cmd in commands
if cmd
)
else:
command = test_gradle_command
return self._craft_clean_gradle_task( return self._craft_clean_gradle_task(
name='test: {}'.format(variant.raw), name='test: {}'.format(variant.name),
description='Building and testing variant {}'.format(variant.raw), description='Building and testing variant {}'.format(variant.name),
gradle_task=command, gradle_task=command,
treeherder={ treeherder={
'groupSymbol': variant.build_type, 'groupSymbol': variant.build_type,
'jobKind': 'test', 'jobKind': 'test',
'machine': { 'machine': {
'platform': variant.platform, 'platform': 'android-all',
}, },
'symbol': 'T', 'symbol': 'T',
'tier': 1, 'tier': 1,
@ -491,27 +465,27 @@ class TaskBuilder(object):
): ):
staging_prefix = '.staging' if is_staging else '' staging_prefix = '.staging' if is_staging else ''
routes = [ routes = [
"index.project.mobile.fenix.v2{}.performance-test.{}.{}.{}.latest.{}".format( "index.project.mobile.fenix.v2{}.performance-test.{}.{}.{}.latest".format(
staging_prefix, self.date.year, self.date.month, self.date.day, variant.abi staging_prefix, self.date.year, self.date.month, self.date.day
), ),
"index.project.mobile.fenix.v2{}.performance-test.{}.{}.{}.revision.{}.{}".format( "index.project.mobile.fenix.v2{}.performance-test.{}.{}.{}.revision.{}".format(
staging_prefix, self.date.year, self.date.month, self.date.day, self.commit, variant.abi staging_prefix, self.date.year, self.date.month, self.date.day, self.commit
), ),
"index.project.mobile.fenix.v2{}.performance-test.latest.{}".format(staging_prefix, variant.abi), "index.project.mobile.fenix.v2{}.performance-test.latest".format(staging_prefix),
] ]
return self._craft_signing_task( return self._craft_signing_task(
name='sign: {}'.format(variant.raw), name='sign: {}'.format('forPerformanceTest'),
description='Dep-signing variant {}'.format(variant.raw), description='Dep-signing variant {}'.format('forPerformanceTest'),
signing_type="dep", signing_type="dep",
assemble_task_id=assemble_task_id, assemble_task_id=assemble_task_id,
apk_paths=[DEFAULT_APK_ARTIFACT_LOCATION], apk_paths=variant.upstream_artifacts(),
routes=routes, routes=routes,
treeherder={ treeherder={
'groupSymbol': variant.build_type, 'groupSymbol': 'forPerformanceTest',
'jobKind': 'other', 'jobKind': 'other',
'machine': { 'machine': {
'platform': variant.platform, 'platform': 'android-all',
}, },
'symbol': 'As', 'symbol': 'As',
'tier': 1, 'tier': 1,
@ -553,7 +527,7 @@ class TaskBuilder(object):
) )
def craft_push_task( def craft_push_task(
self, signing_task_id, apks, channel, is_staging=False, override_google_play_track=None self, signing_task_id, apk_paths, channel, is_staging=False, override_google_play_track=None
): ):
payload = { payload = {
"commit": True, "commit": True,
@ -561,7 +535,7 @@ class TaskBuilder(object):
"certificate_alias": 'fenix' if is_staging else 'fenix-{}'.format(channel), "certificate_alias": 'fenix' if is_staging else 'fenix-{}'.format(channel),
"upstreamArtifacts": [ "upstreamArtifacts": [
{ {
"paths": apks, "paths": apk_paths,
"taskId": signing_task_id, "taskId": signing_task_id,
"taskType": "signing" "taskType": "signing"
} }
@ -596,11 +570,11 @@ class TaskBuilder(object):
def craft_raptor_tp6m_cold_task(self, for_suite): def craft_raptor_tp6m_cold_task(self, for_suite):
def craft_function(signing_task_id, mozharness_task_id, variant, gecko_revision, force_run_on_64_bit_device=False): def craft_function(signing_task_id, mozharness_task_id, abi, gecko_revision, force_run_on_64_bit_device=False):
return self._craft_raptor_task( return self._craft_raptor_task(
signing_task_id, signing_task_id,
mozharness_task_id, mozharness_task_id,
variant, abi,
gecko_revision, gecko_revision,
name_prefix='raptor tp6m-cold-{}'.format(for_suite), name_prefix='raptor tp6m-cold-{}'.format(for_suite),
description='Raptor tp6m cold on Fenix', description='Raptor tp6m cold on Fenix',
@ -610,12 +584,12 @@ class TaskBuilder(object):
) )
return craft_function return craft_function
def craft_raptor_youtube_playback_task(self, signing_task_id, mozharness_task_id, variant, gecko_revision, def craft_raptor_youtube_playback_task(self, signing_task_id, mozharness_task_id, abi, gecko_revision,
force_run_on_64_bit_device=False): force_run_on_64_bit_device=False):
return self._craft_raptor_task( return self._craft_raptor_task(
signing_task_id, signing_task_id,
mozharness_task_id, mozharness_task_id,
variant, abi,
gecko_revision, gecko_revision,
name_prefix='raptor youtube playback', name_prefix='raptor youtube playback',
description='Raptor YouTube Playback on Fenix', description='Raptor YouTube Playback on Fenix',
@ -629,7 +603,7 @@ class TaskBuilder(object):
self, self,
signing_task_id, signing_task_id,
mozharness_task_id, mozharness_task_id,
variant, abi,
gecko_revision, gecko_revision,
name_prefix, name_prefix,
description, description,
@ -638,19 +612,19 @@ class TaskBuilder(object):
group_symbol=None, group_symbol=None,
force_run_on_64_bit_device=False, force_run_on_64_bit_device=False,
): ):
worker_type = 'gecko-t-bitbar-gw-perf-p2' if force_run_on_64_bit_device or variant.abi == 'aarch64' else 'gecko-t-bitbar-gw-perf-g5' worker_type = 'gecko-t-bitbar-gw-perf-p2' if force_run_on_64_bit_device or abi == 'aarch64' else 'gecko-t-bitbar-gw-perf-g5'
if force_run_on_64_bit_device: if force_run_on_64_bit_device:
treeherder_platform = 'android-hw-p2-8-0-arm7-api-16' treeherder_platform = 'android-hw-p2-8-0-arm7-api-16'
elif variant.abi == 'arm': elif abi == 'arm':
treeherder_platform = 'android-hw-g5-7-0-arm7-api-16' treeherder_platform = 'android-hw-g5-7-0-arm7-api-16'
elif variant.abi == 'aarch64': elif abi == 'aarch64':
treeherder_platform = 'android-hw-p2-8-0-android-aarch64' treeherder_platform = 'android-hw-p2-8-0-android-aarch64'
else: else:
raise ValueError('Unsupported architecture "{}"'.format(variant.abi)) raise ValueError('Unsupported architecture "{}"'.format(abi))
task_name = '{}: {} {}'.format( task_name = '{}: forPerformanceTest {}'.format(
name_prefix, variant.raw, '(on 64-bit-device)' if force_run_on_64_bit_device else '' name_prefix, '(on 64-bit-device)' if force_run_on_64_bit_device else ''
) )
apk_url = '{}/{}/artifacts/{}'.format(_DEFAULT_TASK_URL, signing_task_id, apk_url = '{}/{}/artifacts/{}'.format(_DEFAULT_TASK_URL, signing_task_id,
@ -667,7 +641,7 @@ class TaskBuilder(object):
"--download-symbols=ondemand", "--download-symbols=ondemand",
]] ]]
# Bug 1558456 - Stop tracking youtube-playback-test on motoG5 for >1080p cases # Bug 1558456 - Stop tracking youtube-playback-test on motoG5 for >1080p cases
if variant.abi == 'arm' and test_name == 'raptor-youtube-playback': if abi == 'arm' and test_name == 'raptor-youtube-playback':
params_query = '&'.join(ARM_RAPTOR_URL_PARAMS) params_query = '&'.join(ARM_RAPTOR_URL_PARAMS)
add_extra_params_option = "--test-url-params={}".format(params_query) add_extra_params_option = "--test-url-params={}".format(params_query)
command[0].append(add_extra_params_option) command[0].append(add_extra_params_option)
@ -744,16 +718,6 @@ class TaskBuilder(object):
) )
def _craft_artifacts_from_variant(variant):
return {
DEFAULT_APK_ARTIFACT_LOCATION: {
'type': 'file',
'path': variant.apk_absolute_path(),
'expires': taskcluster.stringDate(taskcluster.fromNow(DEFAULT_EXPIRES_IN)),
}
}
def schedule_task(queue, taskId, task): def schedule_task(queue, taskId, task):
print("TASK", taskId) print("TASK", taskId)
print(json.dumps(task, indent=4, separators=(',', ': '))) print(json.dumps(task, indent=4, separators=(',', ': ')))

View File

@ -1,20 +1,27 @@
class Variant: import taskcluster
def __init__(self, raw, abi, is_signed, build_type):
self.raw = raw
class VariantApk:
def __init__(self, build_type, abi, engine, file_name):
self.abi = abi self.abi = abi
self.taskcluster_path = 'public/build/{}/{}/target.apk'.format(abi, engine)
self.absolute_path = '/opt/fenix/app/build/outputs/apk/{}/{}/{}'.format(engine, build_type, file_name)
class Variant:
def __init__(self, name, build_type, apks):
self.name = name
self.build_type = build_type self.build_type = build_type
self._is_signed = is_signed self._apks = apks
self.for_gradle_command = raw[:1].upper() + raw[1:]
self.platform = 'android-{}-{}'.format(self.abi, self.build_type)
def apk_absolute_path(self): def artifacts(self):
return '/opt/fenix/app/build/outputs/apk/{abi}/{build_type}/app-{abi}-{build_type}{unsigned}.apk'.format( return {
build_type=self.build_type, apk.taskcluster_path: {
abi=self.abi, 'type': 'file',
unsigned='' if self._is_signed else '-unsigned', 'path': apk.absolute_path,
) 'expires': taskcluster.stringDate(taskcluster.fromNow('1 year')),
} for apk in self._apks
}
@staticmethod def upstream_artifacts(self):
def from_values(abi, is_signed, build_type): return [apk.taskcluster_path for apk in self._apks]
raw = abi + build_type[:1].upper() + build_type[1:]
return Variant(raw, abi, is_signed, build_type)

View File

@ -17,4 +17,4 @@
./gradlew -q \ ./gradlew -q \
ktlint \ ktlint \
detekt \ detekt \
app:assembleX86Debug app:assembleDebug

View File

@ -17,5 +17,5 @@ android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableR8=true android.enableR8=true
android.enableR8.fullMode=true android.enableR8.fullMode=true
android.enableUnitTestBinaryResources=true android.enableUnitTestBinaryResources=false
org.gradle.parallel=false org.gradle.parallel=false