Применял для резервного копирования пакетов, из-за нестабильной работы интернета при получении данных с зарубежных сервисов из-за кривых фильтров РосКомПозора (РКН).

#!/usr/bin/env python3

import subprocess
from pathlib import Path
from typing import Optional, Tuple, Dict
from datetime import datetime
import tarfile
import os

def run_subprocess(cmd: list[str], cwd: Path, env_vars: Optional[Dict[str, str]] = None) -> Tuple[bool, str]:
    """
    Запуск subprocess с возвратом результата.
    """
    # Создаем копию текущих переменных окружения и добавляем/обновляем переданные
    full_env = os.environ.copy()
    if env_vars:
        full_env.update(env_vars)

    try:
        result = subprocess.run(
            cmd,
            cwd=str(cwd),
            check=True,
            capture_output=True,
            text=True,
            env=full_env # Передаем сформированные переменные окружения
        )
        return True, result.stdout
    except subprocess.CalledProcessError as e:
        return False, e.stderr or str(e)

def install_or_update_packages(
    package_list: tuple[str, ...],
    composer_cmd: str = "composer",
    cwd: Optional[Path | str] = None,
    update_flags: tuple[str, ...] = ("update", "--prefer-dist", "--no-interaction", "--quiet")
) -> Tuple[bool, str]:
    """
    Если composer.json существует, выполнить composer update с флагами.
    Если нет — выполнить composer require для указанных пакетов.
    """
    cwd = Path(cwd) if cwd else Path.cwd()
    composer_json = cwd / "composer.json"

    # Определяем переменные окружения для Composer
    composer_env = {"COMPOSER_ALLOW_SUPERUSER": "1"}

    if composer_json.exists():
        # composer.json есть — обновляем
        update_cmd = (composer_cmd,) + update_flags
        return run_subprocess(list(update_cmd), cwd, env_vars=composer_env)
    else:
        # composer.json нет — устанавливаем
        require_cmd = [composer_cmd, "require", *package_list]
        return run_subprocess(require_cmd, cwd, env_vars=composer_env)

def gzip_directory_with_date(source_dir: Path, output_dir: Path) -> Path:
    """
    Упаковывает содержимое source_dir в tar.gz архив с датой в имени.
    """
    date_str = datetime.now().strftime("%Y-%m-%d")
    archive_name = f"composer_packages_dump_{date_str}.tar.gz"
    archive_path = output_dir / archive_name
    with tarfile.open(archive_path, "w:gz") as tar:
        # Убедимся, что добавляем только содержимое, а не сам каталог
        # Переходим в source_dir, чтобы tar.add использовал относительные пути
        # иначе в архиве будет абсолютный путь /var/www/composer_dump/...
        original_cwd = os.getcwd() # Сохраняем текущий рабочий каталог
        os.chdir(source_dir)
        try:
            for item in source_dir.iterdir():
                tar.add(item.name, arcname=item.name) # Добавляем только имя файла/папки
        finally:
            os.chdir(original_cwd) # Возвращаемся в исходный рабочий каталог
    return archive_path

if __name__ == "__main__":
    PACKAGES = (
        "twig/twig",
        "nesbot/carbon",
        "phpmailer/phpmailer",
        "mpdf/mpdf",
        "mjaschen/phpgeo",
        "chillerlan/php-qrcode",
        "symfony/routing",
        "guzzlehttp/guzzle",
        "doctrine/orm",
        "doctrine/dbal",
        "monolog/monolog",
        "vlucas/phpdotenv",
        "ramsey/uuid",
        "firebase/php-jwt",
        "phpunit/phpunit",
        "symfony/http-foundation",
        "filp/whoops",
        "nikic/php-parser",
	"symfony/cache",
	"symfony/validator",
	"symfony/serializer",
        "phpstan/phpstan",
        "league/flysystem",
        "psr/log",
        "league/commonmark"
    )

    working_directory = Path("/var/www/composer_dump")
    output_directory = Path("/var/www/html/uploads/composer")

    # Убедимся, что рабочая директория существует
    working_directory.mkdir(parents=True, exist_ok=True)
    output_directory.mkdir(parents=True, exist_ok=True)

    success, message = install_or_update_packages(PACKAGES, cwd=working_directory)
    if success:
        print("Операция с пакетами Composer прошла успешно.")
        print(message)
        try:
            archive_path = gzip_directory_with_date(working_directory, output_directory)
            print(f"Архив создан по адресу: {archive_path}")
        except Exception as e:
            print(f"Ошибка при создании архива: {e}")
    else:
        print("Ошибка при работе с пакетами Composer:")
        print(message)

Комментарии (1)


  1. MadridianFox
    09.10.2025 05:48

    Мало подробностей. Выглядит как велосипед, который сделан от незнания уже существующих инструментов.