Release 0.3.2: YAML config, default_baseconfs, autodiscover.

Конфиг утилиты переведён на YAML; default_baseconfs для reindex без -b;
автопоиск config.local.yml/.yaml/.json; обобщённая документация.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mihailkudravcev
2026-07-17 10:56:56 +03:00
parent 45e798f2bf
commit 404659a545
14 changed files with 293 additions and 151 deletions
+85 -16
View File
@@ -3,7 +3,6 @@
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from typing import Any
@@ -21,25 +20,89 @@ class BaseconfError(Exception):
def load_tool_config(path: Path) -> dict[str, Any]:
"""Файл настроек утилиты index (JSON). Не путать с конфигурацией 1С."""
"""Файл настроек утилиты index (YAML; legacy JSON поддерживается)."""
ext = path.suffix.lower()
try:
data = json.loads(path.read_text(encoding="utf-8"))
raw = path.read_text(encoding="utf-8")
except OSError as e:
raise BaseconfError(f"Не удалось прочитать --config {path}: {e}", exit_code=1) from e
except json.JSONDecodeError as e:
raise BaseconfError(f"Некорректный JSON в --config {path}: {e}", exit_code=1) from e
if ext == ".json":
try:
import json
data = json.loads(raw)
except Exception as e:
raise BaseconfError(f"Некорректный JSON в --config {path}: {e}", exit_code=1) from e
if not isinstance(data, dict):
raise BaseconfError(f"--config {path}: ожидается JSON-объект", exit_code=1)
return data
try:
import yaml # type: ignore
except Exception as e:
raise BaseconfError(
"Для чтения --config в формате YAML нужен пакет pyyaml: pip install pyyaml",
exit_code=1,
) from e
try:
data = yaml.safe_load(raw)
except Exception as e:
raise BaseconfError(f"Некорректный YAML в --config {path}: {e}", exit_code=1) from e
if not isinstance(data, dict):
raise BaseconfError(f"--config {path}: ожидается JSON-объект", exit_code=1)
raise BaseconfError(f"--config {path}: ожидается YAML-объект", exit_code=1)
return data
def discover_local_tool_config() -> Path | None:
"""Автопоиск локального конфига без --config."""
candidates = ("config.local.yml", "config.local.yaml", "config.local.json")
roots = [Path.cwd(), project_root_from_here()]
seen: set[Path] = set()
for root in roots:
for rel in (Path("tools/index"), Path(".")):
base = (root / rel).resolve()
if base in seen or not base.is_dir():
continue
seen.add(base)
for name in candidates:
p = base / name
if p.is_file():
return p
return None
def apply_tool_config(data: dict[str, Any]) -> None:
"""Применить настройки из YAML (--config): алиасы и дефолтные baseconf."""
from .config import set_config_aliases, set_default_baseconfs
aliases = data.get("baseconf_aliases")
if aliases is not None:
if not isinstance(aliases, dict):
raise BaseconfError("--config: baseconf_aliases должен быть объектом YAML")
set_config_aliases({str(k): str(v) for k, v in aliases.items()})
default_baseconfs = data.get("default_baseconfs")
if default_baseconfs is not None:
if not isinstance(default_baseconfs, list):
raise BaseconfError("--config: default_baseconfs должен быть YAML-списком")
set_default_baseconfs([str(x) for x in default_baseconfs])
else:
set_default_baseconfs([])
def resolve_project_root(args: argparse.Namespace) -> Path:
"""Корень проекта: --root → project_root из --config → авто."""
"""Корень проекта: --root → --config/auto-config → авто."""
if getattr(args, "root", None):
return Path(args.root).resolve()
cfg_path = getattr(args, "config", None)
if not cfg_path:
auto = discover_local_tool_config()
if auto is not None:
cfg_path = str(auto)
setattr(args, "config", cfg_path)
if cfg_path:
data = load_tool_config(Path(cfg_path))
apply_tool_config(data)
pr = data.get("project_root")
if pr:
return Path(pr).resolve()
@@ -52,13 +115,16 @@ def add_tool_args(parser: argparse.ArgumentParser) -> None:
"--config",
"-c",
metavar="FILE",
help="JSON с настройками утилиты (project_root и др.), см. config.example.json",
help=(
"YAML с настройками утилиты (project_root и др.), см. config.example.yml. "
"Без параметра: автопоиск config.local.yml/.yaml/.json"
),
)
parser.add_argument(
"--root",
"-r",
metavar="DIR",
help="Корень проекта CRM3-26 (перекрывает project_root из --config)",
help="Корень проекта с выгрузками 1С (перекрывает project_root из --config)",
)
@@ -69,7 +135,10 @@ def add_baseconf_args(
) -> None:
"""Фильтр по выгрузке конфигурации 1С в репозитории."""
if mode == "index":
default_hint = "Без параметра — индексировать все выгрузки с src/ в репозитории."
default_hint = (
"Без параметра — default_baseconfs из --config; "
"если блок не задан, индексировать все выгрузки с src/."
)
else:
default_hint = "Без параметра — поиск по всем проиндексированным."
parser.add_argument(
@@ -80,8 +149,8 @@ def add_baseconf_args(
dest="baseconf",
help=(
"Конфигурация 1С в репозитории (папка <name>/src/). "
"Можно несколько раз или через запятую: -b target,source. "
"Алиасы: target→crm3-26, source→crm3-dev. "
"Можно несколько раз или через запятую: -b cfg1,cfg2. "
"Краткие имена (алиасы) — в baseconf_aliases файла --config. "
f"{default_hint}"
),
)
@@ -192,7 +261,7 @@ def validate_baseconfs(
lines.extend(
[
"",
"Алиасы: target→crm3-26, source→crm3-dev, crm3_old→crm3-dev",
"Краткие имена (алиасы) для -b задаются в baseconf_aliases файла --config.",
]
)
raise BaseconfError("\n".join(lines))
@@ -216,7 +285,7 @@ def validate_baseconfs(
lines.extend(
[
"",
f"Переиндексация: python tools/index/index_1c.py reindex --full -b {not_indexed[0]}",
f"Переиндексация: python index_1c.py reindex --full -b {not_indexed[0]}",
]
)
raise BaseconfError("\n".join(lines))
@@ -297,7 +366,7 @@ def index_db_missing_message(project_root: Path) -> str:
return (
f"Индекс не найден: {path}\n"
"Сначала выполните:\n"
" python tools/index/index_1c.py reindex --full -j 12\n"
" python index_1c.py reindex --full -j 12\n"
"или для одной конфигурации:\n"
" python tools/index/index_1c.py reindex --full -b crm3-26"
" python index_1c.py reindex --full -b <имя-выгрузки>"
)