Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions mobile/android/fenix/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ android.defaultConfig.with {
def buildDate = LocalDateTime.parse(getBuildId(gradle.mozconfig.topobjdir, providers.environmentVariable("MOZ_BUILD_DATE").getOrNull()), DateTimeFormatter.ofPattern("yyyyMMddHHmmss")).toString()
buildConfigField 'String', 'BUILD_DATE', '"' + buildDate + '"'

// Default for the manifest merges that onVariants does not reach, notably the unit test and
// android test components. Real variants override this below.
manifestPlaceholders.put('sentryProguardUuid', '')

try {
def token = new File("${rootDir}/.adjust_token").text.trim()
buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"'
Expand Down Expand Up @@ -415,6 +419,35 @@ android.buildTypes.debug.with {
buildConfigField 'String', 'SECRET_SETTINGS_OVERRIDES', '"' + secretSettingsOverrides + '"'
}

// -------------------------------------------------------------------------------------------------
// Sentry: Generate the ProGuard UUID used to match crash reports with their mapping file
//
// The same UUID is embedded in the manifest, for the SDK to report, and written to a file, for the
// sentry-upload task to pass to sentry-cli. Debug builds are not minified, so they get no UUID.
// -------------------------------------------------------------------------------------------------

androidComponents {
onVariants(selector().all()) { variant ->
def proguardUuid = variant.buildType == 'debug' ? '' : UUID.nameUUIDFromBytes(
(getBuildId(gradle.mozconfig.topobjdir, providers.environmentVariable("MOZ_BUILD_DATE").getOrNull()) + variant.name).bytes
).toString()
variant.manifestPlaceholders.put('sentryProguardUuid', proguardUuid)

def variantNameCap = variant.name.capitalize()
def uuidOutputFile = layout.buildDirectory.file("sentry/${variant.name}/proguard-uuid.txt")
def writeUuid = tasks.register("writeSentryProguardUuid${variantNameCap}") {
outputs.file(uuidOutputFile)
doLast {
def destFile = uuidOutputFile.get().asFile
destFile.parentFile.mkdirs()
destFile.text = proguardUuid
}
}
tasks.matching { it.name == "assemble${variantNameCap}" || it.name == "bundle${variantNameCap}" }
.configureEach { dependsOn(writeUuid) }
}
}

// Generate Kotlin code for the Fenix Glean metrics.
ext {
// Enable expiration by major version.
Expand Down
3 changes: 3 additions & 0 deletions mobile/android/fenix/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,9 @@
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<meta-data
android:name="io.sentry.proguard-uuid"
android:value="${sentryProguardUuid}" />
<!-- Removes the default Workmanager initialization so that we can use on-demand initializer. -->
<provider
android:name="androidx.startup.InitializationProvider"
Expand Down
35 changes: 35 additions & 0 deletions mobile/android/focus-android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,41 @@ android.defaultConfig.with {
} catch (FileNotFoundException ignored) {
buildConfigField 'String', 'SENTRY_TOKEN', '""'
}

// Default for the manifest merges that onVariants does not reach, notably the unit test and
// android test components. Real variants override this below.
manifestPlaceholders.put("sentryProguardUuid", "")
}

// -------------------------------------------------------------------------------------------------
// Sentry: Generate the ProGuard UUID used to match crash reports with their mapping file
//
// The same UUID is embedded in the manifest, for the SDK to report, and written to a file, for the
// sentry-upload task to pass to sentry-cli. Debug builds are not minified, so they get no UUID.
// -------------------------------------------------------------------------------------------------

androidComponents {
def buildId = org.mozilla.conventions.BuildConfigKt.getBuildId(
gradle.mozconfig.topobjdir, providers.environmentVariable("MOZ_BUILD_DATE").getOrNull())

onVariants(selector().all()) { variant ->
def proguardUuid = variant.buildType == "debug" ? "" :
UUID.nameUUIDFromBytes((buildId + variant.name).bytes).toString()
variant.manifestPlaceholders.put("sentryProguardUuid", proguardUuid)

def variantNameCap = variant.name.capitalize()
def uuidOutputFile = layout.buildDirectory.file("sentry/${variant.name}/proguard-uuid.txt")
def writeUuid = tasks.register("writeSentryProguardUuid${variantNameCap}") {
outputs.file(uuidOutputFile)
doLast {
def destFile = uuidOutputFile.get().asFile
destFile.parentFile.mkdirs()
destFile.text = proguardUuid
}
}
tasks.matching { it.name == "assemble${variantNameCap}" || it.name == "bundle${variantNameCap}" }
.configureEach { dependsOn(writeUuid) }
}
}

// -------------------------------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions mobile/android/focus-android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@
</provider>


<meta-data
android:name="io.sentry.proguard-uuid"
android:value="${sentryProguardUuid}" />

</application>

</manifest>
24 changes: 24 additions & 0 deletions taskcluster/android_taskgraph/transforms/build_android_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,28 @@ def add_artifacts(config, tasks):
})
task["attributes"]["aab"] = artifact_template["name"]

# Only bundles are minified and consumed by the sentry-upload kind. Debug builds have no
# mapping file at all, so declaring these artifacts there would leave them permanently
# missing.
if config.kind == "build-bundle" and gradle_build_type != "debug":
artifacts.append({
"type": "file",
"name": "public/build/mapping.txt",
"path": (
"/builds/worker/workspace/obj-build/gradle/build/mobile/android"
f"/{source_project_name}/app/outputs/mapping"
f"/{gradle_build_name}/mapping.txt"
),
})

