Public/Sync.ps1
|
function Sync-PCNotionSet { <# .SYNOPSIS Materialises an approved set into its local root on this device. .DESCRIPTION Precedence: Active set with current approval, lock, journal recovery under guards, then per item approval, page state and the decision table. Writes twins through a journal, updates folder receipts and the reported fields of the register, and writes a report under .pcnotion/reports. Reads approval fields; never writes them. .PARAMETER Full Ignore timestamps and compare every item's bytes against a fresh fetch. .PARAMETER BreakLock Remove an existing lock after showing its holder. The owner's action after confirming that run is not alive. .PARAMETER ReportPath Also write the JSON report to this path. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, Position = 0)] [string]$Name, [switch]$Full, [switch]$BreakLock, [string]$ReportPath ) $ctx = New-PCNotionContext try { if ($BreakLock) { $setRow = $ctx.Register.FindSet($Name) if (-not $setRow) { throw "No File Sync Set is named '$Name'." } $root = $ctx.Anchors.Resolve($setRow.LocalRoot) $holder = [PowerCraft.Notion.Core.Folder.FolderLock]::Read($root) if ($holder) { Write-Host "Lock held by: $holder" if ($PSCmdlet.ShouldProcess($root, 'Break the lock')) { [PowerCraft.Notion.Core.Folder.FolderLock]::Break($root) } } else { Write-Host 'No lock present.' } } if (-not $PSCmdlet.ShouldProcess($Name, 'Materialise the set')) { return } $options = [PowerCraft.Notion.Core.Export.ExportOptions]::new() $options.Full = [bool]$Full $run = [PowerCraft.Notion.Core.Export.ExportRun]::new($ctx.Register, $ctx.Anchors, $options) $report = $run.Run($Name) if ($ReportPath) { [PowerCraft.Notion.Core.Folder.AtomicFile]::WriteAllText([IO.Path]::GetFullPath($ReportPath), $report.ToJson()) } foreach ($w in $report.Warnings) { Write-Warning $w } ConvertTo-PCNotionReportObject $report } finally { $ctx.Client.Dispose() } } function Test-PCNotionSet { <# .SYNOPSIS Reports the state of a set on this device without writing anything. .DESCRIPTION No files, receipts, journal entries, rows or lock are written. Without -Verify an item can be metadata-current at most; with -Verify each page is fetched and compared byte for byte and reported verified-complete or differs. #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Name, [switch]$Verify, [string]$ReportPath ) $ctx = New-PCNotionContext try { $run = [PowerCraft.Notion.Core.Export.TestRun]::new($ctx.Register, $ctx.Anchors, [bool]$Verify, $null) $report = $run.Run($Name) if ($ReportPath) { [PowerCraft.Notion.Core.Folder.AtomicFile]::WriteAllText([IO.Path]::GetFullPath($ReportPath), $report.ToJson()) } foreach ($w in $report.Warnings) { Write-Warning $w } ConvertTo-PCNotionReportObject $report } finally { $ctx.Client.Dispose() } } function Resolve-PCNotionPending { <# .SYNOPSIS Shows a pending or unresolved transaction and, on confirmation, abandons its journal entry. .DESCRIPTION Abandoning removes the journal entry only. Files, temp files and receipts are left for the owner. The item is then re-evaluated by the next run from its receipt and file, under the normal decision table. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] param( [Parameter(Mandatory)] [string]$Set, [Parameter(Mandatory, Position = 0)] [string]$Item ) $ctx = New-PCNotionContext try { $setRow = $ctx.Register.FindSet($Set) if (-not $setRow) { throw "No File Sync Set is named '$Set'." } $record = [PowerCraft.Notion.Core.Approval.ApprovalService]::FindItem($ctx.Register.LoadItems($setRow), $Item) $root = $ctx.Anchors.Resolve($setRow.LocalRoot) $journal = [PowerCraft.Notion.Core.Folder.JournalStore]::new($root) $entry = $journal.LoadAll() | Where-Object { $_.ItemRowId -eq $record.RowId } | Select-Object -First 1 if (-not $entry) { Write-Host "No pending transaction for '$($record.LocalPath)'."; return } Write-Host ("Stage {0}{1}; run {2}; started {3}; intended hash {4}; prior baseline {5}; temp {6}" -f $entry.Stage, $(if ($entry.StageUnreadable) { ' (unreadable)' } else { '' }), $entry.RunId, $entry.StartedAt, $entry.IntendedFileHash, $entry.PriorBaselineHash, $entry.TempPath) if ($PSCmdlet.ShouldProcess("$Set / $($record.LocalPath)", 'Abandon the journal entry (files are left in place)')) { $journal.Remove($record.RowId) [pscustomobject]@{ Set = $Set; Item = $record.Name; LocalPath = $record.LocalPath; Abandoned = $true } } } finally { $ctx.Client.Dispose() } } |