Source code for stouputils.installer.linux
""" Installer module for Linux/macOS specific functions.
Provides Linux/macOS specific implementations for checking admin privileges,
determining appropriate installation paths (global/local), and suggesting
how to add directories to the system's PATH environment variable.
"""
# Imports
import os
from ..decorators import LogLevels, handle_error
from ..io import clean_path
from ..print import debug, info, warning
from .common import ask_install_type, prompt_for_path
# Functions
[docs]
@handle_error(message="Failed to suggest how to add to PATH (Linux)", error_log=LogLevels.WARNING_TRACEBACK)
def add_to_path_linux(install_path: str) -> bool:
""" Suggest how to add install_path to PATH on Linux.
Checks the current shell and provides instructions for adding the path
to the appropriate configuration file (e.g., .bashrc, .zshrc, config.fish).
Args:
install_path (str): The path to add to the PATH environment variable.
Returns:
bool: True if instructions were provided, False otherwise (e.g., unknown shell).
"""
shell_config_files: dict[str, str] = {
"bash": "~/.bashrc",
"zsh": "~/.zshrc",
"fish": "~/.config/fish/config.fish"
}
current_shell: str = os.environ.get("SHELL", "").split('/')[-1]
config_file: str | None = shell_config_files.get(current_shell)
if config_file:
export_cmd: str = ""
if current_shell == "fish":
export_cmd = f"set -gx PATH $PATH {install_path}"
else:
export_cmd = f"export PATH=\"$PATH:{install_path}\"" # Escape quotes for print
debug(
f"To add the installation directory to your PATH, add the following line to your '{config_file}':\n"
f" {export_cmd}\n"
f"Then restart your shell or run 'source {config_file}'."
)
return True
else:
warning(f"Could not determine your shell configuration file. Please add '{install_path}' to your PATH manually.")
return False
[docs]
def check_admin_linux() -> bool:
""" Check if the script is running with root privileges on Linux/macOS.
Returns:
bool: True if the effective user ID is 0 (root), False otherwise.
"""
try:
return os.geteuid() == 0 # type: ignore
except AttributeError as e:
# os.geteuid() is not available on all platforms (e.g., Windows)
# This function should ideally only be called on Linux/macOS.
warning(f"Could not determine user privileges on this platform: {e}")
return False
except Exception as e:
warning(f"Error checking admin privileges: {e}")
return False
[docs]
@handle_error(message="Failed to get installation path (Linux)", error_log=LogLevels.ERROR_TRACEBACK)
def get_install_path_linux(
program_name: str,
ask_global: int = 0,
add_path: bool = True,
append_to_path: str = "",
default_global: str = "/usr/local/bin",
) -> str:
""" Get the installation path for the program on Linux/macOS.
Args:
program_name (str): The name of the program to install.
ask_global (int): 0 = ask for anything, 1 = install globally, 2 = install locally
add_path (bool): Whether to add the program to the PATH environment variable. (Only if installed globally)
append_to_path (str): String to append to the installation path when adding to PATH.
(ex: "bin" if executables are in the bin folder)
default_global (str): The default global installation path.
(Default is "/usr/local/bin" which is the most common location for executables on Linux/macOS,
could be "/opt" or any other directory)
Returns:
str: The chosen installation path, or an empty string if installation is cancelled.
"""
# Default paths
default_local_path: str = clean_path(os.path.join(os.getcwd(), program_name))
# Common global locations: /usr/local/bin for executables, /opt/ for self-contained apps
# We assume 'program_name' might be an executable or a directory, /usr/local/ is safer
default_global_path: str = clean_path(f"{default_global}/{program_name}") # Or potentially /opt/{program_name}
# Ask user for installation type (global/local)
install_type: str = ask_install_type(ask_global, default_local_path, default_global_path)
# Handle global installation choice
if install_type == 'g':
if not check_admin_linux():
warning(
f"Global installation typically requires sudo privileges to write to "
f"'{os.path.dirname(default_global_path)}'.\n"
f"You may need to re-run the script with 'sudo'.\n"
f"Install locally instead to '{default_local_path}'? (Y/n): "
)
if input().lower() == 'n':
info("Installation cancelled.")
return ""
else:
# Fallback to local path if user agrees
return prompt_for_path(
f"Falling back to local installation path: {default_local_path}.",
default_local_path
)
else:
# User is admin or proceeding with global install anyway
install_path: str = prompt_for_path(
f"Default global installation path is {default_global_path}.",
default_global_path
)
if add_path:
# Suggest adding the *directory* containing the program to PATH,
# or the path itself if it seems like a directory install
path_to_add: str = os.path.dirname(install_path) if os.path.isfile(install_path) else install_path
add_to_path_linux(os.path.join(path_to_add, append_to_path))
return install_path
# Handle local installation choice
else: # install_type == 'l'
return prompt_for_path(
f"Default local installation path is {default_local_path}.",
default_local_path
)