feat(db): ibcmd для частичной выгрузки/загрузки из исходников

Закрыты частичные режимы, ранее дававшие «use 1cv8»:
- db-dump-xml Mode=Partial → ibcmd config export objects <имена>
  --out=<dir> (выгрузка указанных объектов конфигурации);
- db-load-xml Mode=Partial / -Files / -ListFile → ibcmd config import
  files <отн.пути> --base-dir=<ConfigDir> (--base-dir обязателен);
- db-load-git → добавлена ibcmd-поддержка (раньше отсутствовала):
  config import files <изменённые из git> --base-dir=<ConfigDir>,
  с цепочкой config apply --force при -UpdateDB; DryRun не трогает движок.

Несовместимое под ibcmd по-прежнему даёт чистую ошибку: -Format Plain,
-AllExtensions, db-dump-xml Mode UpdateInfo. 1cv8-ветки без изменений.
Версии: db-dump-xml 1.2→1.3, db-load-xml 1.6→1.7, db-load-git 1.5→1.6.

E2E: export objects (только указанные объекты), import files round-trip
(+apply), db-load-git staged→import files+apply, DryRun, error-кейсы,
1cv8-регресс Partial — всё зелёное.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-06-21 16:33:07 +03:00
parent 4102b7005a
commit 27b9d539e0
7 changed files with 175 additions and 33 deletions
@@ -1,4 +1,4 @@
# db-dump-xml v1.2 — Dump 1C configuration to XML files
# db-dump-xml v1.3 — Dump 1C configuration to XML files
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
<#
.SYNOPSIS
@@ -182,13 +182,20 @@ try {
Write-Host "Error: ibcmd config export does not support -AllExtensions (use -Extension or 1cv8)" -ForegroundColor Red
exit 1
}
if ($Mode -eq "Partial" -or $Mode -eq "UpdateInfo") {
Write-Host "Error: ibcmd config export supports Mode Full/Changes only; use 1cv8 for $Mode" -ForegroundColor Red
if ($Mode -eq "UpdateInfo") {
Write-Host "Error: ibcmd config export does not support Mode UpdateInfo; use 1cv8" -ForegroundColor Red
exit 1
}
$arguments = @("infobase", "config", "export", "--db-path=$InfoBasePath")
if ($Extension) { $arguments += "--extension=$Extension" }
$arguments += "$ConfigDir"
if ($Mode -eq "Partial") {
$objList = @($Objects -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$arguments = @("infobase", "config", "export", "objects") + $objList
$arguments += "--out=$ConfigDir", "--db-path=$InfoBasePath"
if ($Extension) { $arguments += "--extension=$Extension" }
} else {
$arguments = @("infobase", "config", "export", "--db-path=$InfoBasePath")
if ($Extension) { $arguments += "--extension=$Extension" }
$arguments += "$ConfigDir"
}
Write-Host "Running: ibcmd $($arguments -join ' ')"
$output = & $V8Path @arguments 2>&1
$exitCode = $LASTEXITCODE
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# db-dump-xml v1.2 — Dump 1C configuration to XML files
# db-dump-xml v1.3 — Dump 1C configuration to XML files
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -127,13 +127,20 @@ def main():
if args.AllExtensions:
print("Error: ibcmd config export does not support -AllExtensions (use -Extension or 1cv8)", file=sys.stderr)
sys.exit(1)
if args.Mode in ("Partial", "UpdateInfo"):
print(f"Error: ibcmd config export supports Mode Full/Changes only; use 1cv8 for {args.Mode}", file=sys.stderr)
if args.Mode == "UpdateInfo":
print("Error: ibcmd config export does not support Mode UpdateInfo; use 1cv8", file=sys.stderr)
sys.exit(1)
arguments = ["infobase", "config", "export", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
arguments.append(args.ConfigDir)
if args.Mode == "Partial":
obj_list = [o.strip() for o in args.Objects.split(",") if o.strip()]
arguments = ["infobase", "config", "export", "objects"] + obj_list
arguments += [f"--out={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
else:
arguments = ["infobase", "config", "export", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
arguments.append(args.ConfigDir)
print(f"Running: ibcmd {' '.join(arguments)}")
result = subprocess.run([v8path] + arguments, capture_output=True, encoding="utf-8", errors="replace")
if result.returncode == 0:
+3 -1
View File
@@ -45,7 +45,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-load-git.ps1" <
| Параметр | Обязательный | Описание |
|----------|:------------:|----------|
| `-V8Path <путь>` | нет | Каталог bin платформы (или полный путь к 1cv8.exe) |
| `-V8Path <путь>` | нет | Каталог bin платформы, или полный путь к `1cv8.exe` / `ibcmd.exe` |
| `-InfoBasePath <путь>` | * | Файловая база |
| `-InfoBaseServer <сервер>` | * | Сервер 1С (для серверной базы) |
| `-InfoBaseRef <имя>` | * | Имя базы на сервере |
@@ -61,6 +61,8 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-load-git.ps1" <
| `-UpdateDB` | нет | После загрузки сразу обновить конфигурацию БД (`/UpdateDBCfg`) |
> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef`
>
> Если `-V8Path` указывает на `ibcmd.exe` — загрузка идёт через автономный сервер (`config import files`); поддерживаются **только файловые базы** (`-InfoBasePath`).
## После выполнения
@@ -1,4 +1,4 @@
# db-load-git v1.5 — Load Git changes into 1C database
# db-load-git v1.6 — Load Git changes into 1C database
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
<#
.SYNOPSIS
@@ -163,9 +163,16 @@ if (-not $DryRun) {
}
}
# --- Validate connection (skip if DryRun) ---
# --- Detect engine + validate connection (skip if DryRun) ---
$engine = "1cv8"
if (-not $DryRun) {
if (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) {
$engine = if ((Split-Path $V8Path -Leaf) -match '^ibcmd') { "ibcmd" } else { "1cv8" }
if ($engine -eq "ibcmd") {
if (-not $InfoBasePath) {
Write-Host "Error: ibcmd supports file infobases only (use -InfoBasePath)" -ForegroundColor Red
exit 1
}
} elseif (-not $InfoBasePath -and (-not $InfoBaseServer -or -not $InfoBaseRef)) {
Write-Host "Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef" -ForegroundColor Red
exit 1
}
@@ -319,6 +326,45 @@ $tempDir = Join-Path $env:TEMP "db_load_git_$(Get-Random)"
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
try {
if ($engine -eq "ibcmd") {
# --- ibcmd branch (file infobase only; import specific files) ---
if ($Format -eq "Plain") {
Write-Host "Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)" -ForegroundColor Red
exit 1
}
if ($AllExtensions) {
Write-Host "Error: ibcmd config import does not support -AllExtensions (use -Extension or 1cv8)" -ForegroundColor Red
exit 1
}
$arguments = @("infobase", "config", "import", "files") + $configFiles
$arguments += "--base-dir=$ConfigDir", "--db-path=$InfoBasePath"
if ($Extension) { $arguments += "--extension=$Extension" }
Write-Host "Running: ibcmd $($arguments -join ' ')"
$output = & $V8Path @arguments 2>&1
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
Write-Host "Error loading changes (code: $exitCode)" -ForegroundColor Red
if ($output) { Write-Host ($output | Out-String) }
exit $exitCode
}
Write-Host "Changes loaded successfully ($($configFiles.Count) files)" -ForegroundColor Green
if ($output) { Write-Host ($output | Out-String) }
if ($UpdateDB) {
$applyArgs = @("infobase", "config", "apply", "--db-path=$InfoBasePath", "--force")
Write-Host "Running: ibcmd $($applyArgs -join ' ')"
$applyOut = & $V8Path @applyArgs 2>&1
$exitCode = $LASTEXITCODE
if ($exitCode -eq 0) {
Write-Host "Database configuration updated successfully" -ForegroundColor Green
} else {
Write-Host "Error updating database configuration (code: $exitCode)" -ForegroundColor Red
}
if ($applyOut) { Write-Host ($applyOut | Out-String) }
}
exit $exitCode
}
# --- 1cv8 branch ---
# --- Write list file (UTF-8 with BOM) ---
$listFile = Join-Path $tempDir "load_list.txt"
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# db-load-git v1.5 — Load Git changes into 1C database
# db-load-git v1.6 — Load Git changes into 1C database
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -125,9 +125,15 @@ def main():
if not args.DryRun:
v8path = resolve_v8path(args.V8Path)
# --- Validate connection (skip if DryRun) ---
# --- Detect engine + validate connection (skip if DryRun) ---
engine = "1cv8"
if not args.DryRun:
if not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8"
if engine == "ibcmd":
if not args.InfoBasePath:
print("Error: ibcmd supports file infobases only (use -InfoBasePath)", file=sys.stderr)
sys.exit(1)
elif not args.InfoBasePath and (not args.InfoBaseServer or not args.InfoBaseRef):
print("Error: specify -InfoBasePath or -InfoBaseServer + -InfoBaseRef", file=sys.stderr)
sys.exit(1)
@@ -248,6 +254,46 @@ def main():
os.makedirs(temp_dir, exist_ok=True)
try:
if engine == "ibcmd":
# --- ibcmd branch (file infobase only; import specific files) ---
if args.Format == "Plain":
print("Error: ibcmd config import supports hierarchical format only (use -Format Hierarchical or 1cv8)", file=sys.stderr)
sys.exit(1)
if args.AllExtensions:
print("Error: ibcmd config import does not support -AllExtensions (use -Extension or 1cv8)", file=sys.stderr)
sys.exit(1)
arguments = ["infobase", "config", "import", "files"] + config_files
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
print(f"Running: ibcmd {' '.join(arguments)}")
result = subprocess.run([v8path] + arguments, capture_output=True, encoding="utf-8", errors="replace")
if result.returncode != 0:
print(f"Error loading changes (code: {result.returncode})", file=sys.stderr)
if result.stdout:
print(result.stdout)
if result.stderr:
print(result.stderr, file=sys.stderr)
sys.exit(result.returncode)
print(f"Changes loaded successfully ({len(config_files)} files)")
if result.stdout:
print(result.stdout)
exit_code = 0
if args.UpdateDB:
apply_args = ["infobase", "config", "apply", f"--db-path={args.InfoBasePath}", "--force"]
print(f"Running: ibcmd {' '.join(apply_args)}")
ar = subprocess.run([v8path] + apply_args, capture_output=True, encoding="utf-8", errors="replace")
exit_code = ar.returncode
if exit_code == 0:
print("Database configuration updated successfully")
else:
print(f"Error updating database configuration (code: {exit_code})", file=sys.stderr)
if ar.stdout:
print(ar.stdout)
if ar.stderr:
print(ar.stderr, file=sys.stderr)
sys.exit(exit_code)
# --- Write list file (UTF-8 with BOM) ---
list_file = os.path.join(temp_dir, "load_list.txt")
with open(list_file, "w", encoding="utf-8-sig") as f:
@@ -1,4 +1,4 @@
# db-load-xml v1.6 — Load 1C configuration from XML files
# db-load-xml v1.7 — Load 1C configuration from XML files
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
<#
.SYNOPSIS
@@ -192,12 +192,29 @@ try {
exit 1
}
if ($Mode -eq "Partial" -or $Files -or $ListFile) {
Write-Host "Error: ibcmd config import supports full-directory import only; use 1cv8 for partial/file lists" -ForegroundColor Red
exit 1
# partial: import specific files (relative to ConfigDir)
$fileList = @()
if ($ListFile) {
if (-not (Test-Path $ListFile)) {
Write-Host "Error: list file not found: $ListFile" -ForegroundColor Red
exit 1
}
$fileList = @(Get-Content -Path $ListFile -Encoding UTF8 | ForEach-Object { $_.Trim() } | Where-Object { $_ })
} elseif ($Files) {
$fileList = @($Files -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
}
if ($fileList.Count -eq 0) {
Write-Host "Error: -Files or -ListFile required for partial import" -ForegroundColor Red
exit 1
}
$arguments = @("infobase", "config", "import", "files") + $fileList
$arguments += "--base-dir=$ConfigDir", "--db-path=$InfoBasePath"
if ($Extension) { $arguments += "--extension=$Extension" }
} else {
$arguments = @("infobase", "config", "import", "--db-path=$InfoBasePath")
if ($Extension) { $arguments += "--extension=$Extension" }
$arguments += "$ConfigDir"
}
$arguments = @("infobase", "config", "import", "--db-path=$InfoBasePath")
if ($Extension) { $arguments += "--extension=$Extension" }
$arguments += "$ConfigDir"
Write-Host "Running: ibcmd $($arguments -join ' ')"
$output = & $V8Path @arguments 2>&1
$exitCode = $LASTEXITCODE
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# db-load-xml v1.6 — Load 1C configuration from XML files
# db-load-xml v1.7 — Load 1C configuration from XML files
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
@@ -136,12 +136,29 @@ def main():
print("Error: ibcmd config import does not support -AllExtensions (use -Extension or 1cv8)", file=sys.stderr)
sys.exit(1)
if args.Mode == "Partial" or args.Files or args.ListFile:
print("Error: ibcmd config import supports full-directory import only; use 1cv8 for partial/file lists", file=sys.stderr)
sys.exit(1)
arguments = ["infobase", "config", "import", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
arguments.append(args.ConfigDir)
# partial: import specific files (relative to ConfigDir)
if args.ListFile:
if not os.path.isfile(args.ListFile):
print(f"Error: list file not found: {args.ListFile}", file=sys.stderr)
sys.exit(1)
with open(args.ListFile, encoding="utf-8-sig") as f:
file_list = [ln.strip() for ln in f if ln.strip()]
elif args.Files:
file_list = [p.strip() for p in args.Files.split(",") if p.strip()]
else:
file_list = []
if not file_list:
print("Error: -Files or -ListFile required for partial import", file=sys.stderr)
sys.exit(1)
arguments = ["infobase", "config", "import", "files"] + file_list
arguments += [f"--base-dir={args.ConfigDir}", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
else:
arguments = ["infobase", "config", "import", f"--db-path={args.InfoBasePath}"]
if args.Extension:
arguments.append(f"--extension={args.Extension}")
arguments.append(args.ConfigDir)
print(f"Running: ibcmd {' '.join(arguments)}")
result = subprocess.run([v8path] + arguments, capture_output=True, encoding="utf-8", errors="replace")
if result.returncode != 0: