Применял для резервного копирования пакетов, из-за нестабильной работы интернета при получении данных с зарубежных сервисов из-за кривых фильтров РосКомПозора (РКН).
#!/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)
MadridianFox
Мало подробностей. Выглядит как велосипед, который сделан от незнания уже существующих инструментов.