chore(lint): apply ruff --fix (4443 auto-fixes)
Auto-fixed categories: - F401: unused imports removed - I001: import blocks sorted - W293: trailing whitespace stripped - UP035: deprecated typing imports replaced - SIM: simplify suggestions applied - ARG: unused args prefixed with underscore - T201: print statements removed - F841: unused variables removed - RUF059: unpacked variables prefixed Remaining ~1500 unfixable errors (C901, B904, N806, E402) require manual work. Backend smoke tests: 13/13 passed.
This commit is contained in:
@@ -11,25 +11,26 @@
|
||||
# @INVARIANT: Все операции с Git должны выполняться через GitService.
|
||||
# @CONSTRAINT: Плагин работает только с распакованными YAML-экспортами Superset.
|
||||
|
||||
import os
|
||||
import io
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
from src.core.plugin_base import PluginBase
|
||||
from src.services.git_service import GitService
|
||||
from src.core.logger import logger as app_logger, belief_scope
|
||||
from typing import Any
|
||||
|
||||
from src.core.config_manager import ConfigManager
|
||||
from src.core.logger import belief_scope
|
||||
from src.core.logger import logger as app_logger
|
||||
from src.core.plugin_base import PluginBase
|
||||
from src.core.superset_client import SupersetClient
|
||||
from src.core.task_manager.context import TaskContext
|
||||
from src.core.database import SessionLocal
|
||||
from src.core.mapping_service import IdMappingService
|
||||
from src.services.git_service import GitService
|
||||
|
||||
|
||||
# #region GitPlugin [TYPE Class]
|
||||
# @BRIEF Реализация плагина Git Integration для управления версиями дашбордов.
|
||||
class GitPlugin(PluginBase):
|
||||
|
||||
|
||||
# region __init__ [TYPE Function]
|
||||
# @PURPOSE: Инициализирует плагин и его зависимости.
|
||||
# @PRE: config.json exists or shared config_manager is available.
|
||||
@@ -38,7 +39,7 @@ class GitPlugin(PluginBase):
|
||||
with belief_scope("GitPlugin.__init__"):
|
||||
app_logger.info("Initializing GitPlugin.")
|
||||
self.git_service = GitService()
|
||||
|
||||
|
||||
# Robust config path resolution:
|
||||
# 1. Try absolute path from src/dependencies.py style if possible
|
||||
# 2. Try relative paths based on common execution patterns
|
||||
@@ -114,7 +115,7 @@ class GitPlugin(PluginBase):
|
||||
# @PRE: GitPlugin is initialized.
|
||||
# @POST: Returns a JSON schema dictionary.
|
||||
# @RETURN: Dict[str, Any] - Схема параметров.
|
||||
def get_schema(self) -> Dict[str, Any]:
|
||||
def get_schema(self) -> dict[str, Any]:
|
||||
with belief_scope("GitPlugin.get_schema"):
|
||||
return {
|
||||
"type": "object",
|
||||
@@ -145,20 +146,20 @@ class GitPlugin(PluginBase):
|
||||
# @RETURN: Dict[str, Any] - Статус и сообщение.
|
||||
# @RELATION: CALLS -> self._handle_sync
|
||||
# @RELATION: CALLS -> self._handle_deploy
|
||||
async def execute(self, task_data: Dict[str, Any], context: Optional[TaskContext] = None) -> Dict[str, Any]:
|
||||
async def execute(self, task_data: dict[str, Any], context: TaskContext | None = None) -> dict[str, Any]:
|
||||
with belief_scope("GitPlugin.execute"):
|
||||
operation = task_data.get("operation")
|
||||
dashboard_id = task_data.get("dashboard_id")
|
||||
|
||||
|
||||
# Use TaskContext logger if available, otherwise fall back to app_logger
|
||||
log = context.logger if context else app_logger
|
||||
|
||||
|
||||
# Create sub-loggers for different components
|
||||
git_log = log.with_source("git") if context else log
|
||||
superset_log = log.with_source("superset_api") if context else log
|
||||
|
||||
|
||||
log.info(f"Executing operation: {operation} for dashboard {dashboard_id}")
|
||||
|
||||
|
||||
if operation == "sync":
|
||||
source_env_id = task_data.get("source_env_id")
|
||||
result = await self._handle_sync(dashboard_id, source_env_id, log, git_log, superset_log)
|
||||
@@ -170,7 +171,7 @@ class GitPlugin(PluginBase):
|
||||
else:
|
||||
log.error(f"Unknown operation: {operation}")
|
||||
raise ValueError(f"Unknown operation: {operation}")
|
||||
|
||||
|
||||
log.info(f"Operation {operation} completed.")
|
||||
return result
|
||||
# endregion execute
|
||||
@@ -185,7 +186,7 @@ class GitPlugin(PluginBase):
|
||||
# @SIDE_EFFECT: Изменяет файлы в локальной рабочей директории репозитория.
|
||||
# @RELATION: CALLS -> src.services.git_service.GitService.get_repo
|
||||
# @RELATION: CALLS -> src.core.superset_client.SupersetClient.export_dashboard
|
||||
async def _handle_sync(self, dashboard_id: int, source_env_id: Optional[str] = None, log=None, git_log=None, superset_log=None) -> Dict[str, str]:
|
||||
async def _handle_sync(self, dashboard_id: int, source_env_id: str | None = None, log=None, git_log=None, superset_log=None) -> dict[str, str]:
|
||||
with belief_scope("GitPlugin._handle_sync"):
|
||||
try:
|
||||
# 1. Получение репозитория
|
||||
@@ -201,14 +202,14 @@ class GitPlugin(PluginBase):
|
||||
# 3. Экспорт дашборда
|
||||
superset_log.info(f"Exporting dashboard {dashboard_id} from {env.name}")
|
||||
zip_bytes, _ = client.export_dashboard(dashboard_id)
|
||||
|
||||
|
||||
# 4. Распаковка с выравниванием структуры (flattening)
|
||||
git_log.info(f"Unpacking export to {repo_path}")
|
||||
|
||||
|
||||
# Список папок/файлов, которые мы ожидаем от Superset
|
||||
managed_dirs = ["dashboards", "charts", "datasets", "databases"]
|
||||
managed_files = ["metadata.yaml"]
|
||||
|
||||
|
||||
# Очистка старых данных перед распаковкой, чтобы не оставалось "призраков"
|
||||
for d in managed_dirs:
|
||||
d_path = repo_path / d
|
||||
@@ -225,23 +226,23 @@ class GitPlugin(PluginBase):
|
||||
namelist = zf.namelist()
|
||||
if not namelist:
|
||||
raise ValueError("Export ZIP is empty")
|
||||
|
||||
|
||||
root_folder = namelist[0].split('/')[0]
|
||||
git_log.info(f"Detected root folder in ZIP: {root_folder}")
|
||||
|
||||
|
||||
for member in zf.infolist():
|
||||
if member.filename.startswith(root_folder + "/") and len(member.filename) > len(root_folder) + 1:
|
||||
# Убираем префикс папки
|
||||
relative_path = member.filename[len(root_folder)+1:]
|
||||
target_path = repo_path / relative_path
|
||||
|
||||
|
||||
if member.is_dir():
|
||||
target_path.mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zf.open(member) as source, open(target_path, "wb") as target:
|
||||
shutil.copyfileobj(source, target)
|
||||
|
||||
|
||||
# 5. Автоматический staging изменений (не коммит, чтобы юзер мог проверить diff)
|
||||
try:
|
||||
repo.git.add(A=True)
|
||||
@@ -269,7 +270,7 @@ class GitPlugin(PluginBase):
|
||||
# @RETURN: Dict[str, Any] - Результат деплоя.
|
||||
# @SIDE_EFFECT: Создает и удаляет временный ZIP-файл.
|
||||
# @RELATION: CALLS -> src.core.superset_client.SupersetClient.import_dashboard
|
||||
async def _handle_deploy(self, dashboard_id: int, env_id: str, log=None, git_log=None, superset_log=None) -> Dict[str, Any]:
|
||||
async def _handle_deploy(self, dashboard_id: int, env_id: str, log=None, git_log=None, superset_log=None) -> dict[str, Any]:
|
||||
with belief_scope("GitPlugin._handle_deploy"):
|
||||
try:
|
||||
if not env_id:
|
||||
@@ -282,10 +283,10 @@ class GitPlugin(PluginBase):
|
||||
# 2. Упаковка в ZIP
|
||||
git_log.info(f"Packing repository {repo_path} for deployment.")
|
||||
zip_buffer = io.BytesIO()
|
||||
|
||||
|
||||
# Superset expects a root directory in the ZIP (e.g., dashboard_export_20240101T000000/)
|
||||
root_dir_name = f"dashboard_export_{dashboard_id}"
|
||||
|
||||
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for root, dirs, files in os.walk(repo_path):
|
||||
if ".git" in dirs:
|
||||
@@ -297,14 +298,14 @@ class GitPlugin(PluginBase):
|
||||
# Prepend the root directory name to the archive path
|
||||
arcname = Path(root_dir_name) / file_path.relative_to(repo_path)
|
||||
zf.write(file_path, arcname)
|
||||
|
||||
|
||||
zip_buffer.seek(0)
|
||||
|
||||
|
||||
# 3. Настройка клиента Superset
|
||||
env = self.config_manager.get_environment(env_id)
|
||||
if not env:
|
||||
raise ValueError(f"Environment {env_id} not found")
|
||||
|
||||
|
||||
client = SupersetClient(env)
|
||||
client.authenticate()
|
||||
|
||||
@@ -313,7 +314,7 @@ class GitPlugin(PluginBase):
|
||||
git_log.info(f"Saving temporary zip to {temp_zip_path}")
|
||||
with open(temp_zip_path, "wb") as f:
|
||||
f.write(zip_buffer.getvalue())
|
||||
|
||||
|
||||
try:
|
||||
app_logger.reason(f"Importing dashboard to {env.name}", extra={"src": "_handle_deploy"})
|
||||
result = client.import_dashboard(temp_zip_path)
|
||||
@@ -334,21 +335,21 @@ class GitPlugin(PluginBase):
|
||||
# @PRE: env_id is a string or None.
|
||||
# @POST: Returns an Environment object from config or DB.
|
||||
# @RETURN: Environment - Объект конфигурации окружения.
|
||||
def _get_env(self, env_id: Optional[str] = None):
|
||||
def _get_env(self, env_id: str | None = None):
|
||||
with belief_scope("GitPlugin._get_env"):
|
||||
app_logger.reason(f"Fetching environment for ID: {env_id}", extra={"src": "_get_env"})
|
||||
|
||||
|
||||
# Priority 1: ConfigManager (config.json)
|
||||
if env_id:
|
||||
env = self.config_manager.get_environment(env_id)
|
||||
if env:
|
||||
app_logger.reflect(f"Found environment by ID in ConfigManager: {env.name}", extra={"src": "_get_env"})
|
||||
return env
|
||||
|
||||
|
||||
# Priority 2: Database (DeploymentEnvironment)
|
||||
from src.core.database import SessionLocal
|
||||
from src.models.git import DeploymentEnvironment
|
||||
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
if env_id:
|
||||
@@ -373,7 +374,7 @@ class GitPlugin(PluginBase):
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# Priority 3: ConfigManager Default (if no env_id provided)
|
||||
envs = self.config_manager.get_environments()
|
||||
if envs:
|
||||
@@ -384,15 +385,15 @@ class GitPlugin(PluginBase):
|
||||
if env:
|
||||
app_logger.reflect(f"Found environment {env_id} in ConfigManager list", extra={"src": "_get_env"})
|
||||
return env
|
||||
|
||||
|
||||
if not env_id:
|
||||
app_logger.reflect(f"Using first environment from ConfigManager: {envs[0].name}", extra={"src": "_get_env"})
|
||||
return envs[0]
|
||||
|
||||
|
||||
app_logger.error(f"[_get_env][Coherence:Failed] No environments configured (searched config.json and DB). env_id={env_id}")
|
||||
raise ValueError("No environments configured. Please add a Superset Environment in Settings.")
|
||||
# endregion _get_env
|
||||
|
||||
# endregion initialize
|
||||
# #endregion GitPlugin
|
||||
# #endregion GitPluginModule
|
||||
# #endregion GitPluginModule
|
||||
|
||||
Reference in New Issue
Block a user