tor-browser

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

run_operations.py (5750B)


      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 import os
      5 import platform
      6 import subprocess
      7 import sys
      8 from enum import Enum, auto
      9 
     10 # This is a collection of helper functions that use the subprocess.run
     11 # command to execute commands.  In the future, if we have cases where
     12 # we find common python functionality in utilizing the data returned
     13 # from these functions, that functionality can live here for easy reuse
     14 # between other python scripts in dom/media/webrtc/third_party_build.
     15 
     16 
     17 # must run 'hg' with HGPLAIN=1 to ensure aliases don't interfere with
     18 # command output.
     19 env = os.environ.copy()
     20 env["HGPLAIN"] = "1"
     21 
     22 
     23 def run_hg(cmd):
     24    cmd_list = cmd.split(" ")
     25    res = subprocess.run(
     26        cmd_list,
     27        capture_output=True,
     28        text=True,
     29        env=env,
     30        check=False,
     31    )
     32    if res.returncode != 0:
     33        print(
     34            f"Hit return code {res.returncode} running '{cmd}'. Aborting.",
     35            file=sys.stderr,
     36        )
     37        print(res.stderr)
     38        sys.exit(1)
     39    stdout = res.stdout.strip()
     40    return [] if len(stdout) == 0 else stdout.split("\n")
     41 
     42 
     43 def run_git(cmd, working_dir):
     44    cmd_list = cmd.split(" ")
     45    res = subprocess.run(
     46        cmd_list,
     47        capture_output=True,
     48        text=True,
     49        cwd=working_dir,
     50        check=False,
     51    )
     52    if res.returncode != 0:
     53        print(
     54            f"Hit return code {res.returncode} running '{cmd}'. Aborting.",
     55            file=sys.stderr,
     56        )
     57        print(res.stderr)
     58        sys.exit(1)
     59    stdout = res.stdout.strip()
     60    return [] if len(stdout) == 0 else stdout.split("\n")
     61 
     62 
     63 def run_shell(cmd, capture_output=True):
     64    res = subprocess.run(
     65        cmd,
     66        shell=True,
     67        capture_output=capture_output,
     68        text=True,
     69        check=False,
     70    )
     71    if res.returncode != 0:
     72        print(
     73            f"Hit return code {res.returncode} running '{cmd}'. Aborting.",
     74            file=sys.stderr,
     75        )
     76        if capture_output:
     77            print(res.stderr)
     78        sys.exit(1)
     79    output_lines = []
     80    if capture_output:
     81        stdout = res.stdout.strip()
     82        output_lines = [] if len(stdout) == 0 else stdout.split("\n")
     83 
     84    return output_lines
     85 
     86 
     87 def get_last_line(file_path):
     88    # technique from https://stackoverflow.com/questions/46258499/how-to-read-the-last-line-of-a-file-in-python
     89    with open(file_path, "rb") as f:
     90        try:  # catch OSError in case of a one line file
     91            f.seek(-2, os.SEEK_END)
     92            while f.read(1) != b"\n":
     93                f.seek(-2, os.SEEK_CUR)
     94        except OSError:
     95            f.seek(0)
     96        return f.readline().decode().strip()
     97 
     98 
     99 def update_resume_state(state, resume_state_filename):
    100    with open(resume_state_filename, "w") as ofile:
    101        ofile.write(state)
    102        ofile.write("\n")
    103 
    104 
    105 class ErrorHelp:
    106    def __init__(self):
    107        self.prefix = None
    108        self.error_help = None
    109        self.postfix = None
    110 
    111    def set_prefix(self, prefix):
    112        self.prefix = prefix
    113 
    114    def set_postfix(self, postfix):
    115        self.postfix = postfix
    116 
    117    def set_help(self, error_help):
    118        self.error_help = error_help
    119 
    120    def print_help(self):
    121        if self.prefix is not None:
    122            print(self.prefix)
    123        if self.error_help is not None:
    124            print(self.error_help)
    125        if self.postfix is not None:
    126            print(self.postfix)
    127 
    128 
    129 class RepoType(Enum):
    130    HG = auto()
    131    GIT = auto()
    132 
    133 
    134 def detect_repo_type():
    135    if os.path.exists(".git"):
    136        return RepoType.GIT
    137    elif os.path.exists(".hg"):
    138        return RepoType.HG
    139    return None
    140 
    141 
    142 def git_status(working_dir, search_path="."):
    143    # We don't use run_git above because it strips all leading
    144    # whitespace, which causes issues with the output of
    145    # git status --porcelain since leading spaces matter in the output.
    146    # For example, the following two lines mean different things:
    147    #  M file1.txt
    148    # M  file2.txt
    149    # file2.txt is staged, while file1.txt is modified, but unstaged.
    150    cmd = f"git --no-optional-locks status --porcelain {search_path}"
    151    cmd_list = cmd.split(" ")
    152    res = subprocess.run(
    153        cmd_list,
    154        capture_output=True,
    155        text=True,
    156        cwd=working_dir,
    157        check=False,
    158    )
    159    if res.returncode != 0:
    160        print(
    161            f"Hit return code {res.returncode} running '{cmd}'. Aborting.",
    162            file=sys.stderr,
    163        )
    164        print(res.stderr)
    165        sys.exit(1)
    166    stdout = res.stdout.strip("\n")
    167    return [] if len(stdout) == 0 else stdout.split("\n")
    168 
    169 
    170 def check_repo_status(repo_type):
    171    if not isinstance(repo_type, RepoType):
    172        print("check_repo_status requires type RepoType")
    173        raise TypeError
    174    if repo_type == RepoType.GIT:
    175        return git_status(".", "third_party/libwebrtc")
    176    else:
    177        return run_hg("hg status third_party/libwebrtc")
    178 
    179 
    180 def is_mac_os():
    181    return platform.system() == "Darwin"
    182 
    183 
    184 def git_get_config(config_name, working_dir):
    185    # using run_shell rather than run_git because a config name that is
    186    # unset returns a non-zero error code.
    187    stdout_lines = run_shell(f"cd {working_dir} ; git config {config_name} || true")
    188    line_cnt = len(stdout_lines)
    189    if line_cnt > 1:
    190        print(f"Error: {config_name} returned multiple values ({line_cnt})")
    191        sys.exit(1)
    192    elif line_cnt == 1:
    193        return stdout_lines[0]
    194    return None
    195 
    196 
    197 def git_is_config_set(config_name, working_dir):
    198    config_val = git_get_config(config_name, working_dir)
    199    if config_val is not None and config_val == "true":
    200        return True
    201    return False