summaryrefslogtreecommitdiffstats
path: root/tools/phabricator/mach_commands.py
blob: 657267a5109445dacf75b4cd9180f5a6f8a6740a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 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/.

import mozfile
from mach.decorators import Command, CommandArgument
from mach.site import MozSiteMetadata


@Command(
    "install-moz-phab",
    category="misc",
    description="Install patch submission tool.",
)
@CommandArgument(
    "--force",
    "-f",
    action="store_true",
    help="Force installation even if already installed.",
)
def install_moz_phab(command_context, force=False):
    import logging
    import os
    import re
    import subprocess
    import sys

    existing = mozfile.which("moz-phab")
    if existing and not force:
        command_context.log(
            logging.ERROR,
            "already_installed",
            {},
            "moz-phab is already installed in %s." % existing,
        )
        sys.exit(1)

    active_metadata = MozSiteMetadata.from_runtime()
    original_python = active_metadata.original_python.python_path
    is_external_python_virtualenv = (
        subprocess.check_output(
            [
                original_python,
                "-c",
                "import sys; print(sys.prefix != sys.base_prefix)",
            ]
        ).strip()
        == b"True"
    )

    # pip3 is part of Python since 3.4, however some distros choose to
    # remove core components from languages, so show a useful error message
    # if pip3 is missing.
    has_pip = subprocess.run([original_python, "-c", "import pip"]).returncode == 0
    if not has_pip:
        command_context.log(
            logging.ERROR,
            "pip_not_installed",
            {},
            "Python 3's `pip` is not installed. Try installing it with your system "
            "package manager.",
        )
        sys.exit(1)

    command = [original_python, "-m", "pip", "install", "--upgrade", "MozPhab"]

    if (
        sys.platform.startswith("linux")
        or sys.platform.startswith("openbsd")
        or sys.platform.startswith("dragonfly")
        or sys.platform.startswith("freebsd")
        or sys.platform.startswith("netbsd")
    ):
        # On all Linux and BSD distros we consider doing a user installation.
        platform_prefers_user_install = True

    elif sys.platform.startswith("darwin"):
        # On MacOS we require brew or ports, which work better without --user.
        platform_prefers_user_install = False

    elif sys.platform.startswith("win32") or sys.platform.startswith("msys"):
        # Likewise for Windows we assume a system level install is preferred.
        platform_prefers_user_install = False

    else:
        # Unsupported, default to --user.
        command_context.log(
            logging.WARNING,
            "unsupported_platform",
            {},
            "Unsupported platform (%s), assuming per-user installation is "
            "preferred." % sys.platform,
        )
        platform_prefers_user_install = True

    command_env = os.environ.copy()

    if platform_prefers_user_install and not is_external_python_virtualenv:
        # Virtual environments don't see user packages, so only perform a user
        # installation if we're not within one.
        command.append("--user")
        # This is needed to work around a problem on Ubuntu 23.04 and Debian 12
        # See bug 1831442 for more details
        command_env["PIP_BREAK_SYSTEM_PACKAGES"] = "1"

    command_context.log(logging.INFO, "run", {}, "Installing moz-phab")
    subprocess.run(command, env=command_env)

    # There isn't an elegant way of determining the CLI location of a pip-installed package.
    # The viable mechanism used here is to:
    # 1. Get the list of info about the installed package via pip
    # 2. Parse out the install location. This gives us the python environment in which the
    #    package is installed
    # 3. Parse out the relative location of the cli script
    # 4. Join the two paths, and execute the script at that location

    info = subprocess.check_output(
        [original_python, "-m", "pip", "show", "-f", "MozPhab"],
        universal_newlines=True,
    )
    mozphab_package_location = re.compile(r"Location: (.*)").search(info).group(1)
    # This needs to match "moz-phab" (*nix) and "moz-phab.exe" (Windows) while missing
    # "moz-phab-script.py" (Windows).
    potential_cli_paths = re.compile(
        r"([^\s]*moz-phab(?:\.exe)?)$", re.MULTILINE
    ).findall(info)

    if len(potential_cli_paths) != 1:
        command_context.log(
            logging.WARNING,
            "no_mozphab_console_script",
            {},
            "Could not find the CLI script for moz-phab. Skipping install-certificate step.",
        )
        sys.exit(1)

    console_script = os.path.realpath(
        os.path.join(mozphab_package_location, potential_cli_paths[0])
    )
    subprocess.run([console_script, "install-certificate"])