mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-02 17:57:45 +03:00
feat(validate): brief output by default, -Detailed for verbose
All 10 validation skills (meta, epf, skd, cf, cfe, form, mxl, role, subsystem, interface) now output a single summary line on success: === Validation OK: Type.Name (N checks) === Errors/warnings always shown. Full per-check [OK] output behind -Detailed flag. Removed all N/A check lines. Unified role-validate output format. Trimmed SKILL.md files from 42-119 to 51-67 lines. Version bumps: meta-validate v1.2, all others v1.1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
93e4130ff2
commit
422e397381
@@ -1,4 +1,4 @@
|
||||
# role-validate v1.0 — Validate 1C role structure
|
||||
# role-validate v1.1 — Validate 1C role structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -6,7 +6,11 @@ param(
|
||||
|
||||
[string]$MetadataPath,
|
||||
|
||||
[string]$OutFile
|
||||
[string]$OutFile,
|
||||
|
||||
[switch]$Detailed,
|
||||
|
||||
[int]$MaxErrors = 30
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
@@ -132,25 +136,36 @@ $script:commandRights = @("View")
|
||||
|
||||
# --- 2. Output helpers ---
|
||||
|
||||
$script:lines = @()
|
||||
$script:errors = 0
|
||||
$script:warnings = 0
|
||||
$script:okCount = 0
|
||||
$script:stopped = $false
|
||||
$script:output = New-Object System.Text.StringBuilder 8192
|
||||
|
||||
function Out-OK {
|
||||
function Out-Line {
|
||||
param([string]$msg)
|
||||
$script:lines += " OK $msg"
|
||||
$script:output.AppendLine($msg) | Out-Null
|
||||
}
|
||||
|
||||
function Out-WARN {
|
||||
function Report-OK {
|
||||
param([string]$msg)
|
||||
$script:warnings++
|
||||
$script:lines += " WARN $msg"
|
||||
$script:okCount++
|
||||
if ($Detailed) { Out-Line "[OK] $msg" }
|
||||
}
|
||||
|
||||
function Out-ERR {
|
||||
function Report-Error {
|
||||
param([string]$msg)
|
||||
$script:errors++
|
||||
$script:lines += " ERR $msg"
|
||||
Out-Line "[ERROR] $msg"
|
||||
if ($script:errors -ge $MaxErrors) {
|
||||
$script:stopped = $true
|
||||
}
|
||||
}
|
||||
|
||||
function Report-Warn {
|
||||
param([string]$msg)
|
||||
$script:warnings++
|
||||
Out-Line "[WARN] $msg"
|
||||
}
|
||||
|
||||
function Get-ObjectType {
|
||||
@@ -176,18 +191,17 @@ function Find-Similar {
|
||||
|
||||
# --- 3. Validate Rights.xml ---
|
||||
|
||||
$script:lines += "Validating: $RightsPath"
|
||||
|
||||
if (-not (Test-Path $RightsPath)) {
|
||||
Out-ERR "File not found: $RightsPath"
|
||||
$script:lines += "---"
|
||||
$script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)"
|
||||
$output = $script:lines -join "`n"
|
||||
Report-Error "File not found: $RightsPath"
|
||||
$result = $script:output.ToString()
|
||||
Write-Host $result
|
||||
if ($OutFile) {
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($OutFile, $output, $enc)
|
||||
} else {
|
||||
Write-Host $output
|
||||
$outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile }
|
||||
$outDir = [System.IO.Path]::GetDirectoryName($outPath)
|
||||
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null }
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding $true
|
||||
[System.IO.File]::WriteAllText($outPath, $result, $utf8Bom)
|
||||
Write-Host "Written to: $outPath"
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
@@ -195,17 +209,18 @@ if (-not (Test-Path $RightsPath)) {
|
||||
# 3a. Parse XML
|
||||
try {
|
||||
[xml]$xml = Get-Content -Path $RightsPath -Encoding UTF8
|
||||
Out-OK "XML well-formed"
|
||||
Report-OK "XML well-formed"
|
||||
} catch {
|
||||
Out-ERR "XML parse error: $($_.Exception.Message)"
|
||||
$script:lines += "---"
|
||||
$script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)"
|
||||
$output = $script:lines -join "`n"
|
||||
Report-Error "XML parse error: $($_.Exception.Message)"
|
||||
$result = $script:output.ToString()
|
||||
Write-Host $result
|
||||
if ($OutFile) {
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($OutFile, $output, $enc)
|
||||
} else {
|
||||
Write-Host $output
|
||||
$outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile }
|
||||
$outDir = [System.IO.Path]::GetDirectoryName($outPath)
|
||||
if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null }
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding $true
|
||||
[System.IO.File]::WriteAllText($outPath, $result, $utf8Bom)
|
||||
Write-Host "Written to: $outPath"
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
@@ -215,11 +230,11 @@ $rightsNs = "http://v8.1c.ru/8.2/roles"
|
||||
|
||||
# 3b. Check root element
|
||||
if ($root.LocalName -ne "Rights") {
|
||||
Out-ERR "Root element is '$($root.LocalName)', expected 'Rights'"
|
||||
Report-Error "Root element is '$($root.LocalName)', expected 'Rights'"
|
||||
} elseif ($root.NamespaceURI -ne $rightsNs) {
|
||||
Out-WARN "Namespace is '$($root.NamespaceURI)', expected '$rightsNs'"
|
||||
Report-Warn "Namespace is '$($root.NamespaceURI)', expected '$rightsNs'"
|
||||
} else {
|
||||
Out-OK "Root element: <Rights> with correct namespace"
|
||||
Report-OK "Root element: <Rights> with correct namespace"
|
||||
}
|
||||
|
||||
# 3c. Global flags
|
||||
@@ -230,15 +245,15 @@ foreach ($fn in $flagNames) {
|
||||
if ($node.Count -gt 0) {
|
||||
$val = $node[0].InnerText
|
||||
if ($val -ne "true" -and $val -ne "false") {
|
||||
Out-WARN "$fn = '$val' (expected 'true' or 'false')"
|
||||
Report-Warn "$fn = '$val' (expected 'true' or 'false')"
|
||||
}
|
||||
$flagsFound++
|
||||
} else {
|
||||
Out-WARN "Missing global flag: $fn"
|
||||
Report-Warn "Missing global flag: $fn"
|
||||
}
|
||||
}
|
||||
if ($flagsFound -eq 3) {
|
||||
Out-OK "3 global flags present"
|
||||
Report-OK "3 global flags present"
|
||||
}
|
||||
|
||||
# 3d. Objects
|
||||
@@ -257,7 +272,7 @@ foreach ($obj in $objects) {
|
||||
}
|
||||
|
||||
if (-not $objName) {
|
||||
Out-ERR "Object without <name>"
|
||||
Report-Error "Object without <name>"
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -266,7 +281,7 @@ foreach ($obj in $objects) {
|
||||
|
||||
# Check object type is known
|
||||
if (-not $isNested -and -not $script:knownRights.ContainsKey($objectType)) {
|
||||
Out-WARN "${objName}: unknown object type '$objectType'"
|
||||
Report-Warn "${objName}: unknown object type '$objectType'"
|
||||
}
|
||||
|
||||
# Check rights
|
||||
@@ -289,18 +304,18 @@ foreach ($obj in $objects) {
|
||||
if ($rcc.LocalName -eq "condition") { $condNode = $rcc }
|
||||
}
|
||||
if (-not $condNode -or -not $condNode.InnerText) {
|
||||
Out-WARN "${objName}: RLS condition for '$rName' is empty"
|
||||
Report-Warn "${objName}: RLS condition for '$rName' is empty"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $rName) {
|
||||
Out-ERR "${objName}: <right> without <name>"
|
||||
Report-Error "${objName}: <right> without <name>"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($rValue -ne "true" -and $rValue -ne "false") {
|
||||
Out-ERR "${objName}: right '$rName' has invalid value '$rValue'"
|
||||
Report-Error "${objName}: right '$rName' has invalid value '$rValue'"
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -310,15 +325,15 @@ foreach ($obj in $objects) {
|
||||
if ($isNested) {
|
||||
if ($objName -match '\.Command\.') {
|
||||
if ($rName -notin $script:commandRights) {
|
||||
Out-WARN "${objName}: '$rName' not valid for commands (only: View)"
|
||||
Report-Warn "${objName}: '$rName' not valid for commands (only: View)"
|
||||
}
|
||||
} elseif ($objName -match '\.IntegrationServiceChannel\.') {
|
||||
if ($rName -notin $script:channelRights) {
|
||||
Out-WARN "${objName}: '$rName' not valid for channels (only: Use)"
|
||||
Report-Warn "${objName}: '$rName' not valid for channels (only: Use)"
|
||||
}
|
||||
} else {
|
||||
if ($rName -notin $script:nestedRights) {
|
||||
Out-WARN "${objName}: '$rName' not valid for nested objects (only: View, Edit)"
|
||||
Report-Warn "${objName}: '$rName' not valid for nested objects (only: View, Edit)"
|
||||
}
|
||||
}
|
||||
} elseif ($script:knownRights.ContainsKey($objectType)) {
|
||||
@@ -326,15 +341,15 @@ foreach ($obj in $objects) {
|
||||
if ($rName -notin $validRights) {
|
||||
$similar = Find-Similar -needle $rName -haystack $validRights
|
||||
$sugStr = if ($similar.Count -gt 0) { " Did you mean: $($similar -join ', ')?" } else { "" }
|
||||
Out-WARN "${objName}: unknown right '$rName'.$sugStr"
|
||||
Report-Warn "${objName}: unknown right '$rName'.$sugStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Out-OK "$objCount objects, $rightCount rights"
|
||||
Report-OK "$objCount objects, $rightCount rights"
|
||||
if ($rlsCount -gt 0) {
|
||||
Out-OK "$rlsCount RLS restrictions"
|
||||
Report-OK "$rlsCount RLS restrictions"
|
||||
}
|
||||
|
||||
# 3e. Templates
|
||||
@@ -349,56 +364,56 @@ if ($templates.Count -gt 0) {
|
||||
if ($child.LocalName -eq "condition") { $tCond = $child.InnerText }
|
||||
}
|
||||
if (-not $tName) {
|
||||
Out-WARN "Restriction template without <name>"
|
||||
Report-Warn "Restriction template without <name>"
|
||||
} else {
|
||||
$parenIdx = $tName.IndexOf("(")
|
||||
$shortName = if ($parenIdx -gt 0) { $tName.Substring(0, $parenIdx) } else { $tName }
|
||||
$tplNames += $shortName
|
||||
}
|
||||
if (-not $tCond) {
|
||||
Out-WARN "Template '$tName': empty <condition>"
|
||||
Report-Warn "Template '$tName': empty <condition>"
|
||||
}
|
||||
}
|
||||
Out-OK "$($templates.Count) templates: $($tplNames -join ', ')"
|
||||
Report-OK "$($templates.Count) templates: $($tplNames -join ', ')"
|
||||
}
|
||||
|
||||
# --- 4. Validate metadata (optional) ---
|
||||
|
||||
if ($MetadataPath) {
|
||||
$script:lines += ""
|
||||
Out-Line ""
|
||||
|
||||
if (-not (Test-Path $MetadataPath)) {
|
||||
Out-ERR "Metadata file not found: $MetadataPath"
|
||||
Report-Error "Metadata file not found: $MetadataPath"
|
||||
} else {
|
||||
try {
|
||||
[xml]$metaXml = Get-Content -Path $MetadataPath -Encoding UTF8
|
||||
$roleNode = $metaXml.DocumentElement.SelectSingleNode("//*[local-name()='Role']")
|
||||
if (-not $roleNode) {
|
||||
Out-ERR "Metadata: <Role> element not found"
|
||||
Report-Error "Metadata: <Role> element not found"
|
||||
} else {
|
||||
$uuid = $roleNode.GetAttribute("uuid")
|
||||
if ($uuid -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
|
||||
Out-OK "Metadata: UUID valid ($uuid)"
|
||||
Report-OK "Metadata: UUID valid ($uuid)"
|
||||
} else {
|
||||
Out-ERR "Metadata: invalid UUID format '$uuid'"
|
||||
Report-Error "Metadata: invalid UUID format '$uuid'"
|
||||
}
|
||||
|
||||
$nameNode = $roleNode.SelectSingleNode(".//*[local-name()='Name']")
|
||||
if ($nameNode -and $nameNode.InnerText) {
|
||||
Out-OK "Metadata: Name = $($nameNode.InnerText)"
|
||||
Report-OK "Metadata: Name = $($nameNode.InnerText)"
|
||||
} else {
|
||||
Out-ERR "Metadata: <Name> is empty or missing"
|
||||
Report-Error "Metadata: <Name> is empty or missing"
|
||||
}
|
||||
|
||||
$synNode = $roleNode.SelectSingleNode(".//*[local-name()='Synonym']")
|
||||
if ($synNode -and $synNode.InnerXml) {
|
||||
Out-OK "Metadata: Synonym present"
|
||||
Report-OK "Metadata: Synonym present"
|
||||
} else {
|
||||
Out-WARN "Metadata: <Synonym> is empty"
|
||||
Report-Warn "Metadata: <Synonym> is empty"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Out-ERR "Metadata XML parse error: $($_.Exception.Message)"
|
||||
Report-Error "Metadata XML parse error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -425,7 +440,7 @@ if ($MetadataPath -and (Test-Path $MetadataPath)) {
|
||||
}
|
||||
|
||||
if (Test-Path $configXmlPath2) {
|
||||
$script:lines += ""
|
||||
Out-Line ""
|
||||
try {
|
||||
[xml]$cfgXml = Get-Content -Path $configXmlPath2 -Encoding UTF8
|
||||
$cfgNs = New-Object System.Xml.XmlNamespaceManager($cfgXml.NameTable)
|
||||
@@ -441,22 +456,30 @@ if (Test-Path $configXmlPath2) {
|
||||
}
|
||||
}
|
||||
if ($found) {
|
||||
Out-OK "Configuration.xml: <Role>$inferredRoleName</Role> registered"
|
||||
Report-OK "Configuration.xml: <Role>$inferredRoleName</Role> registered"
|
||||
} else {
|
||||
Out-WARN "Configuration.xml: <Role>$inferredRoleName</Role> NOT found in ChildObjects"
|
||||
Report-Warn "Configuration.xml: <Role>$inferredRoleName</Role> NOT found in ChildObjects"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Out-WARN "Configuration.xml: parse error — $($_.Exception.Message)"
|
||||
Report-Warn "Configuration.xml: parse error — $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
# --- 6. Summary ---
|
||||
|
||||
$script:lines += "---"
|
||||
$script:lines += "Result: $($script:errors) error(s), $($script:warnings) warning(s)"
|
||||
# Insert header
|
||||
$script:output.Insert(0, "=== Validation: Role.$inferredRoleName ===$([Environment]::NewLine)") | Out-Null
|
||||
|
||||
$output = $script:lines -join "`n"
|
||||
$checks = $script:okCount + $script:errors + $script:warnings
|
||||
if ($script:errors -eq 0 -and $script:warnings -eq 0 -and -not $Detailed) {
|
||||
$result = "=== Validation OK: Role.$inferredRoleName ($checks checks) ==="
|
||||
} else {
|
||||
Out-Line ""
|
||||
Out-Line "=== Result: $($script:errors) errors, $($script:warnings) warnings ($checks checks) ==="
|
||||
$result = $script:output.ToString()
|
||||
}
|
||||
Write-Host $result
|
||||
|
||||
if ($OutFile) {
|
||||
$outPath = if ([System.IO.Path]::IsPathRooted($OutFile)) { $OutFile } else { Join-Path (Get-Location) $OutFile }
|
||||
@@ -464,11 +487,9 @@ if ($OutFile) {
|
||||
if (-not (Test-Path $outDir)) {
|
||||
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
|
||||
}
|
||||
$enc = New-Object System.Text.UTF8Encoding($true)
|
||||
[System.IO.File]::WriteAllText($outPath, $output, $enc)
|
||||
Write-Host "[OK] Validation result written to: $outPath"
|
||||
} else {
|
||||
Write-Host $output
|
||||
$utf8Bom = New-Object System.Text.UTF8Encoding $true
|
||||
[System.IO.File]::WriteAllText($outPath, $result, $utf8Bom)
|
||||
Write-Host "Written to: $outPath"
|
||||
}
|
||||
|
||||
if ($script:errors -gt 0) { exit 1 } else { exit 0 }
|
||||
|
||||
Reference in New Issue
Block a user