param( [string]$Server = $env:ONEC_SQL_SERVER, [string]$Database = $env:ONEC_SQL_DATABASE, [string]$User = $env:ONEC_SQL_USER, [string]$Password = $env:ONEC_SQL_PASSWORD, [string]$OutputRoot = "reports\1c-sql\saved-state-watch", [string]$PreviousReport = "", [string]$BaseMetadataDir = "reports\1c-sql\upo\structured-metadata-all-kinds", [string]$ExtensionGuidIndex = "reports\1c-sql\upo\xml-guid-index-extensions.json", [string]$ExtensionManifestSummary = "reports\1c-sql\upo\extension-manifest-xml-part-summary.json", [string]$ConfigCASAllDir = "reports\1c-sql\upo\ConfigCAS-all", [string]$Python = "python", [switch]$SkipMarkdown, [switch]$SkipDelta ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" if (-not $Server) { throw "Server is required. Use -Server or ONEC_SQL_SERVER." } if (-not $Database) { throw "Database is required. Use -Database or ONEC_SQL_DATABASE." } if (-not $User) { throw "User is required. Use -User or ONEC_SQL_USER." } if (-not $Password) { throw "Password is required. Use -Password or ONEC_SQL_PASSWORD." } $resolvedRoot = [System.IO.Path]::GetFullPath($OutputRoot) New-Item -ItemType Directory -Force -Path $resolvedRoot | Out-Null $stamp = Get-Date -Format "yyyyMMdd-HHmmss" $runDir = Join-Path $resolvedRoot $stamp New-Item -ItemType Directory -Force -Path $runDir | Out-Null $manifestPath = Join-Path $runDir "saved-state-watch-run.json" $manifestCheckPath = Join-Path $runDir "saved-state-watch-run-check.json" $manifestMarkdownPath = Join-Path $runDir "saved-state-watch-run.md" $reportPath = Join-Path $runDir "saved-state-object-report.json" $deltaPath = Join-Path $runDir "saved-state-object-report-delta.json" function Read-JsonFile { param([string]$Path) return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json } function Find-PreviousReport { param( [string]$Root, [string]$CurrentRunDir ) $currentFull = [System.IO.Path]::GetFullPath($CurrentRunDir) $candidates = @( Get-ChildItem -LiteralPath $Root -Directory | Sort-Object Name -Descending | ForEach-Object { $candidate = Join-Path $_.FullName "saved-state-object-report.json" if ([System.IO.Path]::GetFullPath($_.FullName) -ne $currentFull -and (Test-Path -LiteralPath $candidate)) { $candidate } } ) if (($candidates | Measure-Object).Count -gt 0) { return [string]$candidates[0] } return "" } if (-not $PreviousReport) { $PreviousReport = Find-PreviousReport -Root $resolvedRoot -CurrentRunDir $runDir } elseif ($PreviousReport) { $PreviousReport = [System.IO.Path]::GetFullPath($PreviousReport) } $buildArgs = @( "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "scripts\build_1c_saved_state_object_report.ps1", "-Server", $Server, "-Database", $Database, "-User", $User, "-Password", $Password, "-OutputDir", $runDir, "-BaseMetadataDir", $BaseMetadataDir, "-ExtensionGuidIndex", $ExtensionGuidIndex, "-ExtensionManifestSummary", $ExtensionManifestSummary, "-ConfigCASAllDir", $ConfigCASAllDir, "-Python", $Python ) if ($SkipMarkdown) { $buildArgs += "-SkipMarkdown" } $buildJson = & powershell @buildArgs if ($LASTEXITCODE -ne 0) { throw "Saved-state report build failed." } $report = ($buildJson | Out-String | ConvertFrom-Json) $delta = $null if (-not $SkipDelta -and $PreviousReport -and (Test-Path -LiteralPath $PreviousReport)) { $deltaJson = & $Python scripts\compare_1c_saved_state_object_reports.py ` --before $PreviousReport ` --after $reportPath ` --output $deltaPath if ($LASTEXITCODE -ne 0) { throw "Saved-state report delta failed." } $delta = Read-JsonFile $deltaPath } $result = [pscustomobject]@{ schema = "onec_saved_state_watch_once.v1" server = $Server database = $Database output_root = $resolvedRoot run_dir = $runDir previous_report = if ($PreviousReport) { $PreviousReport } else { $null } report = $reportPath markdown = $report.markdown check = $report.check manifest_check = $manifestCheckPath manifest_markdown = if ($SkipMarkdown) { $null } else { $manifestMarkdownPath } delta = if ($delta) { $deltaPath } else { $null } delta_markdown = if ($delta) { $delta.markdown } else { $null } delta_check = if ($delta) { $delta.check } else { $null } counts = [pscustomobject]@{ object_changes = $report.counts.object_changes system_changes = $report.counts.system_changes delta_objects_added = if ($delta) { $delta.counts.objects_added } else { $null } delta_objects_removed = if ($delta) { $delta.counts.objects_removed } else { $null } delta_objects_changed = if ($delta) { $delta.counts.objects_changed } else { $null } delta_objects_unchanged = if ($delta) { $delta.counts.objects_unchanged } else { $null } } safety = [pscustomobject]@{ read_only = $true sql_write_performed = $false secrets_in_report = $false } } $result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $manifestPath -Encoding UTF8 if (-not $SkipMarkdown) { & $Python scripts\render_1c_saved_state_watch_once_markdown.py ` --manifest $manifestPath ` --output $manifestMarkdownPath | Out-Null if ($LASTEXITCODE -ne 0) { throw "Saved-state watch Markdown rendering failed." } } & $Python scripts\check_1c_saved_state_watch_once.py ` --manifest $manifestPath ` --output $manifestCheckPath | Out-Null if ($LASTEXITCODE -ne 0) { throw "Saved-state watch manifest check failed." } $result | ConvertTo-Json -Depth 12