# Copyright 2023 Oliver Smith # SPDX-License-Identifier: GPL-3.0-or-later # PYTHON_ARGCOMPLETE_OK import sys import os import traceback from typing import TYPE_CHECKING from pathlib import Path from pmb.helpers.exceptions import BuildFailedError, NonBugError if TYPE_CHECKING: from pmb.types import PmbArgs from . import config from . import parse from .config import init as config_init, require_programs from .helpers import logging from .helpers import mount from .helpers import other from .helpers import status from .core import Chroot, Config from .core.context import get_context from .commands import run_command # pmbootstrap version __version__ = "3.5.2" # Python version check # === CHECKLIST FOR UPGRADING THE REQUIRED PYTHON VERSION === # * .ci/vermin.sh # * README.md # * docs/usage.rst # * pmb/__init__.py (you are here) # * pyproject.toml # * when upgrading to python 3.11: pmb/helpers/toml.py and remove this line version = sys.version_info if version < (3, 10): print("You need at least Python 3.10 to run pmbootstrap") print("(You are running it with Python " + str(version.major) + "." + str(version.minor) + ")") sys.exit() def print_log_hint() -> None: context = get_context(allow_failure=True) if context and context.details_to_stdout: return log = context.log if context else Config().work / "log.txt" # Hints about the log file (print to stdout only) log_hint = "Run 'pmbootstrap log' for details." if not os.path.exists(log): log_hint += ( " Alternatively you can use '--details-to-stdout' to get more" " output, e.g. 'pmbootstrap --details-to-stdout init'." ) print() print(log_hint) def main() -> int: # Wrap everything to display nice error messages args: PmbArgs try: # Parse arguments, set up logging args = parse.arguments() context = get_context() os.umask(0o22) # Store script invocation command os.environ["PMBOOTSTRAP_CMD"] = sys.argv[0] # Sanity checks other.check_grsec() if not args.as_root and os.geteuid() == 0: raise RuntimeError("Do not run pmbootstrap as root!") # Check for required programs (and find their absolute paths) require_programs() # Initialize or require config if args.action == "init": config_init.frontend(args) return 0 elif not os.path.exists(args.config): if args.config != config.defaults["config"]: raise NonBugError(f"Couldn't find file passed with --config: {args.config}") old_config = ( Path(os.environ.get("XDG_CONFIG_HOME") or os.path.expanduser("~/.config")) / "pmbootstrap.cfg" ) if os.path.exists(old_config): raise NonBugError( f"Thanks for upgrading to pmbootstrap {__version__}!" " The config file format has changed, please generate a new config with" " 'pmbootstrap init'." ) raise NonBugError( "Run 'pmbootstrap init' first to generate a config file (or use --config)." ) elif not os.path.exists(context.config.work): raise NonBugError("Work path not found, please run 'pmbootstrap init' to create it.") # Migrate work folder if necessary if args.action not in ["shutdown", "zap", "log"]: other.migrate_work_folder() # Run the function with the action's name (in pmb/helpers/frontend.py) if args.action: run_command(args) else: logging.info("Run pmbootstrap -h for usage information.") # Still active notice if mount.ismount(Chroot.native() / "dev"): logging.info("NOTE: chroot is still active (use 'pmbootstrap shutdown' as necessary)") logging.info("DONE!") except KeyboardInterrupt: print("\nCaught KeyboardInterrupt, exiting …") sys.exit(130) # SIGINT(2) + 128 except NonBugError as exception: logging.error(f"ERROR: {exception}") return 2 except BuildFailedError as exception: logging.error(f"ERROR: {exception}") print_log_hint() return 3 except Exception as e: # Dump log to stdout when args (and therefore logging) init failed can_print_status = get_context(allow_failure=True) is not None if "args" not in locals(): import logging as pylogging pylogging.getLogger().setLevel(logging.DEBUG) can_print_status = False logging.info("ERROR: " + str(e)) logging.info("See also: ") logging.debug(traceback.format_exc()) print_log_hint() print() print("Before you report this error, ensure that pmbootstrap is up to date.") print( "Find the latest version here: https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/tags" ) print(f"Your version: {__version__}") if can_print_status: status.print_status() return 1 return 0 if __name__ == "__main__": sys.exit(main())