# 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 shlex
import quote
as shell_quote
from gecko_taskgraph.transforms.job
import configure_taskdesc_for_run, run_job_using
from taskgraph.util
import path
from taskgraph.util.schema
import Schema, taskref_or_string
from voluptuous
import Any, Optional, Required
secret_schema = {
Required(
"name"): str,
Required(
"path"): str,
Required(
"key"): str,
Optional(
"json"): bool,
Optional(
"decode"): bool,
}
dummy_secret_schema = {
Required(
"content"): str,
Required(
"path"): str,
Optional(
"json"): bool,
}
gradlew_schema = Schema(
{
Required(
"using"):
"gradlew",
Optional(
"pre-gradlew"): [[str]],
Required(
"gradlew"): [str],
Optional(
"post-gradlew"): [[str]],
# Base work directory used to set up the task.
Required(
"workdir"): str,
Optional(
"use-caches"): Any(bool, [str]),
Optional(
"secrets"): [secret_schema],
Optional(
"dummy-secrets"): [dummy_secret_schema],
}
)
run_commands_schema = Schema(
{
Required(
"using"):
"run-commands",
Optional(
"pre-commands"): [[str]],
Required(
"commands"): [[taskref_or_string]],
Required(
"workdir"): str,
Optional(
"use-caches"): Any(bool, [str]),
Optional(
"secrets"): [secret_schema],
Optional(
"dummy-secrets"): [dummy_secret_schema],
}
)
@run_job_using(
"docker-worker",
"run-commands", schema=run_commands_schema)
def configure_run_commands_schema(config, job, taskdesc):
run = job[
"run"]
pre_commands = run.pop(
"pre-commands", [])
pre_commands += [
_generate_dummy_secret_command(secret)
for secret
in run.pop(
"dummy-secrets", [])
]
pre_commands += [
_generate_secret_command(secret)
for secret
in run.get(
"secrets", [])
]
all_commands = pre_commands + run.pop(
"commands", [])
run[
"command"] = _convert_commands_to_string(all_commands)
_inject_secrets_scopes(run, taskdesc)
_set_run_task_attributes(job)
configure_taskdesc_for_run(config, job, taskdesc, job[
"worker"][
"implementation"])
@run_job_using(
"docker-worker",
"gradlew", schema=gradlew_schema)
def configure_gradlew(config, job, taskdesc):
run = job[
"run"]
worker = taskdesc[
"worker"] = job[
"worker"]
fetches_dir =
"/builds/worker/fetches"
topsrc_dir =
"/builds/worker/checkouts/gecko"
worker.setdefault(
"env", {}).update(
{
"ANDROID_SDK_ROOT": path.join(fetches_dir,
"android-sdk-linux"),
"GRADLE_USER_HOME": path.join(
topsrc_dir,
"mobile/android/gradle/dotgradle-offline"
),
"MOZ_BUILD_DATE": config.params[
"moz_build_date"],
}
)
worker[
"env"].setdefault(
"MOZCONFIG",
path.join(
topsrc_dir,
"mobile/android/config/mozconfigs/android-arm/nightly-android-lints",
),
)
worker[
"env"].setdefault(
"MOZ_ANDROID_FAT_AAR_ARCHITECTURES",
"armeabi-v7a,arm64-v8a,x86,x86_64"
)
dummy_secrets = [
_generate_dummy_secret_command(secret)
for secret
in run.pop(
"dummy-secrets", [])
]
secrets = [_generate_secret_command(secret)
for secret
in run.get(
"secrets", [])]
worker[
"env"].update(
{
"PRE_GRADLEW": _convert_commands_to_string(run.pop(
"pre-gradlew", [])),
"GET_SECRETS": _convert_commands_to_string(dummy_secrets + secrets),
"GRADLEW_ARGS":
" ".join(run.pop(
"gradlew")),
"POST_GRADLEW": _convert_commands_to_string(run.pop(
"post-gradlew", [])),
}
)
run[
"command"] = (
"/builds/worker/checkouts/gecko/taskcluster/scripts/builder/build-android.sh"
)
_inject_secrets_scopes(run, taskdesc)
_set_run_task_attributes(job)
configure_taskdesc_for_run(config, job, taskdesc, job[
"worker"][
"implementation"])
def _generate_secret_command(secret):
secret_command = [
"/builds/worker/checkouts/gecko/taskcluster/scripts/get-secret.py",
"-s",
secret[
"name"],
"-k",
secret[
"key"],
"-f",
secret[
"path"],
]
if secret.get(
"json"):
secret_command.append(
"--json")
if secret.get(
"decode"):
secret_command.append(
"--decode")
return secret_command
def _generate_dummy_secret_command(secret):
secret_command = [
"/builds/worker/checkouts/gecko/taskcluster/scripts/write-dummy-secret.py",
"-f",
secret[
"path"],
"-c",
secret[
"content"],
]
if secret.get(
"json"):
secret_command.append(
"--json")
return secret_command
def _convert_commands_to_string(commands):
should_artifact_reference =
False
should_task_reference =
False
sanitized_commands = []
for command
in commands:
sanitized_parts = []
for part
in command:
if isinstance(part, dict):
if "artifact-reference" in part:
part_string = part[
"artifact-reference"]
should_artifact_reference =
True
elif "task-reference" in part:
part_string = part[
"task-reference"]
should_task_reference =
True
else:
raise ValueError(f
"Unsupported dict: {part}")
else:
part_string = part
sanitized_parts.append(part_string)
sanitized_commands.append(sanitized_parts)
shell_quoted_commands = [
" ".join(map(shell_quote, command))
for command
in sanitized_commands
]
full_string_command =
" && ".join(shell_quoted_commands)
if should_artifact_reference
and should_task_reference:
raise NotImplementedError(
'"arifact-reference" and "task-reference" cannot be both used'
)
elif should_artifact_reference:
return {
"artifact-reference": full_string_command}
elif should_task_reference:
return {
"task-reference": full_string_command}
else:
return full_string_command
def _inject_secrets_scopes(run, taskdesc):
secrets = run.pop(
"secrets", [])
scopes = taskdesc.setdefault(
"scopes", [])
new_secret_scopes = [
"secrets:get:{}".format(secret[
"name"])
for secret
in secrets]
new_secret_scopes = list(
set(new_secret_scopes)
)
# Scopes must not have any duplicates
scopes.extend(new_secret_scopes)
def _set_run_task_attributes(job):
run = job[
"run"]
run[
"cwd"] =
"{checkout}"
run[
"using"] =
"run-task"