mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-31 23:40:51 +03:00
feat(db-repo): навык работы с хранилищем конфигурации 1С
Новый навык /db-repo с подкомандами: рабочий цикл (lock, unlock, commit, update), подключение базы (connect, disconnect), история (report, dump-cfg), администрирование (create, add-user, copy-users) и сервис (set-label, optimize, clear-cache). Каскад инструкций в references/. Ядро навыка — разбор вывода платформы: код возврата о фактическом результате не говорит. Захват и помещение не атомарны (код 1 при реально захваченном объекте), а все no-op'ы дают код 0. Вердикт строится по строкам лога и отражает достижение запрошенного состояния, а не факт изменения. Захват, обновление и unlock -Force молча подтягивают свежие версии в локальную конфигурацию. Навык называет полученные объекты и печатает готовую команду перевыгрузки: без неё частичная загрузка старых исходников молча откатывает чужие изменения. Соседние навыки группы получили общий блок разрешения реквизитов хранилища из .v8-project.json — база под хранилищем не принимает без них ни одной операции конфигуратора. db-load-xml дополнительно подсказывает действие по трём сообщениям платформы, db-dump-xml получил -ObjectsFile для передачи списка объектов между навыками без перепечатывания. Порты .py следуют отдельно. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0442aa015a
commit
478e7489f9
@@ -1,4 +1,4 @@
|
||||
# db-dump-xml v1.16 — Dump 1C configuration to XML files
|
||||
# db-dump-xml v1.17 — Dump 1C configuration to XML files
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||
<#
|
||||
@@ -101,6 +101,18 @@ param(
|
||||
[ValidateSet("Hierarchical", "Plain")]
|
||||
[string]$Format = "Hierarchical",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$ObjectsFile,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$RepositoryPath,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$RepositoryUser,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$RepositoryPassword,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string[]]$AdditionalV8Arguments = @(),
|
||||
|
||||
@@ -111,6 +123,90 @@ param(
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Реквизиты хранилища из .v8-project.json ---
|
||||
# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[]
|
||||
# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc).
|
||||
function Find-V8Project([string]$startDir) {
|
||||
$d = $startDir
|
||||
for ($i = 0; $i -lt 20 -and $d; $i++) {
|
||||
$pj = Join-Path $d ".v8-project.json"
|
||||
if (Test-Path $pj) { return $pj }
|
||||
$parent = [System.IO.Path]::GetDirectoryName($d)
|
||||
if ($parent -eq $d) { break }
|
||||
$d = $parent
|
||||
}
|
||||
return $null
|
||||
}
|
||||
function Test-SamePath {
|
||||
param([string]$A, [string]$B)
|
||||
if (-not $A -or -not $B) { return $false }
|
||||
try {
|
||||
$na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/')
|
||||
$nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/')
|
||||
return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase)
|
||||
} catch { return $false }
|
||||
}
|
||||
|
||||
function Find-ProjectDatabase {
|
||||
# Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена.
|
||||
$pf = Find-V8Project (Get-Location).Path
|
||||
if (-not $pf) { return $null }
|
||||
try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null }
|
||||
if (-not $proj.databases) { return $null }
|
||||
foreach ($db in $proj.databases) {
|
||||
if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db }
|
||||
if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) {
|
||||
if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and
|
||||
$db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db }
|
||||
}
|
||||
}
|
||||
return $null
|
||||
}
|
||||
|
||||
function Resolve-RepositorySettings {
|
||||
# Возвращает @{ Path; User; Password; FromRegistry }. Явные -Repository* всегда сильнее реестра.
|
||||
$dbRec = Find-ProjectDatabase
|
||||
$rec = $null
|
||||
if ($dbRec) {
|
||||
if ($Extension) {
|
||||
# У расширения СВОЁ хранилище со своим путём (проверено): выбирается парой
|
||||
# /ConfigurationRepositoryF"<путь расширения>" + -Extension "<Имя>".
|
||||
if ($dbRec.extensions) {
|
||||
foreach ($ext in $dbRec.extensions) {
|
||||
if ($ext.name -and $ext.name.Equals($Extension, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
$rec = $ext.repository
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$rec = $dbRec.repository
|
||||
}
|
||||
}
|
||||
$path = if ($RepositoryPath) { $RepositoryPath } elseif ($rec -and $rec.path) { [string]$rec.path } else { $null }
|
||||
$user = if ($RepositoryUser) { $RepositoryUser } elseif ($rec -and $rec.user) { [string]$rec.user } else { $null }
|
||||
# Пустой пароль = отсутствующий: 1С требует опускать ключ целиком, а не передавать пустое значение.
|
||||
$pwd = if ($RepositoryPassword) { $RepositoryPassword } elseif ($rec -and $rec.password) { [string]$rec.password } else { $null }
|
||||
return @{
|
||||
Path = if ($path) { $path.Trim().Trim('"') } else { $null }
|
||||
User = $user
|
||||
Password = $pwd
|
||||
FromRegistry = [bool]($rec -and $rec.path)
|
||||
DbRecord = $dbRec
|
||||
}
|
||||
}
|
||||
|
||||
function Get-RepositoryArgs {
|
||||
# Ключи доступа к хранилищу. Форма — кавычки ВНУТРИ токена, как у /N и /P.
|
||||
param([hashtable]$Repo)
|
||||
$a = @()
|
||||
if (-not $Repo -or -not $Repo.Path) { return $a }
|
||||
$a += "/ConfigurationRepositoryF`"$($Repo.Path)`""
|
||||
if ($Repo.User) { $a += "/ConfigurationRepositoryN`"$($Repo.User)`"" }
|
||||
if ($Repo.Password) { $a += "/ConfigurationRepositoryP`"$($Repo.Password)`"" }
|
||||
return ,$a
|
||||
}
|
||||
|
||||
function Protect-Secrets {
|
||||
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
||||
param([string]$Text, [string[]]$Secrets)
|
||||
@@ -132,7 +228,7 @@ $script:IbcmdOwnedKeys = @(
|
||||
'--import', '--export', '--apply', '--force', '--create-database',
|
||||
'--user', '--password'
|
||||
)
|
||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
|
||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP', '/ConfigurationRepositoryP')
|
||||
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
||||
|
||||
function Test-ArgKeyMatch {
|
||||
@@ -415,8 +511,19 @@ if ($engine -eq "ibcmd") {
|
||||
}
|
||||
|
||||
# --- Validate Partial mode ---
|
||||
# Список объектов приходит либо строкой, либо файлом: файл нужен, чтобы не перепечатывать
|
||||
# то, что уже напечатал другой навык (например /db-repo update со списком полученных объектов).
|
||||
if ($ObjectsFile) {
|
||||
if (-not (Test-Path $ObjectsFile)) {
|
||||
Write-Host "Error: -ObjectsFile not found: $ObjectsFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
$fromFile = @([System.IO.File]::ReadAllLines($ObjectsFile, [System.Text.Encoding]::UTF8) |
|
||||
ForEach-Object { $_.Trim() } | Where-Object { $_ -and -not $_.StartsWith('#') })
|
||||
$Objects = (@(@($Objects -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ }) + $fromFile) -join ',')
|
||||
}
|
||||
if ($Mode -eq "Partial" -and -not $Objects) {
|
||||
Write-Host "Error: -Objects required for Partial mode" -ForegroundColor Red
|
||||
Write-Host "Error: -Objects or -ObjectsFile required for Partial mode" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -486,6 +593,11 @@ try {
|
||||
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
||||
if ($Password) { $arguments += "/P`"$Password`"" }
|
||||
|
||||
# База под хранилищем не примет НИ ОДНОЙ операции конфигуратора без этих реквизитов, а для
|
||||
# базы вне хранилища они безвредны — поэтому подставляем всегда, когда они известны.
|
||||
$__repo = Resolve-RepositorySettings
|
||||
$arguments += Get-RepositoryArgs $__repo
|
||||
|
||||
$arguments += "/DumpConfigToFiles", "`"$ConfigDir`""
|
||||
$arguments += "-Format", $Format
|
||||
|
||||
@@ -530,7 +642,7 @@ try {
|
||||
$arguments += $extraArgs
|
||||
|
||||
# --- Execute ---
|
||||
Write-Host "Running: 1cv8.exe $(Protect-Secrets ((Format-ArgsForDisplay $arguments $engine) -join ' ') @($Password, $UserName))"
|
||||
Write-Host "Running: 1cv8.exe $(Protect-Secrets ((Format-ArgsForDisplay $arguments $engine) -join ' ') @($Password, $UserName, $__repo.Password))"
|
||||
$__v8 = Invoke-PlatformProcess $V8Path $arguments -PreQuoted
|
||||
$exitCode = $__v8.ExitCode
|
||||
|
||||
|
||||
Reference in New Issue
Block a user