From cd6565dc088b56efc6ad5bf76554960f1b1d4da0 Mon Sep 17 00:00:00 2001 From: Softer Date: Sun, 3 May 2026 05:11:09 +0300 Subject: [PATCH] Rework share-log per review: subcommand, TUI confirmation, truncate large logs --- archinstall/lib/args.py | 8 ---- archinstall/lib/output.py | 90 ++++++++++++++++++++++++++++++++++++ archinstall/lib/share_log.py | 71 ---------------------------- archinstall/main.py | 11 ++--- 4 files changed, 95 insertions(+), 85 deletions(-) delete mode 100644 archinstall/lib/share_log.py diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index a09f805d..8353ce9e 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -55,7 +55,6 @@ class Arguments: skip_wifi_check: bool = False advanced: bool = False verbose: bool = False - share_log: bool = False @dataclass @@ -432,13 +431,6 @@ class ArchConfigHandler: default=False, help='Enabled verbose options', ) - parser.add_argument( - '--share-log', - action='store_true', - default=False, - help='Upload /var/log/archinstall/install.log to paste.rs and print the URL, then exit', - ) - return parser def _parse_args(self) -> Arguments: diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index e01da3eb..8f6018ec 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -333,3 +333,93 @@ def log( if level != logging.DEBUG: print(text) + + +_PASTE_URL = 'https://paste.rs' +_PASTE_MAX_SIZE = 10 * 1024 * 1024 + + +def share_install_log() -> int: + from archinstall.lib.command import SysCommand + from archinstall.lib.exceptions import SysCallError + + log_path = logger.path + + if not log_path.exists(): + info(f'Log file not found: {log_path}') + return 1 + + size = log_path.stat().st_size + if size == 0: + info(f'Log file is empty: {log_path}') + return 1 + + if size > _PASTE_MAX_SIZE: + info(f'Log file exceeds {_PASTE_MAX_SIZE} bytes, uploading last {_PASTE_MAX_SIZE} bytes') + content = log_path.read_bytes()[-_PASTE_MAX_SIZE:] + else: + content = log_path.read_bytes() + + header = f'About to upload {log_path} ({len(content)} bytes) to {_PASTE_URL}\n\n' + header += 'The log may contain hostname, mirror URLs, package list and partition layout.\n' + header += 'The uploaded paste is public.\n\n' + header += 'Continue?' + + try: + from archinstall.tui.ui.components import tui + + confirmed: bool = tui.run(lambda: _confirm_share(header)) + except Exception: + confirmed = False + + if not confirmed: + info('Cancelled.') + return 1 + + import tempfile + + if size > _PASTE_MAX_SIZE: + fd, tmp_path_str = tempfile.mkstemp(suffix='.log') + try: + with os.fdopen(fd, 'wb') as f: + f.write(content) + upload_path = tmp_path_str + except Exception: + os.close(fd) + raise + else: + upload_path = str(log_path) + tmp_path_str = None + + try: + result = SysCommand(f'curl -sS --data-binary @{upload_path} {_PASTE_URL}') + except SysCallError as e: + info(f'Upload failed: {e}') + return 1 + finally: + if tmp_path_str: + Path(tmp_path_str).unlink(missing_ok=True) + + url = result.decode().strip() + + if not url.startswith('http'): + info(f'Unexpected response from {_PASTE_URL}: {url[:200]!r}') + return 1 + + # raw print so the URL is pipe-friendly (no ANSI colors, no log prefix) + print(url) + return 0 + + +async def _confirm_share(header: str) -> bool: + from archinstall.lib.menu.helpers import Confirmation + from archinstall.tui.ui.menu_item import MenuItemGroup + + result = await Confirmation( + group=MenuItemGroup.yes_no(), + header=header, + allow_skip=False, + preset=False, + ).show() + + return result.get_value() diff --git a/archinstall/lib/share_log.py b/archinstall/lib/share_log.py deleted file mode 100644 index 8af659b1..00000000 --- a/archinstall/lib/share_log.py +++ /dev/null @@ -1,71 +0,0 @@ -import sys - -from archinstall.lib.command import SysCommand -from archinstall.lib.exceptions import SysCallError -from archinstall.lib.output import logger - -# paste.rs is a minimal text pastebin with syntax highlighting by extension. -# 10 MiB is its documented upload limit. -_PASTE_URL = 'https://paste.rs' -_PASTE_MAX_SIZE = 10 * 1024 * 1024 - - -def share_install_log() -> int: - """Upload /var/log/archinstall/install.log to paste.rs and print the URL. - - Intended for users to paste the URL into a GitHub issue when reporting a - bug. Always asks for explicit confirmation - the log may contain hostname, - mirror URLs, package list, partition layout and other system details which - become public on upload. - - All diagnostic output goes to stderr instead of the standard log helpers, - so the file we are about to upload is not modified by this command. - """ - log_path = logger.path - - if not log_path.exists(): - print(f'Log file not found: {log_path}', file=sys.stderr) - return 1 - - size = log_path.stat().st_size - if size == 0: - print(f'Log file is empty: {log_path}', file=sys.stderr) - return 1 - - if size > _PASTE_MAX_SIZE: - print( - f'Log file is too large to share: {size} bytes (limit: {_PASTE_MAX_SIZE} bytes). Trim it or upload manually.', - file=sys.stderr, - ) - return 1 - - print(f'About to upload {log_path} ({size} bytes) to {_PASTE_URL}', file=sys.stderr) - print( - 'The log may contain hostname, mirror URLs, package list and partition layout. The uploaded paste is public.', - file=sys.stderr, - ) - - try: - answer = input('Continue? [y/N]: ').strip().lower() - except EOFError, KeyboardInterrupt: - print(file=sys.stderr) - return 1 - - if answer not in ('y', 'yes'): - print('Cancelled.', file=sys.stderr) - return 1 - - try: - result = SysCommand(f'curl -sS --data-binary @{log_path} {_PASTE_URL}') - except SysCallError as e: - print(f'Upload failed: {e}', file=sys.stderr) - return 1 - - url = result.decode().strip() - - if not url.startswith('http'): - print(f'Unexpected response from {_PASTE_URL}: {url[:200]!r}', file=sys.stderr) - return 1 - - print(url) - return 0 diff --git a/archinstall/main.py b/archinstall/main.py index f7a6e4a5..9c30e63c 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -13,10 +13,9 @@ from archinstall.lib.disk.utils import disk_layouts from archinstall.lib.hardware import SysInfo from archinstall.lib.network.wifi_handler import WifiHandler from archinstall.lib.networking import ping -from archinstall.lib.output import debug, error, info, warn +from archinstall.lib.output import debug, error, info, share_install_log, warn from archinstall.lib.packages.util import check_version_upgrade from archinstall.lib.pacman.pacman import Pacman -from archinstall.lib.share_log import share_install_log from archinstall.lib.translationhandler import tr, translation_handler from archinstall.lib.utils.util import running_from_iso from archinstall.tui.ui.components import tui @@ -80,6 +79,9 @@ def run() -> int: OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ + if 'share-log' in sys.argv: + return share_install_log() + arch_config_handler = ArchConfigHandler() if '--help' in sys.argv or '-h' in sys.argv: @@ -96,9 +98,6 @@ def run() -> int: print(tr('Archinstall requires root privileges to run. See --help for more.')) return 1 - if arch_config_handler.args.share_log: - return share_install_log() - translation_handler.save_console_font() _log_sys_info() @@ -146,7 +145,7 @@ def _error_message(exc: Exception) -> None: https://github.com/archlinux/archinstall and include the log file "/var/log/archinstall/install.log". Hint: To upload the log and get a shareable URL, run - archinstall --share-log + archinstall share-log """ ) warn(text)