From 6787e97a72cc521272e3726cffa12c1291521029 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Sun, 21 Jun 2026 13:57:48 +0300 Subject: [PATCH] =?UTF-8?q?feat(db-load-dt):=20=D0=B7=D0=B0=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=B7=D0=BA=D0=B0=20=D0=98=D0=91=20=D0=B8=D0=B7=20DT-?= =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=20(RestoreIB),=20opt-in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Новый навык пакетной загрузки всей информационной базы из .dt через конфигуратор /RestoreIB (с опц. -JobsCount, -UnlockCode/UC). Операция необратима (полная перезапись базы) → disable-model-invocation: true, плюс инструкция: сначала предложить db-dump-dt как точку отката, затем подтверждение. db-update после не нужен. PS1 + py-порт. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/db-load-dt/SKILL.md | 92 ++++++++++ .../skills/db-load-dt/scripts/db-load-dt.ps1 | 158 ++++++++++++++++++ .../skills/db-load-dt/scripts/db-load-dt.py | 126 ++++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 .claude/skills/db-load-dt/SKILL.md create mode 100644 .claude/skills/db-load-dt/scripts/db-load-dt.ps1 create mode 100644 .claude/skills/db-load-dt/scripts/db-load-dt.py diff --git a/.claude/skills/db-load-dt/SKILL.md b/.claude/skills/db-load-dt/SKILL.md new file mode 100644 index 00000000..fcecd4fa --- /dev/null +++ b/.claude/skills/db-load-dt/SKILL.md @@ -0,0 +1,92 @@ +--- +name: db-load-dt +description: Загрузка информационной базы 1С из DT-файла — полная перезапись базы (конфигурация + данные). Используй когда нужно загрузить архив информационной базы, восстановить базу, загрузить dt +disable-model-invocation: true +argument-hint: [database] +allowed-tools: + - Bash + - Read + - Glob + - AskUserQuestion +--- + +# /db-load-dt — Загрузка информационной базы из DT-файла + +Восстанавливает информационную базу целиком (конфигурация **+ данные**) из DT-файла. + +> ⚠️ **Необратимая операция.** Загрузка `.dt` **полностью перезаписывает базу** — и +> конфигурацию, и все данные. Текущее содержимое базы будет потеряно. После загрузки +> `/db-update` **не нужен** — конфигурация БД уже синхронна внутри снимка. + +## Когда НЕ использовать + +- Нужно создать **новую** базу из `.dt` → используй `/db-create` (из DT-шаблона), а не загрузку + в существующую. +- Нужно обновить только конфигурацию (без данных) → `/db-load-cf` или `/db-load-xml`. + +## Usage + +``` +/db-load-dt [database] +/db-load-dt backup.dt dev +``` + +## Порядок действий перед загрузкой + +1. Предложи пользователю сначала сделать `/db-dump-dt` текущего состояния базы — это точка + отката (восстановиться будет нечем, если не сохранить). +2. Запроси **явное подтверждение**: вся база (данные + конфигурация) будет перезаписана. +3. Только после подтверждения выполняй загрузку. + +## Параметры подключения + +Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую +2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` +3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` +4. Если ветка не совпала — используй `default` +Если `v8path` не задан — автоопределение: `Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort -Desc | Select -First 1` +Если файла нет — предложи `/db-list add`. +Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. + +## Команда + +```powershell +powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-load-dt.ps1" <параметры> +``` + +### Параметры скрипта + +| Параметр | Обязательный | Описание | +|----------|:------------:|----------| +| `-V8Path <путь>` | нет | Каталог bin платформы (или полный путь к 1cv8.exe) | +| `-InfoBasePath <путь>` | * | Файловая база | +| `-InfoBaseServer <сервер>` | * | Сервер 1С (для серверной базы) | +| `-InfoBaseRef <имя>` | * | Имя базы на сервере | +| `-UserName <имя>` | нет | Имя пользователя | +| `-Password <пароль>` | нет | Пароль | +| `-InputFile <путь>` | да | Путь к DT-файлу | +| `-JobsCount ` | нет | Число фоновых заданий загрузки (0 = по числу процессоров) | +| `-UnlockCode <код>` | нет | Код разблокировки (`/UC`), если заблокировано начало сеансов | + +> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef` + +## После выполнения + +Если база занята (активные сеансы), загрузка не выполнится — для серверной базы можно +передать `-UnlockCode`; иначе освободи базу и повтори. + +## Примеры + +```powershell +# Файловая база +powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-load-dt.ps1" -InfoBasePath "C:\Bases\MyDB" -UserName "Admin" -InputFile "C:\backup\base.dt" + +# Серверная база с ускорением загрузки +powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-load-dt.ps1" -InfoBaseServer "srv01" -InfoBaseRef "MyApp_Test" -UserName "Admin" -Password "secret" -InputFile "base.dt" -JobsCount 4 +``` + +## Связанные навыки + +- `/db-dump-dt` — выгрузка ИБ в DT (обратная операция, точка отката перед загрузкой) +- `/db-create` — создать новую базу (в т.ч. из DT-шаблона) diff --git a/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 b/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 new file mode 100644 index 00000000..1484d9ff --- /dev/null +++ b/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 @@ -0,0 +1,158 @@ +# db-load-dt v1.0 — Load 1C information base from DT file +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Загрузка информационной базы 1С из DT-файла + +.DESCRIPTION + Загружает информационную базу целиком (конфигурация + данные) из DT-файла. + ВНИМАНИЕ: операция полностью перезаписывает базу. + +.PARAMETER V8Path + Путь к каталогу bin платформы или к 1cv8.exe + +.PARAMETER InfoBasePath + Путь к файловой информационной базе + +.PARAMETER InfoBaseServer + Сервер 1С (для серверной базы) + +.PARAMETER InfoBaseRef + Имя базы на сервере + +.PARAMETER UserName + Имя пользователя 1С + +.PARAMETER Password + Пароль пользователя + +.PARAMETER InputFile + Путь к DT-файлу для загрузки + +.PARAMETER JobsCount + Количество фоновых заданий для загрузки (0 = по числу процессоров) + +.PARAMETER UnlockCode + Код разблокировки базы (/UC) — если заблокировано начало сеансов + +.EXAMPLE + .\db-load-dt.ps1 -InfoBasePath "C:\Bases\MyDB" -InputFile "backup.dt" +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory=$false)] + [string]$V8Path, + + [Parameter(Mandatory=$false)] + [string]$InfoBasePath, + + [Parameter(Mandatory=$false)] + [string]$InfoBaseServer, + + [Parameter(Mandatory=$false)] + [string]$InfoBaseRef, + + [Parameter(Mandatory=$false)] + [string]$UserName, + + [Parameter(Mandatory=$false)] + [string]$Password, + + [Parameter(Mandatory=$true)] + [string]$InputFile, + + [Parameter(Mandatory=$false)] + [int]$JobsCount = 0, + + [Parameter(Mandatory=$false)] + [string]$UnlockCode +) + +$OutputEncoding = [System.Text.Encoding]::UTF8 +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +# --- Resolve V8Path --- +if (-not $V8Path) { + $found = Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" -ErrorAction SilentlyContinue | Sort-Object FullName -Descending | Select-Object -First 1 + if ($found) { + $V8Path = $found.FullName + } else { + Write-Host "Error: 1cv8.exe not found. Specify -V8Path" -ForegroundColor Red + exit 1 + } +} elseif (Test-Path $V8Path -PathType Container) { + $V8Path = Join-Path $V8Path "1cv8.exe" +} + +if (-not (Test-Path $V8Path)) { + Write-Host "Error: 1cv8.exe not found at $V8Path" -ForegroundColor Red + exit 1 +} + +# --- Validate connection --- +if (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) { + Write-Host "Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef" -ForegroundColor Red + exit 1 +} + +# --- Validate input file --- +if (-not (Test-Path $InputFile)) { + Write-Host "Error: input file not found: $InputFile" -ForegroundColor Red + exit 1 +} + +# --- Temp dir --- +$tempDir = Join-Path $env:TEMP "db_load_dt_$(Get-Random)" +New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + +try { + # --- Build arguments --- + $arguments = @("DESIGNER") + + if ($InfoBaseServer -and $InfoBaseRef) { + $arguments += "/S", "`"$InfoBaseServer/$InfoBaseRef`"" + } else { + $arguments += "/F", "`"$InfoBasePath`"" + } + + if ($UserName) { $arguments += "/N`"$UserName`"" } + if ($Password) { $arguments += "/P`"$Password`"" } + if ($UnlockCode) { $arguments += "/UC`"$UnlockCode`"" } + + $arguments += "/RestoreIB", "`"$InputFile`"" + if ($JobsCount -gt 0) { $arguments += "-JobsCount", "$JobsCount" } + + # --- Output --- + $outFile = Join-Path $tempDir "load_dt_log.txt" + $arguments += "/Out", "`"$outFile`"" + $arguments += "/DisableStartupDialogs" + + # --- Execute --- + Write-Host "Running: 1cv8.exe $($arguments -join ' ')" + $process = Start-Process -FilePath $V8Path -ArgumentList $arguments -NoNewWindow -Wait -PassThru + $exitCode = $process.ExitCode + + # --- Result --- + if ($exitCode -eq 0) { + Write-Host "Information base restored successfully from: $InputFile" -ForegroundColor Green + } else { + Write-Host "Error restoring information base (code: $exitCode)" -ForegroundColor Red + } + + if (Test-Path $outFile) { + $logContent = Get-Content $outFile -Raw -ErrorAction SilentlyContinue + if ($logContent) { + Write-Host "--- Log ---" + Write-Host $logContent + Write-Host "--- End ---" + } + } + + exit $exitCode + +} finally { + if (Test-Path $tempDir) { + Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } +} diff --git a/.claude/skills/db-load-dt/scripts/db-load-dt.py b/.claude/skills/db-load-dt/scripts/db-load-dt.py new file mode 100644 index 00000000..e7ec0b74 --- /dev/null +++ b/.claude/skills/db-load-dt/scripts/db-load-dt.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# db-load-dt v1.0 — Load 1C information base from DT file +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills + +import argparse +import glob +import os +import random +import shutil +import subprocess +import sys +import tempfile + + +def resolve_v8path(v8path): + """Resolve path to 1cv8.exe.""" + if not v8path: + found = sorted(glob.glob(r"C:\Program Files\1cv8\*\bin\1cv8.exe")) + if found: + return found[-1] + else: + print("Error: 1cv8.exe not found. Specify -V8Path", file=sys.stderr) + sys.exit(1) + elif os.path.isdir(v8path): + v8path = os.path.join(v8path, "1cv8.exe") + + if not os.path.isfile(v8path): + print(f"Error: 1cv8.exe not found at {v8path}", file=sys.stderr) + sys.exit(1) + return v8path + + +def main(): + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") + parser = argparse.ArgumentParser( + description="Load 1C information base from DT file", + allow_abbrev=False, + ) + parser.add_argument("-V8Path", default="") + parser.add_argument("-InfoBasePath", default="") + parser.add_argument("-InfoBaseServer", default="") + parser.add_argument("-InfoBaseRef", default="") + parser.add_argument("-UserName", default="") + parser.add_argument("-Password", default="") + parser.add_argument("-InputFile", required=True) + parser.add_argument("-JobsCount", type=int, default=0) + parser.add_argument("-UnlockCode", default="") + args = parser.parse_args() + + v8path = resolve_v8path(args.V8Path) + + # --- Validate connection --- + if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef): + print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr) + sys.exit(1) + + # --- Validate input file --- + if not os.path.isfile(args.InputFile): + print(f"Error: input file not found: {args.InputFile}", file=sys.stderr) + sys.exit(1) + + # --- Temp dir --- + temp_dir = os.path.join(tempfile.gettempdir(), f"db_load_dt_{random.randint(0, 999999)}") + os.makedirs(temp_dir, exist_ok=True) + + try: + # --- Build arguments --- + arguments = ["DESIGNER"] + + if args.InfoBaseServer and args.InfoBaseRef: + arguments.extend(["/S", f"{args.InfoBaseServer}/{args.InfoBaseRef}"]) + else: + arguments.extend(["/F", args.InfoBasePath]) + + if args.UserName: + arguments.append(f"/N{args.UserName}") + if args.Password: + arguments.append(f"/P{args.Password}") + if args.UnlockCode: + arguments.append(f"/UC{args.UnlockCode}") + + arguments.extend(["/RestoreIB", args.InputFile]) + if args.JobsCount > 0: + arguments.extend(["-JobsCount", str(args.JobsCount)]) + + # --- Output --- + out_file = os.path.join(temp_dir, "load_dt_log.txt") + arguments.extend(["/Out", out_file]) + arguments.append("/DisableStartupDialogs") + + # --- Execute --- + print(f"Running: 1cv8.exe {' '.join(arguments)}") + result = subprocess.run( + [v8path] + arguments, + capture_output=True, + text=True, + ) + exit_code = result.returncode + + # --- Result --- + if exit_code == 0: + print(f"Information base restored successfully from: {args.InputFile}") + else: + print(f"Error restoring information base (code: {exit_code})", file=sys.stderr) + + if os.path.isfile(out_file): + try: + with open(out_file, "r", encoding="utf-8-sig") as f: + log_content = f.read() + if log_content: + print("--- Log ---") + print(log_content) + print("--- End ---") + except Exception: + pass + + sys.exit(exit_code) + + finally: + if os.path.isdir(temp_dir): + shutil.rmtree(temp_dir, ignore_errors=True) + + +if __name__ == "__main__": + main()