Set-StrictMode -Version Latest function Get-ToolRoot { return (Resolve-Path (Join-Path $PSScriptRoot "..")).Path } function Import-Settings { $root = Get-ToolRoot $settingsPath = Join-Path $root "config\settings.ps1" if (!(Test-Path $settingsPath)) { throw "Missing settings file: $settingsPath" } . $settingsPath if ($null -eq $Settings) { throw "settings.ps1 did not define `$Settings" } return $Settings } function New-LogFile { param([hashtable]$Settings) $root = Get-ToolRoot $logDir = Join-Path $root $Settings.LogFolder New-Item -ItemType Directory -Force -Path $logDir | Out-Null $stamp = Get-Date -Format "yyyy-MM-dd" return Join-Path $logDir "daily-build-uploader-$stamp.log" } function Write-Log { param( [string]$Message, [string]$Level = "INFO" ) $Settings = Import-Settings $log = New-LogFile -Settings $Settings $line = "{0} [{1}] {2}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Level, $Message Add-Content -Path $log -Value $line -Encoding UTF8 Write-Host $line } function Get-BuildInfoFromZipName { param([string]$Path) $name = Split-Path $Path -Leaf if ($name -match '^index_beta_(\d{4}-\d{2}-\d{2})-v(\d{3})\.zip$') { return [pscustomobject]@{ Date = $Matches[1] Version = $Matches[2] FileName = $name } } return $null } function Wait-FileStable { param( [string]$Path, [int]$StableSeconds = 4 ) if (!(Test-Path $Path)) { return $false } $lastLength = -1 $lastWrite = $null $stableStart = Get-Date while ($true) { if (!(Test-Path $Path)) { return $false } $item = Get-Item $Path $same = ($item.Length -eq $lastLength) -and ($item.LastWriteTimeUtc -eq $lastWrite) if ($same) { if (((Get-Date) - $stableStart).TotalSeconds -ge $StableSeconds) { return $true } } else { $stableStart = Get-Date $lastLength = $item.Length $lastWrite = $item.LastWriteTimeUtc } Start-Sleep -Milliseconds 500 } } function Get-StatePath { $root = Get-ToolRoot return Join-Path $root "logs\processed-zips.json" } function Read-ProcessedState { $path = Get-StatePath if (!(Test-Path $path)) { return @{} } try { $json = Get-Content $path -Raw -Encoding UTF8 if ([string]::IsNullOrWhiteSpace($json)) { return @{} } $obj = $json | ConvertFrom-Json $state = @{} $obj.PSObject.Properties | ForEach-Object { $state[$_.Name] = $_.Value } return $state } catch { return @{} } } function Write-ProcessedState { param([hashtable]$State) $path = Get-StatePath New-Item -ItemType Directory -Force -Path (Split-Path $path) | Out-Null $State | ConvertTo-Json -Depth 8 | Set-Content -Path $path -Encoding UTF8 } function Get-ZipSignature { param([string]$Path) $item = Get-Item $Path return "{0}|{1}" -f $item.Length, $item.LastWriteTimeUtc.ToString("o") }