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
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -101,6 +101,18 @@ param(
|
|||||||
[ValidateSet("Hierarchical", "Plain")]
|
[ValidateSet("Hierarchical", "Plain")]
|
||||||
[string]$Format = "Hierarchical",
|
[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)]
|
[Parameter(Mandatory=$false)]
|
||||||
[string[]]$AdditionalV8Arguments = @(),
|
[string[]]$AdditionalV8Arguments = @(),
|
||||||
|
|
||||||
@@ -111,6 +123,90 @@ param(
|
|||||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
[Console]::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 {
|
function Protect-Secrets {
|
||||||
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
||||||
param([string]$Text, [string[]]$Secrets)
|
param([string]$Text, [string[]]$Secrets)
|
||||||
@@ -132,7 +228,7 @@ $script:IbcmdOwnedKeys = @(
|
|||||||
'--import', '--export', '--apply', '--force', '--create-database',
|
'--import', '--export', '--apply', '--force', '--create-database',
|
||||||
'--user', '--password'
|
'--user', '--password'
|
||||||
)
|
)
|
||||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
|
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP', '/ConfigurationRepositoryP')
|
||||||
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
||||||
|
|
||||||
function Test-ArgKeyMatch {
|
function Test-ArgKeyMatch {
|
||||||
@@ -415,8 +511,19 @@ if ($engine -eq "ibcmd") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# --- Validate Partial mode ---
|
# --- 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) {
|
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
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +593,11 @@ try {
|
|||||||
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
||||||
if ($Password) { $arguments += "/P`"$Password`"" }
|
if ($Password) { $arguments += "/P`"$Password`"" }
|
||||||
|
|
||||||
|
# База под хранилищем не примет НИ ОДНОЙ операции конфигуратора без этих реквизитов, а для
|
||||||
|
# базы вне хранилища они безвредны — поэтому подставляем всегда, когда они известны.
|
||||||
|
$__repo = Resolve-RepositorySettings
|
||||||
|
$arguments += Get-RepositoryArgs $__repo
|
||||||
|
|
||||||
$arguments += "/DumpConfigToFiles", "`"$ConfigDir`""
|
$arguments += "/DumpConfigToFiles", "`"$ConfigDir`""
|
||||||
$arguments += "-Format", $Format
|
$arguments += "-Format", $Format
|
||||||
|
|
||||||
@@ -530,7 +642,7 @@ try {
|
|||||||
$arguments += $extraArgs
|
$arguments += $extraArgs
|
||||||
|
|
||||||
# --- Execute ---
|
# --- 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
|
$__v8 = Invoke-PlatformProcess $V8Path $arguments -PreQuoted
|
||||||
$exitCode = $__v8.ExitCode
|
$exitCode = $__v8.ExitCode
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# db-load-git v1.21 — Load Git changes into 1C database
|
# db-load-git v1.22 — Load Git changes into 1C database
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -116,6 +116,15 @@ param(
|
|||||||
# но в логе есть отбраковка.
|
# но в логе есть отбраковка.
|
||||||
[switch]$StrictLog,
|
[switch]$StrictLog,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryUser,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPassword,
|
||||||
|
|
||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
[string[]]$AdditionalV8Arguments = @(),
|
[string[]]$AdditionalV8Arguments = @(),
|
||||||
|
|
||||||
@@ -126,6 +135,90 @@ param(
|
|||||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
[Console]::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 {
|
function Protect-Secrets {
|
||||||
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
||||||
param([string]$Text, [string[]]$Secrets)
|
param([string]$Text, [string[]]$Secrets)
|
||||||
@@ -147,7 +240,7 @@ $script:IbcmdOwnedKeys = @(
|
|||||||
'--import', '--export', '--apply', '--force', '--create-database',
|
'--import', '--export', '--apply', '--force', '--create-database',
|
||||||
'--user', '--password'
|
'--user', '--password'
|
||||||
)
|
)
|
||||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
|
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP', '/ConfigurationRepositoryP')
|
||||||
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
||||||
|
|
||||||
function Test-ArgKeyMatch {
|
function Test-ArgKeyMatch {
|
||||||
@@ -668,6 +761,11 @@ try {
|
|||||||
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
||||||
if ($Password) { $arguments += "/P`"$Password`"" }
|
if ($Password) { $arguments += "/P`"$Password`"" }
|
||||||
|
|
||||||
|
# База под хранилищем не примет НИ ОДНОЙ операции конфигуратора без этих реквизитов, а для
|
||||||
|
# базы вне хранилища они безвредны — поэтому подставляем всегда, когда они известны.
|
||||||
|
$__repo = Resolve-RepositorySettings
|
||||||
|
$arguments += Get-RepositoryArgs $__repo
|
||||||
|
|
||||||
$arguments += "/LoadConfigFromFiles", "`"$ConfigDir`""
|
$arguments += "/LoadConfigFromFiles", "`"$ConfigDir`""
|
||||||
$arguments += "-listFile", "`"$listFile`""
|
$arguments += "-listFile", "`"$listFile`""
|
||||||
$arguments += "-Format", $Format
|
$arguments += "-Format", $Format
|
||||||
@@ -695,7 +793,7 @@ try {
|
|||||||
# --- Execute ---
|
# --- Execute ---
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
Write-Host "Executing partial configuration load..."
|
Write-Host "Executing partial configuration load..."
|
||||||
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
|
$__v8 = Invoke-PlatformProcess $V8Path $arguments -PreQuoted
|
||||||
$exitCode = $__v8.ExitCode
|
$exitCode = $__v8.ExitCode
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# db-load-xml v1.22 — Load 1C configuration from XML files
|
# db-load-xml v1.23 — Load 1C configuration from XML files
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -110,6 +110,15 @@ param(
|
|||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
[switch]$StrictLog,
|
[switch]$StrictLog,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryUser,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPassword,
|
||||||
|
|
||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
[string[]]$AdditionalV8Arguments = @(),
|
[string[]]$AdditionalV8Arguments = @(),
|
||||||
|
|
||||||
@@ -120,6 +129,114 @@ param(
|
|||||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
[Console]::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 Write-RepositoryHints {
|
||||||
|
param([string]$LogText)
|
||||||
|
if (-not $LogText) { return }
|
||||||
|
if ($LogText -match 'текущая конфигурация помещена в хранилище') {
|
||||||
|
Write-Host "[hint] полная загрузка в базу, подключённую к хранилищу, невозможна." -ForegroundColor Yellow
|
||||||
|
Write-Host " Используйте -Mode Partial, предварительно захватив объекты: /db-repo lock" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
foreach ($m in [regex]::Matches($LogText, 'объект метаданных ([^\s]+) не захвачен в хранилище')) {
|
||||||
|
$obj = $m.Groups[1].Value
|
||||||
|
if ($obj -eq 'Configuration') {
|
||||||
|
Write-Host "[hint] не захвачен корень конфигурации — он нужен, чтобы добавить или удалить объект:" -ForegroundColor Yellow
|
||||||
|
Write-Host " /db-repo lock <база> -Objects `"Конфигурация`"" -ForegroundColor Yellow
|
||||||
|
} else {
|
||||||
|
Write-Host "[hint] объект не захвачен в хранилище: /db-repo lock <база> -Objects `"$obj`"" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($LogText -match 'Соединение с хранилищем конфигурации не установлено') {
|
||||||
|
Write-Host "[hint] база подключена к хранилищу, но его реквизиты неизвестны." -ForegroundColor Yellow
|
||||||
|
Write-Host " Добавьте `"repository`" в запись базы в .v8-project.json (см. /db-list)." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Protect-Secrets {
|
function Protect-Secrets {
|
||||||
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
||||||
param([string]$Text, [string[]]$Secrets)
|
param([string]$Text, [string[]]$Secrets)
|
||||||
@@ -158,7 +275,7 @@ $script:IbcmdOwnedKeys = @(
|
|||||||
'--import', '--export', '--apply', '--force', '--create-database',
|
'--import', '--export', '--apply', '--force', '--create-database',
|
||||||
'--user', '--password'
|
'--user', '--password'
|
||||||
)
|
)
|
||||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
|
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP', '/ConfigurationRepositoryP')
|
||||||
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
||||||
|
|
||||||
function Test-ArgKeyMatch {
|
function Test-ArgKeyMatch {
|
||||||
@@ -567,6 +684,11 @@ try {
|
|||||||
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
||||||
if ($Password) { $arguments += "/P`"$Password`"" }
|
if ($Password) { $arguments += "/P`"$Password`"" }
|
||||||
|
|
||||||
|
# База под хранилищем не примет НИ ОДНОЙ операции конфигуратора без этих реквизитов, а для
|
||||||
|
# базы вне хранилища они безвредны — поэтому подставляем всегда, когда они известны.
|
||||||
|
$__repo = Resolve-RepositorySettings
|
||||||
|
$arguments += Get-RepositoryArgs $__repo
|
||||||
|
|
||||||
$arguments += "/LoadConfigFromFiles", "`"$ConfigDir`""
|
$arguments += "/LoadConfigFromFiles", "`"$ConfigDir`""
|
||||||
|
|
||||||
if ($Mode -eq "Full") {
|
if ($Mode -eq "Full") {
|
||||||
@@ -631,7 +753,7 @@ try {
|
|||||||
$arguments += $extraArgs
|
$arguments += $extraArgs
|
||||||
|
|
||||||
# --- Execute ---
|
# --- 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
|
$__v8 = Invoke-PlatformProcess $V8Path $arguments -PreQuoted
|
||||||
$exitCode = $__v8.ExitCode
|
$exitCode = $__v8.ExitCode
|
||||||
|
|
||||||
@@ -660,6 +782,7 @@ try {
|
|||||||
Write-Host "--- End ---"
|
Write-Host "--- End ---"
|
||||||
}
|
}
|
||||||
Write-PlatformOutput $__v8.Output
|
Write-PlatformOutput $__v8.Output
|
||||||
|
Write-RepositoryHints $logContent
|
||||||
|
|
||||||
# Причину не называем: строки лога печатаются следом и говорят за себя, а класс проблемы
|
# Причину не называем: строки лога печатаются следом и говорят за себя, а класс проблемы
|
||||||
# разный — от отброшенного свойства до нерабочей на этой платформе конфигурации. Подсказку
|
# разный — от отброшенного свойства до нерабочей на этой платформе конфигурации. Подсказку
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
---
|
||||||
|
name: db-repo
|
||||||
|
description: Работа с хранилищем конфигурации 1С. Используй когда нужно захватить объекты, поместить изменения в хранилище конфигурации, получить изменения из него, подключить базу к хранилищу
|
||||||
|
argument-hint: <lock|unlock|commit|update> [database] -Objects "<объекты>"
|
||||||
|
allowed-tools:
|
||||||
|
- Bash
|
||||||
|
- Read
|
||||||
|
- Glob
|
||||||
|
- AskUserQuestion
|
||||||
|
---
|
||||||
|
|
||||||
|
# /db-repo — Хранилище конфигурации 1С
|
||||||
|
|
||||||
|
Захват и помещение объектов, получение изменений, подключение базы, история версий,
|
||||||
|
администрирование хранилища.
|
||||||
|
|
||||||
|
> Хранилище конфигурации 1С, а не Git-репозиторий.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/db-repo lock [database] -Objects "Справочник.Номенклатура"
|
||||||
|
/db-repo commit [database] -Objects "Справочник.Номенклатура" -Comment "Добавлен Артикул"
|
||||||
|
/db-repo unlock [database] -Objects "Справочник.Номенклатура"
|
||||||
|
/db-repo update [database]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Порядок работы
|
||||||
|
|
||||||
|
В базу, подключённую к хранилищу, исходники грузятся **только частично** и **только по захваченным**
|
||||||
|
объектам. Выполняй строго по шагам:
|
||||||
|
|
||||||
|
```
|
||||||
|
1. /db-repo lock <база> -Objects "Справочник.Номенклатура"
|
||||||
|
2. если шаг 1 сообщил о полученных из хранилища объектах — выгрузи их заново:
|
||||||
|
/db-dump-xml <база> <каталог> -Mode Partial -ObjectsFile "<файл из вывода шага 1>"
|
||||||
|
3. правки в исходниках
|
||||||
|
4. /db-load-xml <каталог> <база> -Mode Partial -Files "Catalogs/Номенклатура.xml,…"
|
||||||
|
5. /db-update <база>
|
||||||
|
6. /db-repo commit <база> -Objects "Справочник.Номенклатура" -Comment "…"
|
||||||
|
```
|
||||||
|
|
||||||
|
Шаг 2 пропускать нельзя: захват подтягивает из хранилища свежие версии, и загрузка исходников,
|
||||||
|
снятых до захвата, откатит чужие изменения — молча, без ошибки.
|
||||||
|
|
||||||
|
**Добавляешь новый объект** — захватывай его вместе с корнем конфигурации:
|
||||||
|
`-Objects "Конфигурация,Справочник.Склады"`.
|
||||||
|
|
||||||
|
**Правишь подчинённый объект** — форму, макет, команду, реквизит — захватывай именно его:
|
||||||
|
`-Objects "Справочник.Номенклатура.Форма.ФормаЭлемента"`. Захват объекта его подчинённые объекты
|
||||||
|
не захватывает. Захватывай минимум того, что правишь: чем шире захват, тем больше конфликтов
|
||||||
|
с коллегами.
|
||||||
|
|
||||||
|
## Параметры подключения
|
||||||
|
|
||||||
|
Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу:
|
||||||
|
1. Если пользователь указал параметры подключения — используй напрямую
|
||||||
|
2. Если указал базу по имени — ищи по id / alias / name
|
||||||
|
3. Если не указал — сопоставь текущую ветку Git с `databases[].branches`
|
||||||
|
4. Если ветка не совпала — используй `default`
|
||||||
|
|
||||||
|
Реквизиты хранилища передавать не нужно — они берутся из `repository` записи базы. Задать их явно
|
||||||
|
можно параметрами `-Repository*`.
|
||||||
|
|
||||||
|
## Команда
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" <подкоманда> <параметры>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Рабочий цикл
|
||||||
|
|
||||||
|
| Подкоманда | Что делает |
|
||||||
|
|------------|------------|
|
||||||
|
| `lock` | Захватить объекты |
|
||||||
|
| `unlock` | Отменить захват |
|
||||||
|
| `commit` | Поместить изменения в хранилище |
|
||||||
|
| `update` | Получить изменения из хранилища |
|
||||||
|
|
||||||
|
### Параметры
|
||||||
|
|
||||||
|
| Параметр | Обязательный | Описание |
|
||||||
|
|----------|:------------:|----------|
|
||||||
|
| `-InfoBasePath <путь>` | * | Файловая база |
|
||||||
|
| `-InfoBaseServer <сервер>` | * | Сервер 1С |
|
||||||
|
| `-InfoBaseRef <имя>` | * | Имя базы на сервере |
|
||||||
|
| `-UserName <имя>` | нет | Пользователь базы |
|
||||||
|
| `-Password <пароль>` | нет | Пароль пользователя базы |
|
||||||
|
| `-Objects <список>` | нет | Объекты через запятую. Без него — вся конфигурация |
|
||||||
|
| `-ObjectsFile <путь>` | нет | Файл со списком объектов, одно имя на строку |
|
||||||
|
| `-WithChildren` | нет | Вместе с подчинёнными объектами на полную глубину |
|
||||||
|
| `-Comment <текст>` | нет | Комментарий к помещению (`commit`). Многострочный — как есть, с переводами строк |
|
||||||
|
| `-KeepLocked` | нет | Оставить объекты захваченными после помещения |
|
||||||
|
| `-Revised` | нет | Получать захваченные объекты, если потребуется |
|
||||||
|
| `-Force` | нет | Разное по подкомандам — см. ниже |
|
||||||
|
| `-Extension <имя>` | нет | Работать с хранилищем расширения |
|
||||||
|
| `-RepositoryPath <путь>` | нет | Хранилище явно, вместо реестра |
|
||||||
|
| `-RepositoryUser <имя>` | нет | Пользователь хранилища явно |
|
||||||
|
| `-RepositoryPassword <пароль>` | нет | Пароль пользователя хранилища явно |
|
||||||
|
| `-AdditionalV8Arguments <список>` | нет | Доп. аргументы `1cv8.exe` через запятую |
|
||||||
|
|
||||||
|
> `*` — нужен либо `-InfoBasePath`, либо пара `-InfoBaseServer` + `-InfoBaseRef`
|
||||||
|
|
||||||
|
### `-Force`
|
||||||
|
|
||||||
|
| Подкоманда | Что делает |
|
||||||
|
|------------|------------|
|
||||||
|
| `unlock` | **Теряет локальные правки**: объекты перезаписываются версией из хранилища |
|
||||||
|
| `commit` | Пытается очистить ссылки на удалённые объекты вместо ошибки |
|
||||||
|
| `update` | Подтверждает добавление и удаление объектов конфигурации |
|
||||||
|
|
||||||
|
### Имена объектов
|
||||||
|
|
||||||
|
Объект — `Справочник.Номенклатура`. Подчинённый объект — полным путём:
|
||||||
|
`Документ.ЗаказПокупателя.Форма.ФормаДокумента`, `Справочник.Номенклатура.Макет.Печать`.
|
||||||
|
Корень конфигурации — `Конфигурация`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Код возврата отражает, достигнуто ли запрошенное состояние, а не факт изменения: «уже захвачено»,
|
||||||
|
«обновлять нечего», «помещать нечего» — это успех. Частичный захват тоже успех: захваченное можно
|
||||||
|
править.
|
||||||
|
|
||||||
|
**Всегда читай текст вывода, а не только код.** Под нулевым кодом приходят «новая версия не
|
||||||
|
создана», «часть объектов занята другими» и список полученных из хранилища объектов, требующий
|
||||||
|
перевыгрузки.
|
||||||
|
|
||||||
|
## Требуют подтверждения пользователя
|
||||||
|
|
||||||
|
Перед этими операциями **спроси подтверждение** — они необратимы:
|
||||||
|
|
||||||
|
| Операция | Что теряется |
|
||||||
|
|----------|--------------|
|
||||||
|
| `unlock -Force` | Локальные правки захваченных объектов |
|
||||||
|
| `disconnect` | Подключение базы к хранилищу, в том числе на стороне хранилища |
|
||||||
|
| `connect -ForceReplaceCfg` | Конфигурация базы заменяется конфигурацией из хранилища |
|
||||||
|
|
||||||
|
`update` не выполнится, если у базы в реестре не объявлено `repository`, а реквизиты не заданы
|
||||||
|
явно: на неподключённой к хранилищу базе эта команда заменяет всю конфигурацию его содержимым и
|
||||||
|
рапортует успех.
|
||||||
|
|
||||||
|
## Расширения
|
||||||
|
|
||||||
|
У расширения своё хранилище со своим путём. Укажи `-Extension "<Имя>"` — реквизиты возьмутся из
|
||||||
|
`extensions[].repository` записи базы. Подкоманды работают одинаково для основной конфигурации и
|
||||||
|
для расширения.
|
||||||
|
|
||||||
|
## Остальные задачи
|
||||||
|
|
||||||
|
| Файл | Про что |
|
||||||
|
|------|---------|
|
||||||
|
| [connect.md](references/connect.md) | Подключение и отключение базы от хранилища |
|
||||||
|
| [history.md](references/history.md) | История версий, отчёт, выгрузка версии в CF |
|
||||||
|
| [admin.md](references/admin.md) | Создание хранилища, пользователи и права |
|
||||||
|
| [service.md](references/service.md) | Метки версий, оптимизация, очистка кеша |
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Захватить справочник вместе с подчинёнными объектами
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" lock -InfoBasePath "C:\Bases\MyDB" -Objects "Справочник.Номенклатура" -WithChildren
|
||||||
|
|
||||||
|
# Захватить корень вместе с новым объектом
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" lock -InfoBasePath "C:\Bases\MyDB" -Objects "Конфигурация,Справочник.Склады"
|
||||||
|
|
||||||
|
# Поместить с комментарием, оставив захват
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" commit -InfoBasePath "C:\Bases\MyDB" -Objects "Справочник.Номенклатура" -Comment "Добавлен реквизит Артикул" -KeepLocked
|
||||||
|
|
||||||
|
# Получить изменения из хранилища
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" update -InfoBasePath "C:\Bases\MyDB"
|
||||||
|
|
||||||
|
# Серверная база, расширение
|
||||||
|
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/db-repo.ps1" lock -InfoBaseServer "srv01" -InfoBaseRef "MyApp_Dev" -UserName "Admin" -Extension "МоёРасширение" -Objects "Справочник.Номенклатура"
|
||||||
|
```
|
||||||
|
|
||||||
|
## После выполнения
|
||||||
|
|
||||||
|
- `lock` или `update` сообщил о полученных объектах — выполни `/db-dump-xml -Mode Partial` с
|
||||||
|
указанным в выводе файлом, и только потом правь исходники
|
||||||
|
- после `lock` правки идут через `/db-load-xml -Mode Partial` и `/db-update`
|
||||||
|
- изменения готовы — предложи `/db-repo commit` с комментарием
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Администрирование хранилища
|
||||||
|
|
||||||
|
## create — создать хранилище
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 create -InfoBasePath "C:\Bases\MyDB" -RepositoryPath "C:\Repo\MyApp" -RepositoryUser "Admin" -RepositoryPassword "…"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `-NoBind` | Не подключать базу к созданному хранилищу |
|
||||||
|
| `-AllowConfigurationChanges` | Включить возможность изменения, если конфигурация на поддержке без неё |
|
||||||
|
| `-ChangesAllowedRule <правило>` | Правило для объектов, изменения которых разрешены поставщиком |
|
||||||
|
| `-ChangesNotRecommendedRule <правило>` | То же для «изменения не рекомендуются» |
|
||||||
|
|
||||||
|
Правила: `ObjectNotEditable`, `ObjectIsEditableSupportEnabled`, `ObjectNotSupported`.
|
||||||
|
|
||||||
|
Без `-NoBind` база сразу подключается к созданному хранилищу. Создание — это версия 1.
|
||||||
|
|
||||||
|
Для расширения: `-Extension "<Имя>"` и отдельный путь — у расширения своё хранилище.
|
||||||
|
|
||||||
|
## add-user — создать пользователя
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 add-user -InfoBasePath "C:\Bases\MyDB" -NewUser "Ivanov" -NewUserPassword "…" -Rights LockObjects
|
||||||
|
```
|
||||||
|
|
||||||
|
| Право | Что даёт |
|
||||||
|
|-------|----------|
|
||||||
|
| `ReadOnly` | Просмотр |
|
||||||
|
| `LockObjects` | Захват объектов |
|
||||||
|
| `ManageConfigurationVersions` | Изменение состава версий |
|
||||||
|
| `Administration` | Административные функции |
|
||||||
|
|
||||||
|
`-RestoreDeletedUser` — восстановить одноимённого удалённого. Если пользователь с таким именем
|
||||||
|
существует, он **не** будет добавлен. Выполняющий должен иметь административные права.
|
||||||
|
|
||||||
|
## copy-users — скопировать пользователей из другого хранилища
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 copy-users -InfoBasePath "C:\Bases\MyDB" -SourcePath "\\srv01\repo\Other" -SourceUser "Admin" -SourcePassword "…"
|
||||||
|
```
|
||||||
|
|
||||||
|
`-SourcePath`, `-SourceUser`, `-SourcePassword` описывают хранилище-**источник**. Удалённые пользователи
|
||||||
|
не копируются; существующие не перезаписываются.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# Подключение базы к хранилищу
|
||||||
|
|
||||||
|
## connect — подключить
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 connect -InfoBasePath "C:\Bases\MyDB" -RepositoryPath "\\srv01\repo\MyApp" -RepositoryUser "Ivanov" -RepositoryPassword "…"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `-ForceReplaceCfg` | Конфигурация базы непустая — подтвердить замену её конфигурацией из хранилища. **Спроси подтверждение у пользователя** |
|
||||||
|
| `-ForceBindAlreadyBindedUser` | Подключить, даже если у этого пользователя уже есть конфигурация, связанная с хранилищем |
|
||||||
|
|
||||||
|
На пустой базе `-ForceReplaceCfg` не нужен.
|
||||||
|
|
||||||
|
После подключения добавь `repository` в запись базы в `.v8-project.json` — иначе остальные
|
||||||
|
подкоманды придётся каждый раз звать с явными реквизитами, а `update` откажется работать.
|
||||||
|
|
||||||
|
## disconnect — отключить
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 disconnect -InfoBasePath "C:\Bases\MyDB"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Спроси подтверждение у пользователя.** Операция необратима. Если пользователь аутентифицируется в хранилище, отключение отражается и
|
||||||
|
в самом хранилище: запись о подключении удаляется.
|
||||||
|
|
||||||
|
При наличии захваченных и изменённых объектов операция не выполнится. `-Force` игнорирует это
|
||||||
|
(изменения теряются) и заодно пропускает диалог аутентификации.
|
||||||
|
|
||||||
|
## Расширения
|
||||||
|
|
||||||
|
У расширения своё хранилище: `-Extension "<Имя>"` вместе с путём именно к нему. Неверная пара
|
||||||
|
«путь основного хранилища + `-Extension`» даёт ошибку `Конфигурация связана с другим хранилищем`
|
||||||
|
и ничего не ломает.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# История версий хранилища
|
||||||
|
|
||||||
|
## report — отчёт по версиям
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 report -InfoBasePath "C:\Bases\MyDB" -OutputFile "C:\tmp\repo.txt"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Параметр | Описание |
|
||||||
|
|----------|----------|
|
||||||
|
| `-OutputFile <путь>` | Куда положить отчёт (обязателен) |
|
||||||
|
| `-NBegin <номер>` | С какой версии. `-1` — только последняя |
|
||||||
|
| `-NEnd <номер>` | По какую версию |
|
||||||
|
| `-DateBegin` / `-DateEnd` | Границы по датам |
|
||||||
|
| `-GroupByObject` | Группировать по объектам |
|
||||||
|
| `-GroupByComment` | Группировать по комментарию |
|
||||||
|
| `-ReportFormat <txt\|mxl>` | По умолчанию `txt` |
|
||||||
|
|
||||||
|
`txt` — табуляция-разделённый и легко читается:
|
||||||
|
|
||||||
|
```
|
||||||
|
Версия: 2
|
||||||
|
Пользователь: Admin
|
||||||
|
Дата создания: 22.08.2026
|
||||||
|
Комментарий:
|
||||||
|
Добавлен реквизит Вес
|
||||||
|
|
||||||
|
Изменены 2
|
||||||
|
Справочник.Номенклатура
|
||||||
|
```
|
||||||
|
|
||||||
|
> На боевом хранилище полный отчёт строить не надо — тысячи версий. Нужна головная версия —
|
||||||
|
> `-NBegin -1`.
|
||||||
|
|
||||||
|
## dump-cfg — выгрузить версию в CF
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 dump-cfg -InfoBasePath "C:\Bases\MyDB" -OutputFile "C:\tmp\v120.cf" -Version 120
|
||||||
|
```
|
||||||
|
|
||||||
|
Без `-Version` (или при `-1`) выгружается последняя версия.
|
||||||
|
|
||||||
|
## Сравнить версию с текущей конфигурацией
|
||||||
|
|
||||||
|
```
|
||||||
|
/db-repo dump-cfg … -OutputFile v120.cf -Version 120
|
||||||
|
/db-create <временная база>
|
||||||
|
/db-load-cf v120.cf <временная база>
|
||||||
|
/db-dump-xml <временная база> <каталог> → diff с рабочими исходниками
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Сервисные операции
|
||||||
|
|
||||||
|
## set-label — метка на версию
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 set-label -InfoBasePath "C:\Bases\MyDB" -Label "Релиз 1.2" -Version 120 -Comment "Передано в тест"
|
||||||
|
```
|
||||||
|
|
||||||
|
Без `-Version` метка ставится на последнюю версию. Несуществующая версия — ошибка.
|
||||||
|
|
||||||
|
## optimize — оптимизация хранения
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 optimize -InfoBasePath "C:\Bases\MyDB"
|
||||||
|
```
|
||||||
|
|
||||||
|
Оптимизирует хранение данных в хранилище. Операция долгая.
|
||||||
|
|
||||||
|
## clear-cache — очистка кеша
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
... db-repo.ps1 clear-cache -InfoBasePath "C:\Bases\MyDB" -CacheScope local
|
||||||
|
```
|
||||||
|
|
||||||
|
| `-CacheScope` | Что чистит |
|
||||||
|
|---------------|------------|
|
||||||
|
| `local` (по умолчанию) | Локальный кеш версий конфигурации |
|
||||||
|
| `global` | Глобальный кеш версий |
|
||||||
|
| `db` | Локальную базу данных хранилища |
|
||||||
|
|
||||||
|
Пригождается, когда хранилище ведёт себя странно после сбоя сети или отката версии.
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
# db-update v1.16 — Update 1C database configuration
|
# db-update v1.17 — Update 1C database configuration
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
# NB: *nix-раскладку платформы (/opt/1cv8/<ver>/1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется.
|
||||||
<#
|
<#
|
||||||
@@ -97,6 +97,15 @@ param(
|
|||||||
# но в логе есть отбраковка.
|
# но в логе есть отбраковка.
|
||||||
[switch]$StrictLog,
|
[switch]$StrictLog,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryUser,
|
||||||
|
|
||||||
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$RepositoryPassword,
|
||||||
|
|
||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
[string[]]$AdditionalV8Arguments = @(),
|
[string[]]$AdditionalV8Arguments = @(),
|
||||||
|
|
||||||
@@ -107,6 +116,90 @@ param(
|
|||||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
[Console]::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 {
|
function Protect-Secrets {
|
||||||
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
# Redact literal secret values from a display string (String.Replace is literal, not regex).
|
||||||
param([string]$Text, [string[]]$Secrets)
|
param([string]$Text, [string[]]$Secrets)
|
||||||
@@ -145,7 +238,7 @@ $script:IbcmdOwnedKeys = @(
|
|||||||
'--import', '--export', '--apply', '--force', '--create-database',
|
'--import', '--export', '--apply', '--force', '--create-database',
|
||||||
'--user', '--password'
|
'--user', '--password'
|
||||||
)
|
)
|
||||||
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP')
|
$script:V8SecretKeys = @('/P', '/UC', '/WSP', '/AWSP', '/ConfigurationRepositoryP')
|
||||||
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
$script:IbcmdSecretKeys = @('--password', '--token', '--db-pwd')
|
||||||
|
|
||||||
function Test-ArgKeyMatch {
|
function Test-ArgKeyMatch {
|
||||||
@@ -499,6 +592,11 @@ try {
|
|||||||
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
if ($UserName) { $arguments += "/N`"$UserName`"" }
|
||||||
if ($Password) { $arguments += "/P`"$Password`"" }
|
if ($Password) { $arguments += "/P`"$Password`"" }
|
||||||
|
|
||||||
|
# База под хранилищем не примет НИ ОДНОЙ операции конфигуратора без этих реквизитов, а для
|
||||||
|
# базы вне хранилища они безвредны — поэтому подставляем всегда, когда они известны.
|
||||||
|
$__repo = Resolve-RepositorySettings
|
||||||
|
$arguments += Get-RepositoryArgs $__repo
|
||||||
|
|
||||||
$arguments += "/UpdateDBCfg"
|
$arguments += "/UpdateDBCfg"
|
||||||
|
|
||||||
# --- Options ---
|
# --- Options ---
|
||||||
@@ -526,7 +624,7 @@ try {
|
|||||||
$arguments += $extraArgs
|
$arguments += $extraArgs
|
||||||
|
|
||||||
# --- Execute ---
|
# --- 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
|
$__v8 = Invoke-PlatformProcess $V8Path $arguments -PreQuoted
|
||||||
$exitCode = $__v8.ExitCode
|
$exitCode = $__v8.ExitCode
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user