private/RunRecord.ps1
|
# data-model.md -> Run Record: %LOCALAPPDATA%\Octa\runs\<yyyyMMdd-HHmmss>\run.json # research.md -> per-user, no elevation needed to write, self-contained per-run folder. function Get-OctaRunsRoot { $rootPath = Join-Path $env:LOCALAPPDATA 'Octa\runs' if (-not (Test-Path $rootPath)) { New-Item -ItemType Directory -Path $rootPath -Force | Out-Null } return $rootPath } function New-OctaRunFolder { <# ponytail: the run id is second-resolution, so two runs started within the same second used to resolve to the same folder - New-Item -Force silently reuses it and the second run's Save-OctaRunRecord overwrites the first one's run.json, losing the snapshots that made the first run undoable. Verified real: two back-to-back calls returned the identical RunId. A numeric suffix keeps ids sortable (Get-OctaRunRecord's 'latest' relies on Sort-Object Name) while guaranteeing each run gets its own folder. #> [CmdletBinding()] param() $runsRoot = Get-OctaRunsRoot $baseId = Get-Date -Format 'yyyyMMdd-HHmmss' $runId = $baseId $suffix = 1 while (Test-Path (Join-Path $runsRoot $runId)) { $runId = '{0}-{1:D2}' -f $baseId, $suffix $suffix++ } $folder = Join-Path $runsRoot $runId New-Item -ItemType Directory -Path $folder -Force | Out-Null return [pscustomobject]@{ RunId = $runId; Path = $folder } } function Save-OctaRunRecord { [CmdletBinding()] param( [Parameter(Mandatory)][object]$RunRecord, [Parameter(Mandatory)][string]$RunFolder ) $destination = Join-Path $RunFolder 'run.json' $RunRecord | ConvertTo-Json -Depth 10 | Set-Content -Path $destination -Encoding utf8 } function Get-OctaRunRecord { <# Resolves 'latest' to the most recently created run folder; otherwise looks up the given RunId directly. Returns $null when not found (bin/octa.ps1 maps this to exit 4). #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$RunId ) $runsRoot = Get-OctaRunsRoot if ($RunId -eq 'latest') { $latestFolder = Get-ChildItem -Path $runsRoot -Directory -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1 if (-not $latestFolder) { return $null } $runFile = Join-Path $latestFolder.FullName 'run.json' } else { $runFile = Join-Path $runsRoot "$RunId\run.json" } if (-not (Test-Path $runFile)) { return $null } $record = Get-Content -Path $runFile -Raw | ConvertFrom-Json Add-Member -InputObject $record -MemberType NoteProperty -Name 'RunFolder' -Value (Split-Path $runFile -Parent) -Force return $record } |