artifacts.append({
"type": "file",
"name": "public/build/sentry-proguard-uuid.txt",
"path": (
"/builds/worker/workspace/obj-build/gradle/build/mobile/android"
f"/{source_project_name}/app/sentry"
f"/{gradle_build_name}/proguard-uuid.txt"
),
})

yield task
1 change: 1 addition & 0 deletions taskcluster/docker/sentry/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VOLUME /builds/worker/checkouts

ADD prepare.sh /setup/prepare-docker.sh
ADD submit_sentry_release.sh /usr/bin/submit_sentry_release.sh
ADD upload_proguard_mapping.sh /usr/bin/upload_proguard_mapping.sh
RUN /bin/bash /setup/prepare-docker.sh && rm -R /setup

# Set a default command useful for debugging
Expand Down
49 changes: 49 additions & 0 deletions taskcluster/docker/sentry/upload_proguard_mapping.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash

# 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/.

set -o nounset
set -o pipefail

MAPPING_FILE="$1"
UUID_FILE="$2"

run() {
local proguard_uuid
proguard_uuid=$(cat "$UUID_FILE") || return 1
local secret
secret=$(curl --silent --fail "http://taskcluster/secrets/v1/secret/$SENTRY_SECRET") || return 1
local sentry_auth_token sentry_org sentry_project sentry_url
sentry_auth_token=$(echo "$secret" | jq -r ".secret.sentryToken")
sentry_org=$(echo "$secret" | jq -r ".secret.sentryOrg")
sentry_project=$(echo "$secret" | jq -r ".secret.sentryProject")
sentry_url=$(echo "$secret" | jq -r ".secret.sentryUrl")

SENTRY_AUTH_TOKEN="$sentry_auth_token" sentry-cli \
--url "$sentry_url" \
--org "$sentry_org" \
dif upload \
--type proguard \
--uuid "$proguard_uuid" \
--project "$sentry_project" \
"$MAPPING_FILE" || return 1
}

with_backoff() {
local failures=0
while ! "$@"; do
failures=$(( failures + 1 ))
if (( failures >= 5 )); then
echo "[with_backoff] Unable to succeed after 5 tries, failing the job."
return 1
else
local seconds=$(( 2 ** (failures - 1) ))
echo "[with_backoff] Retrying in $seconds second(s)"
sleep "$seconds"
fi
done
}

with_backoff run
4 changes: 4 additions & 0 deletions taskcluster/docs/kinds.md
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,10 @@ Interact with Sentry, such as by publishing new project releases.

Generate missing macOS and windows system symbols from crash reports.

## sentry-upload

Upload Android ProGuard mapping files to Sentry to enable deobfuscation of crash stack traces.

## system-symbols-upload

Upload macOS and windows system symbols to tecken.
Expand Down
49 changes: 49 additions & 0 deletions taskcluster/gecko_taskgraph/transforms/sentry_upload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# 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/.

from taskgraph.transforms.base import TransformSequence

transforms = TransformSequence()

_SENTRY_SECRET_TMPL = "project/releng/gecko/build/level-{level}/sentry-upload-{app}"


# Attributes that `copy-attributes` brings over from the upstream build-bundle task but that the
# task transform then overwrites with its own defaults, paired with the task key each has to be
# promoted back into. `shipping_phase` and `shipping_product` are deliberately absent: the task
# transform sets those with setdefault, so the copied values already survive.
_SCHEDULING_ATTRIBUTES = (
("run_on_projects", "run-on-projects"),
("run_on_repo_type", "run-on-repo-type"),
)


def _get_app(build_type):
if build_type.startswith("fenix-"):
return "fenix"
return "focus-android"


@transforms.add
def sentry_upload(config, tasks):
level = config.params["level"]
for task in tasks:
attributes = task["attributes"]
build_type = attributes.get("build-type", "")
secret_path = _SENTRY_SECRET_TMPL.format(
app=_get_app(build_type),
level=level,
)

task["worker"].setdefault("env", {})["SENTRY_SECRET"] = secret_path
task.setdefault("scopes", []).append(f"secrets:get:{secret_path}")

# Mirror the upstream build's scheduling rather than restating it per build type, so an
# upload never pulls a shippable build into a graph the build itself would not run in.
for attribute, key in _SCHEDULING_ATTRIBUTES:
value = attributes.get(attribute)
if value is not None:
task.setdefault(key, value)

yield task
50 changes: 50 additions & 0 deletions taskcluster/kinds/sentry-upload/kind.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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/.
---
loader: taskgraph.loader.transform:loader

transforms:
- taskgraph.transforms.from_deps
- gecko_taskgraph.transforms.sentry_upload
- android_taskgraph.transforms.treeherder
- gecko_taskgraph.transforms.job
- gecko_taskgraph.transforms.task

kind-dependencies:
- build-bundle

tasks:
sentry-upload:
description: Upload Android ProGuard mapping file to Sentry
from-deps:
with-attributes:
build-type:
- fenix-nightly
- fenix-release
- fenix-beta
- focus-nightly
- focus-release
- focus-beta
- klar-release
copy-attributes: true
fetches:
build-bundle:
- artifact: mapping.txt
extract: false
- artifact: sentry-proguard-uuid.txt
extract: false
worker-type: b-linux
worker:
docker-image: {in-tree: sentry}
taskcluster-proxy: true
max-run-time: 1800
run:
using: run-task
checkout: false
command: /usr/bin/upload_proguard_mapping.sh /builds/worker/fetches/mapping.txt /builds/worker/fetches/sentry-proguard-uuid.txt
treeherder:
symbol:
by-build-type:
klar-.*: klar-map
default: map
Loading