tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

job.py (6532B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 
      6 from shlex import quote as shell_quote
      7 
      8 from gecko_taskgraph.transforms.job import configure_taskdesc_for_run, run_job_using
      9 from taskgraph.util import path
     10 from taskgraph.util.schema import Schema, taskref_or_string
     11 from voluptuous import Any, Optional, Required
     12 
     13 secret_schema = {
     14    Required("name"): str,
     15    Required("path"): str,
     16    Required("key"): str,
     17    Optional("json"): bool,
     18    Optional("decode"): bool,
     19 }
     20 
     21 dummy_secret_schema = {
     22    Required("content"): str,
     23    Required("path"): str,
     24    Optional("json"): bool,
     25 }
     26 
     27 gradlew_schema = Schema({
     28    Required("using"): "gradlew",
     29    Optional("pre-gradlew"): [[str]],
     30    Required("gradlew"): [str],
     31    Optional("post-gradlew"): [[str]],
     32    # Base work directory used to set up the task.
     33    Required("workdir"): str,
     34    Optional("use-caches"): Any(bool, [str]),
     35    Optional("secrets"): [secret_schema],
     36    Optional("dummy-secrets"): [dummy_secret_schema],
     37 })
     38 
     39 run_commands_schema = Schema({
     40    Required("using"): "run-commands",
     41    Optional("pre-commands"): [[str]],
     42    Required("commands"): [[taskref_or_string]],
     43    Required("workdir"): str,
     44    Optional("use-caches"): Any(bool, [str]),
     45    Optional("secrets"): [secret_schema],
     46    Optional("dummy-secrets"): [dummy_secret_schema],
     47 })
     48 
     49 
     50 @run_job_using("docker-worker", "run-commands", schema=run_commands_schema)
     51 def configure_run_commands_schema(config, job, taskdesc):
     52    run = job["run"]
     53    pre_commands = run.pop("pre-commands", [])
     54    pre_commands += [
     55        _generate_dummy_secret_command(secret)
     56        for secret in run.pop("dummy-secrets", [])
     57    ]
     58    pre_commands += [
     59        _generate_secret_command(secret) for secret in run.get("secrets", [])
     60    ]
     61 
     62    all_commands = pre_commands + run.pop("commands", [])
     63 
     64    run["command"] = _convert_commands_to_string(all_commands)
     65    _inject_secrets_scopes(run, taskdesc)
     66    _set_run_task_attributes(job)
     67    configure_taskdesc_for_run(config, job, taskdesc, job["worker"]["implementation"])
     68 
     69 
     70 @run_job_using("docker-worker", "gradlew", schema=gradlew_schema)
     71 def configure_gradlew(config, job, taskdesc):
     72    run = job["run"]
     73    worker = taskdesc["worker"] = job["worker"]
     74 
     75    fetches_dir = "/builds/worker/fetches"
     76    topsrc_dir = "/builds/worker/checkouts/gecko"
     77    worker.setdefault("env", {}).update({
     78        "ANDROID_SDK_ROOT": path.join(fetches_dir, "android-sdk-linux"),
     79        "GRADLE_USER_HOME": path.join(
     80            topsrc_dir, "mobile/android/gradle/dotgradle-offline"
     81        ),
     82        "MOZ_BUILD_DATE": config.params["moz_build_date"],
     83    })
     84    worker["env"].setdefault(
     85        "MOZCONFIG",
     86        path.join(
     87            topsrc_dir,
     88            "mobile/android/config/mozconfigs/android-arm/nightly-android-lints",
     89        ),
     90    )
     91    worker["env"].setdefault(
     92        "MOZ_ANDROID_FAT_AAR_ARCHITECTURES", "armeabi-v7a,arm64-v8a,x86_64"
     93    )
     94 
     95    dummy_secrets = [
     96        _generate_dummy_secret_command(secret)
     97        for secret in run.pop("dummy-secrets", [])
     98    ]
     99    secrets = [_generate_secret_command(secret) for secret in run.get("secrets", [])]
    100    worker["env"].update({
    101        "PRE_GRADLEW": _convert_commands_to_string(run.pop("pre-gradlew", [])),
    102        "GET_SECRETS": _convert_commands_to_string(dummy_secrets + secrets),
    103        "GRADLEW_ARGS": " ".join(run.pop("gradlew")),
    104        "POST_GRADLEW": _convert_commands_to_string(run.pop("post-gradlew", [])),
    105    })
    106    run["command"] = (
    107        "/builds/worker/checkouts/gecko/taskcluster/scripts/builder/build-android.sh"
    108    )
    109    _inject_secrets_scopes(run, taskdesc)
    110    _set_run_task_attributes(job)
    111    configure_taskdesc_for_run(config, job, taskdesc, job["worker"]["implementation"])
    112 
    113 
    114 def _generate_secret_command(secret):
    115    secret_command = [
    116        "/builds/worker/checkouts/gecko/taskcluster/scripts/get-secret.py",
    117        "-s",
    118        secret["name"],
    119        "-k",
    120        secret["key"],
    121        "-f",
    122        secret["path"],
    123    ]
    124    if secret.get("json"):
    125        secret_command.append("--json")
    126 
    127    if secret.get("decode"):
    128        secret_command.append("--decode")
    129 
    130    return secret_command
    131 
    132 
    133 def _generate_dummy_secret_command(secret):
    134    secret_command = [
    135        "/builds/worker/checkouts/gecko/taskcluster/scripts/write-dummy-secret.py",
    136        "-f",
    137        secret["path"],
    138        "-c",
    139        secret["content"],
    140    ]
    141    if secret.get("json"):
    142        secret_command.append("--json")
    143 
    144    return secret_command
    145 
    146 
    147 def _convert_commands_to_string(commands):
    148    should_artifact_reference = False
    149    should_task_reference = False
    150 
    151    sanitized_commands = []
    152    for command in commands:
    153        sanitized_parts = []
    154        for part in command:
    155            if isinstance(part, dict):
    156                if "artifact-reference" in part:
    157                    part_string = part["artifact-reference"]
    158                    should_artifact_reference = True
    159                elif "task-reference" in part:
    160                    part_string = part["task-reference"]
    161                    should_task_reference = True
    162                else:
    163                    raise ValueError(f"Unsupported dict: {part}")
    164            else:
    165                part_string = part
    166 
    167            sanitized_parts.append(part_string)
    168        sanitized_commands.append(sanitized_parts)
    169 
    170    shell_quoted_commands = [
    171        " ".join(map(shell_quote, command)) for command in sanitized_commands
    172    ]
    173    full_string_command = " && ".join(shell_quoted_commands)
    174 
    175    if should_artifact_reference and should_task_reference:
    176        raise NotImplementedError(
    177            '"arifact-reference" and "task-reference" cannot be both used'
    178        )
    179    elif should_artifact_reference:
    180        return {"artifact-reference": full_string_command}
    181    elif should_task_reference:
    182        return {"task-reference": full_string_command}
    183    else:
    184        return full_string_command
    185 
    186 
    187 def _inject_secrets_scopes(run, taskdesc):
    188    secrets = run.pop("secrets", [])
    189    scopes = taskdesc.setdefault("scopes", [])
    190    new_secret_scopes = ["secrets:get:{}".format(secret["name"]) for secret in secrets]
    191    new_secret_scopes = list(
    192        set(new_secret_scopes)
    193    )  # Scopes must not have any duplicates
    194    scopes.extend(new_secret_scopes)
    195 
    196 
    197 def _set_run_task_attributes(job):
    198    run = job["run"]
    199    run["cwd"] = "{checkout}"
    200    run["using"] = "run-task"