feat: update version and dependencies in setup.py; add privilege management and process handling modules

- Updated version from 2026.05.31 to 2026.06.11 in setup.py
- Replaced 'textual' with 'rich' in install_requires
- Added privilege.py for managing sudo privileges with a keep-alive mechanism
- Introduced process.py for handling subprocesses with enhanced control and output streaming
- Created ui.py for unified console interactions and user prompts
This commit is contained in:
Даниил Грабарь
2026-06-11 12:21:02 +10:00
parent e76a07c8f6
commit ee047618cd
16 changed files with 1877 additions and 2578 deletions
+28 -77
View File
@@ -3,11 +3,10 @@ from __future__ import annotations
import argparse
import shutil
from pathlib import Path
from typing import List, Optional
from typing import List
from textual.app import App
from cobot.tui import SCREEN_CSS, LogScreen, MultiPickScreen
from cobot import ui
from cobot.ui import header, done
_PROJECT_DIR = Path(__file__).parent.parent.parent
@@ -19,80 +18,21 @@ _DIR_MAP = {
}
def _task_clean(screen: LogScreen, dirs: List[str]) -> None:
"""Delete the selected top-level directories.
Удаляет выбранные директории верхнего уровня.
def _clean(dirs: List[str]) -> None:
"""Delete the selected top-level directories, printing the outcome of each.
Удаляет выбранные директории верхнего уровня, печатая результат по каждой.
"""
try:
screen.write("[bold]Cleaning build artifacts[/bold]\n")
total = len(dirs)
for i, label in enumerate(dirs):
if screen.is_stopped():
return
screen.set_progress(i / total * 100, f"Removing {label}...")
path = _DIR_MAP[label]
if path.exists():
shutil.rmtree(path)
screen.write(f"[green][ok][/green] Removed {label}")
else:
screen.write(f"[dim]Not found: {label}[/dim]")
if not screen.is_stopped():
screen.set_progress(100, "Done")
screen.write("\n[green]Done.[/green]")
screen.finish(True)
except Exception as exc:
if not screen.is_stopped():
screen.write(f"\n[red]Error:[/red] {exc}")
screen.finish(False)
class _CleanApp(App[None]):
"""Clean wizard: lets the user pick which directories to delete, then removes them.
Мастер очистки: позволяет выбрать директории для удаления, затем удаляет их.
"""
CSS = SCREEN_CSS
def __init__(self, all_dirs: bool):
super().__init__()
# True = skip the question and delete everything right away.
# True = пропустить вопрос и сразу удалить всё.
self._all_dirs = all_dirs
def on_mount(self) -> None:
if self._all_dirs:
self._start(_DIR_OPTIONS)
header("Очистка артефактов сборки")
removed = False
for label in dirs:
path = _DIR_MAP[label]
if path.exists():
shutil.rmtree(path)
ui.info(f" [green]✓[/green] Удалено {label}")
removed = True
else:
self._ask_dirs()
def _ask_dirs(self) -> None:
self.push_screen(
MultiPickScreen(
"clean",
"Which directories to delete?",
_DIR_OPTIONS,
note="Space — toggle · Enter — confirm",
),
self._got_dirs,
)
def _got_dirs(self, dirs: Optional[List[str]]) -> None:
if not dirs:
self.exit()
return
self._start(dirs)
def _start(self, dirs: List[str]) -> None:
self.push_screen(
LogScreen(
"Cleaning",
lambda s: _task_clean(s, dirs),
show_progress=True,
),
lambda _: self.exit(),
)
ui.info(f" [dim]Нет:[/dim] {label}")
done(True, "Очищено" if removed else "Нечего удалять")
def register(subparsers: argparse._SubParsersAction) -> None:
@@ -114,4 +54,15 @@ def run(args: argparse.Namespace) -> None:
"""Entry point for the clean command.
Точка входа для команды clean.
"""
_CleanApp(all_dirs=(getattr(args, "target", None) == "all")).run()
if getattr(args, "target", None) == "all":
_clean(_DIR_OPTIONS)
return
dirs = ui.multiselect(
"Какие директории удалить?",
_DIR_OPTIONS,
note="Space — отметить · Enter — подтвердить",
)
if not dirs:
return
_clean(dirs)