Source code for duck.cli.commands.sync

"""
Module containing the SyncCommand class for Duck project dependency sync.
"""
import shlex
import click

from pathlib import Path

from duck.logging import console
from duck.version import python_version
from duck.cli.commands.sync.exceptions import DuckSyncError
from duck.cli.commands.sync.sync import DuckSync, SyncReport
from duck.cli.commands.sync.platform_utils import (
    detect_os_family,
    is_termux,
    is_root,
)


[docs] class SyncCommand: """ CLI command group for syncing project dependencies from duck.toml. """
[docs] @classmethod def print_header( cls, distro: str, package_manager: str, py_package_manager: str, config: str, is_root: bool, py_version: str, ) -> None: """ Prints the detected system environment and selected backend. Args: distro: Detected operating system distribution. package_manager: Package manager backend selected by Duck Sync. py_package_manager: Python package manager backend selected by Duck Sync. config: The config path or name relative to cwd. is_root: Whether we are in root environment. py_version: The python version. """ console.log(f" Python version: {py_version}", level=console.DEBUG) console.log(f" Environment: {distro}", level=console.DEBUG) console.log(f" Root: {is_root}", level=console.DEBUG) console.log(f" Backend: {package_manager}", level=console.DEBUG) console.log(f" Python backend: {py_package_manager}", level=console.DEBUG) console.log(f" Configuration: {config}\n", level=console.DEBUG)
[docs] @classmethod def print_report(cls, report: SyncReport): """ Prints a sync run summary to the console. Args: report: The report returned by DuckSync.run(). """ total = ( len(report.already_satisfied) + len(report.installed) + len(report.failed) ) console.log_raw( f"Sync complete: {total} dependencies checked", custom_color=console.Fore.CYAN, ) if report.already_satisfied: console.log_raw( f"\nAlready satisfied ({len(report.already_satisfied)}):", level=console.DEBUG, ) for package in report.already_satisfied: console.log_raw( f" ✓ {package}", custom_color=console.Fore.GREEN, ) if report.installed: console.log_raw( f"\nInstalled ({len(report.installed)}):", level=console.DEBUG, ) for package in report.installed: console.log_raw( f" + {package}", custom_color=console.Fore.GREEN, ) if report.failed: console.log_raw( f"\nFailed ({len(report.failed)}):", level=console.ERROR, ) for package, reason in report.failed.items(): console.log_raw( f" ✗ {package}: {reason}", level=console.ERROR, ) if report.ok(): console.log_raw( "\n✓ All dependencies are synced.", custom_color=console.Fore.GREEN, ) else: console.log_raw( "\n✗ Sync completed with errors.", level=console.ERROR, )
[docs] @classmethod def main( cls, include_dev: bool = False, dev_only: bool = False, dry_run: bool = False, config: str = "", pip_args: str = "", system_args: str = "", sudo: bool | None = None, backend: str = "", ): """ Run a full dependency sync using duck.toml. Args: include_dev: Whether to also sync the [development] group. dev_only: Whether to also sync the [development] group. dry_run: Print planned installs without changing the system. config: Explicit path to duck.toml, auto discovered when empty. pip_args: Extra args appended to every pip/uv install, e.g. "--extra-index-url https://download.pytorch.org/whl/cu118". system_args: Extra args appended to every system package install, e.g. "--no-install-recommends". sudo: Force sudo on or off for system installs. Left as None, duck.toml's [sync] setting is used, falling back to auto-detect (skip sudo when already root). backend: Override the auto-detected system package manager. Accepts a known identifier (``apt``, ``brew``, ``pacman`` …) or a raw install command such as ``"some_command -i"``. Left empty, the backend is detected from the host platform. """ from duck.cli.commands.sync.config import CONFIG_FILENAME config_path = Path(config) if config else None try: duck_sync = DuckSync( config_path=config_path, include_dev=include_dev, dev_only=dev_only, dry_run=dry_run, python_extra_args=shlex.split(pip_args), system_extra_args=shlex.split(system_args), use_sudo=sudo, force_backend=backend or None, ) # Print header first distro = detect_os_family(raise_for_unknown=False) if is_termux(): distro = f"{distro} [Termux]".capitalize() cls.print_header( distro=distro, package_manager=duck_sync.system_backend.base_command[0], py_package_manager=duck_sync.python_backend.base_command[0], config=config_path or CONFIG_FILENAME, is_root=is_root(), py_version=python_version, ) # Generate report report = duck_sync.run() except DuckSyncError as error: console.log(f"duck sync failed: {error}", level=console.ERROR) console.log_exception(error) return cls.print_report(report) if not report.ok(): console.log_raw("One or more dependencies failed.", level=console.WARNING)