404659a545
Конфиг утилиты переведён на YAML; default_baseconfs для reindex без -b; автопоиск config.local.yml/.yaml/.json; обобщённая документация. Co-authored-by: Cursor <cursoragent@cursor.com>
256 lines
9.0 KiB
Python
Executable File
256 lines
9.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""Переиндексация метаданных и модулей конфигураций 1С → cache/1c_meta/index.sqlite.
|
||
|
||
Параметры:
|
||
--config, -c файл настроек утилиты (YAML)
|
||
--baseconf, -b какие выгрузки 1С индексировать (имя каталога с src/)
|
||
|
||
Примеры:
|
||
|
||
python index_1c.py reindex -j 12
|
||
python index_1c.py reindex --full -j 12
|
||
python index_1c.py reindex --full -b my-config -j 12
|
||
python index_1c.py reindex -b main -b my-config.ext -t Documents,Catalogs
|
||
python index_1c.py status
|
||
python index_1c.py list
|
||
|
||
Поиск: python query_1c.py …
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
HERE = Path(__file__).resolve().parent
|
||
if str(HERE) not in sys.path:
|
||
sys.path.insert(0, str(HERE))
|
||
|
||
from index1c.build import build_project_index, default_workers # noqa: E402
|
||
from index1c import read_version_file # noqa: E402
|
||
from index1c.cli import ( # noqa: E402
|
||
BaseconfError,
|
||
add_tool_args,
|
||
handle_baseconf_error,
|
||
index_db_missing_message,
|
||
make_parent_parser,
|
||
resolve_project_root,
|
||
validate_baseconfs,
|
||
)
|
||
from index1c.config import ( # noqa: E402
|
||
DEFAULT_SKIP_TYPES,
|
||
cache_dir,
|
||
discover_configs,
|
||
get_default_baseconfs,
|
||
get_config_aliases,
|
||
resolve_object_type,
|
||
)
|
||
from index1c import db as dbmod # noqa: E402
|
||
|
||
|
||
def _parse_types(raw: str | None) -> set[str] | None:
|
||
if not raw:
|
||
return None
|
||
types: set[str] = set()
|
||
for part in raw.replace(";", ",").split(","):
|
||
part = part.strip()
|
||
if not part:
|
||
continue
|
||
types.add(resolve_object_type(part))
|
||
return types or None
|
||
|
||
|
||
def _select_configs_for_reindex(args: argparse.Namespace, root: Path) -> list:
|
||
"""Какие конфигурации 1С индексировать."""
|
||
if args.baseconf:
|
||
try:
|
||
names = validate_baseconfs(root, args.baseconf, require_indexed=False)
|
||
except BaseconfError as e:
|
||
print(str(e), file=sys.stderr)
|
||
sys.exit(e.exit_code)
|
||
return discover_configs(root, names)
|
||
|
||
# без -b: default_baseconfs из --config или автосканирование.
|
||
defaults = get_default_baseconfs()
|
||
if defaults:
|
||
try:
|
||
names = validate_baseconfs(root, defaults, require_indexed=False)
|
||
except BaseconfError as e:
|
||
print(str(e), file=sys.stderr)
|
||
sys.exit(e.exit_code)
|
||
return discover_configs(root, names)
|
||
|
||
# fallback: все каталоги с src/ в корне проекта
|
||
return discover_configs(root, None)
|
||
|
||
|
||
def cmd_reindex(args: argparse.Namespace) -> int:
|
||
root = resolve_project_root(args)
|
||
configs = _select_configs_for_reindex(args, root)
|
||
if not configs:
|
||
print(
|
||
"Нет конфигураций для индексации (нужна папка <name>/src/).\n"
|
||
"Укажите -b <имя-выгрузки> или проверьте: python index_1c.py list",
|
||
file=sys.stderr,
|
||
)
|
||
return 1
|
||
|
||
types = _parse_types(args.types)
|
||
skip = set(DEFAULT_SKIP_TYPES)
|
||
if args.include_skipped:
|
||
skip.clear()
|
||
if args.skip_types:
|
||
for t in args.skip_types.replace(";", ",").split(","):
|
||
t = t.strip()
|
||
if t:
|
||
skip.add(resolve_object_type(t))
|
||
|
||
workers = args.workers if args.workers and args.workers > 0 else default_workers()
|
||
# --full без -b → пересоздать всю БД
|
||
wipe_db = bool(args.full and not args.baseconf)
|
||
print(
|
||
f"Режим: {'full-wipe' if wipe_db else ('full' if args.full else 'incremental')}, "
|
||
f"workers={workers}, baseconf={[c.name for c in configs]}, modules={not args.no_modules}"
|
||
)
|
||
|
||
summary = build_project_index(
|
||
configs,
|
||
root,
|
||
full=args.full or wipe_db,
|
||
wipe_db=wipe_db,
|
||
types=types,
|
||
skip_types=skip,
|
||
index_modules=not args.no_modules,
|
||
workers=workers,
|
||
write_md=args.md,
|
||
)
|
||
print(
|
||
f"Готово: {summary['db']} ({summary['elapsed_sec']}s) "
|
||
f"meta+{summary['meta_parsed']}/skip{summary['meta_skipped']} "
|
||
f"mod+{summary['modules_parsed']}/skip{summary['modules_skipped']} "
|
||
f"removed={summary['files_removed']} err={summary['errors']}"
|
||
)
|
||
return 0
|
||
|
||
|
||
def cmd_status(args: argparse.Namespace) -> int:
|
||
root = resolve_project_root(args)
|
||
db_path = dbmod.global_db_path(cache_dir(root))
|
||
if not db_path.is_file():
|
||
print(index_db_missing_message(root), file=sys.stderr)
|
||
return 1
|
||
conn = dbmod.connect(db_path, readonly=True)
|
||
try:
|
||
print(f"БД: {db_path} ({db_path.stat().st_size // (1024 * 1024)} МБ)")
|
||
for row in conn.execute("SELECT key, value FROM meta ORDER BY key"):
|
||
print(f" meta.{row['key']} = {row['value']}")
|
||
print("Конфигурации 1С в индексе (--baseconf):")
|
||
for c in dbmod.list_configs(conn):
|
||
print(
|
||
f"- {c['name']}: objects={c['objects']} fields={c['fields']} "
|
||
f"refs={c['refs']} modules={c['modules']} @ {c['updated_at']}"
|
||
)
|
||
finally:
|
||
conn.close()
|
||
return 0
|
||
|
||
|
||
def cmd_list_configs(args: argparse.Namespace) -> int:
|
||
root = resolve_project_root(args)
|
||
found = discover_configs(root, None)
|
||
print("Выгрузки 1С в репозитории (для --baseconf / -b):")
|
||
for c in found:
|
||
aliases = [k for k, v in get_config_aliases().items() if v == c.name]
|
||
alias_s = f" алиасы: {', '.join(aliases)}" if aliases else ""
|
||
print(f"- {c.name}{alias_s}")
|
||
db_path = dbmod.global_db_path(cache_dir(root))
|
||
if db_path.is_file():
|
||
conn = dbmod.connect(db_path, readonly=True)
|
||
try:
|
||
print("\nПроиндексировано (в index.sqlite):")
|
||
for c in dbmod.list_configs(conn):
|
||
print(f"- {c['name']}")
|
||
finally:
|
||
conn.close()
|
||
else:
|
||
print(f"\nИндекс отсутствует. {index_db_missing_message(root)}")
|
||
return 0
|
||
|
||
|
||
def build_parser() -> argparse.ArgumentParser:
|
||
tool_parent = make_parent_parser(include_baseconf=False)
|
||
reindex_parent = make_parent_parser(include_baseconf=True, baseconf_mode="index")
|
||
|
||
p = argparse.ArgumentParser(
|
||
description="Построение единого SQLite-индекса метаданных и BSL-модулей 1С.",
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
epilog=(
|
||
"Конфигурация 1С: --baseconf (-b). Файл настроек утилиты: --config (-c).\n"
|
||
"Алиасы/default для baseconf — в YAML (--config), см. config.example.yml"
|
||
),
|
||
)
|
||
p.add_argument(
|
||
"--version",
|
||
action="version",
|
||
version=f"%(prog)s {read_version_file()}",
|
||
)
|
||
add_tool_args(p)
|
||
sub = p.add_subparsers(dest="cmd", required=True)
|
||
|
||
r = sub.add_parser(
|
||
"reindex",
|
||
help="Построить / обновить index.sqlite",
|
||
parents=[reindex_parent],
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
description="Построение или инкрементальное обновление index.sqlite.",
|
||
)
|
||
r.add_argument("--types", "-t", help="Только типы: Documents,Catalogs,рс,рн,…")
|
||
r.add_argument("--skip-types", help="Дополнительно пропустить типы")
|
||
r.add_argument("--include-skipped", action="store_true")
|
||
r.add_argument(
|
||
"--full",
|
||
action="store_true",
|
||
help="Полная пересборка (вся БД или только указанные -b)",
|
||
)
|
||
r.add_argument("--no-modules", action="store_true", help="Не индексировать .bsl")
|
||
r.add_argument(
|
||
"--workers",
|
||
"-j",
|
||
type=int,
|
||
default=0,
|
||
help=f"Потоков (по умолчанию {default_workers()})",
|
||
)
|
||
r.add_argument("--md", action="store_true", help="Markdown в cache/1c_meta/md/")
|
||
r.set_defaults(func=cmd_reindex)
|
||
|
||
st = sub.add_parser(
|
||
"status",
|
||
help="Состояние index.sqlite",
|
||
parents=[tool_parent],
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
description="Размер БД, meta и список проиндексированных конфигураций.",
|
||
)
|
||
st.set_defaults(func=cmd_status)
|
||
|
||
lc = sub.add_parser(
|
||
"list",
|
||
help="Выгрузки в репозитории и в индексе",
|
||
parents=[tool_parent],
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
description="Выгрузки 1С с src/ в репозитории и что уже в index.sqlite.",
|
||
)
|
||
lc.set_defaults(func=cmd_list_configs)
|
||
|
||
return p
|
||
|
||
|
||
def main(argv: list[str] | None = None) -> int:
|
||
parser = build_parser()
|
||
args = parser.parse_args(argv)
|
||
return int(args.func(args))
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|