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/.
6 from mach
.decorators
import Command
, CommandArgument
7 from mach
.site
import MozSiteMetadata
13 description
="Install patch submission tool.",
19 help="Force installation even if already installed.",
21 def install_moz_phab(command_context
, force
=False):
28 existing
= mozfile
.which("moz-phab")
29 if existing
and not force
:
34 "moz-phab is already installed in %s." % existing
,
38 active_metadata
= MozSiteMetadata
.from_runtime()
39 original_python
= active_metadata
.original_python
.python_path
40 is_external_python_virtualenv
= (
41 subprocess
.check_output(
45 "import sys; print(sys.prefix != sys.base_prefix)",
51 # pip3 is part of Python since 3.4, however some distros choose to
52 # remove core components from languages, so show a useful error message
54 has_pip
= subprocess
.run([original_python
, "-c", "import pip"]).returncode
== 0
60 "Python 3's `pip` is not installed. Try installing it with your system "
65 command
= [original_python
, "-m", "pip", "install", "--upgrade", "MozPhab"]
68 sys
.platform
.startswith("linux")
69 or sys
.platform
.startswith("openbsd")
70 or sys
.platform
.startswith("dragonfly")
71 or sys
.platform
.startswith("freebsd")
72 or sys
.platform
.startswith("netbsd")
74 # On all Linux and BSD distros we consider doing a user installation.
75 platform_prefers_user_install
= True
77 elif sys
.platform
.startswith("darwin"):
78 # On MacOS we require brew or ports, which work better without --user.
79 platform_prefers_user_install
= False
81 elif sys
.platform
.startswith("win32") or sys
.platform
.startswith("msys"):
82 # Likewise for Windows we assume a system level install is preferred.
83 platform_prefers_user_install
= False
86 # Unsupported, default to --user.
89 "unsupported_platform",
91 "Unsupported platform (%s), assuming per-user installation is "
92 "preferred." % sys
.platform
,
94 platform_prefers_user_install
= True
96 command_env
= os
.environ
.copy()
98 if platform_prefers_user_install
and not is_external_python_virtualenv
:
99 # Virtual environments don't see user packages, so only perform a user
100 # installation if we're not within one.
101 command
.append("--user")
102 # This is needed to work around a problem on Ubuntu 23.04 and Debian 12
103 # See bug 1831442 for more details
104 command_env
["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
106 command_context
.log(logging
.INFO
, "run", {}, "Installing moz-phab")
107 subprocess
.run(command
, env
=command_env
)
109 # There isn't an elegant way of determining the CLI location of a pip-installed package.
110 # The viable mechanism used here is to:
111 # 1. Get the list of info about the installed package via pip
112 # 2. Parse out the install location. This gives us the python environment in which the
113 # package is installed
114 # 3. Parse out the relative location of the cli script
115 # 4. Join the two paths, and execute the script at that location
117 info
= subprocess
.check_output(
118 [original_python
, "-m", "pip", "show", "-f", "MozPhab"],
119 universal_newlines
=True,
121 mozphab_package_location
= re
.compile(r
"Location: (.*)").search(info
).group(1)
122 # This needs to match "moz-phab" (*nix) and "moz-phab.exe" (Windows) while missing
123 # "moz-phab-script.py" (Windows).
124 potential_cli_paths
= re
.compile(
125 r
"([^\s]*moz-phab(?:\.exe)?)$", re
.MULTILINE
128 if len(potential_cli_paths
) != 1:
131 "no_mozphab_console_script",
133 "Could not find the CLI script for moz-phab. Skipping install-certificate step.",
137 console_script
= os
.path
.realpath(
138 os
.path
.join(mozphab_package_location
, potential_cli_paths
[0])
140 subprocess
.run([console_script
, "install-certificate"])