Private/func_CdfPackageCache.ps1
|
# Local cache management for CDF packages # Cache layout: ~/.cdf/packages/{templates|configs}/<endpoint>/<path>/<release>/ Function Get-CdfPackageCacheRoot { [CmdletBinding()] Param() $CDF_USER_HOME = $env:APPDATA ?? $env:HOME $cachePath = Join-Path -Path $CDF_USER_HOME -ChildPath '.cdf/packages' return $cachePath } Function Get-CdfPackageCachePath { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateSet('templates', 'configs')] [string]$PackageType, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory = $true)] [string]$PackagePath, [Parameter(Mandatory = $true)] [string]$Release ) $cacheRoot = Get-CdfPackageCacheRoot return Join-Path -Path $cacheRoot -ChildPath "$PackageType/$Endpoint/$PackagePath/$Release" } Function Get-CdfCachedPackage { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateSet('templates', 'configs')] [string]$PackageType, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory = $true)] [string]$PackagePath, [Parameter(Mandatory = $true)] [string]$Release ) $cachePath = Get-CdfPackageCachePath -PackageType $PackageType -Endpoint $Endpoint -PackagePath $PackagePath -Release $Release if (Test-Path $cachePath) { return @{ Path = $cachePath Cached = $true Release = $Release } } return @{ Path = $cachePath Cached = $false Release = $Release } } Function Save-CdfPackageToCache { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [ValidateSet('templates', 'configs')] [string]$PackageType, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory = $true)] [string]$PackagePath, [Parameter(Mandatory = $true)] [string]$Release, [Parameter(Mandatory = $true)] [CdfRegistryProvider]$Provider ) $cachePath = Get-CdfPackageCachePath -PackageType $PackageType -Endpoint $Endpoint -PackagePath $PackagePath -Release $Release if (Test-Path $cachePath) { Write-Verbose "Package already cached at $cachePath" return $cachePath } Write-Host "Downloading $PackageType/${PackagePath}:$Release from $Endpoint..." $registryPath = "cdf/$PackageType/$PackagePath" $Provider.Pull($registryPath, $Release, $cachePath) # Update cache index Update-CdfCacheIndex -PackageType $PackageType -Endpoint $Endpoint -PackagePath $PackagePath -Release $Release -CachePath $cachePath return $cachePath } Function Get-CdfCacheIndex { [CmdletBinding()] Param() $cacheRoot = Get-CdfPackageCacheRoot $indexPath = Join-Path -Path $cacheRoot -ChildPath 'index.json' if (Test-Path $indexPath) { return Get-Content -Raw $indexPath | ConvertFrom-Json -AsHashtable } return @{ packages = @() } } Function Update-CdfCacheIndex { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$PackageType, [Parameter(Mandatory = $true)] [string]$Endpoint, [Parameter(Mandatory = $true)] [string]$PackagePath, [Parameter(Mandatory = $true)] [string]$Release, [Parameter(Mandatory = $true)] [string]$CachePath ) $cacheRoot = Get-CdfPackageCacheRoot $indexPath = Join-Path -Path $cacheRoot -ChildPath 'index.json' $index = Get-CdfCacheIndex # Remove existing entry for same package+release if present $index.packages = @($index.packages | Where-Object { -not ($_.type -eq $PackageType -and $_.endpoint -eq $Endpoint -and $_.path -eq $PackagePath -and $_.release -eq $Release) }) $index.packages += @{ type = $PackageType endpoint = $Endpoint path = $PackagePath release = $Release cachePath = $CachePath installed = (Get-Date -Format 'o') } if (!(Test-Path (Split-Path $indexPath))) { New-Item -ItemType Directory -Path (Split-Path $indexPath) -Force | Out-Null } $index | ConvertTo-Json -Depth 5 | Out-File -FilePath $indexPath -Force } # Semver comparison utilities Function Test-CdfSemverMatch { <# .SYNOPSIS Tests if a release version satisfies a semver range expression. Supports: >=x.y.z, ^x.y.z, ~x.y.z, x.y.z (exact) #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$Release, [Parameter(Mandatory = $true)] [string]$Range ) $releaseVersion = ConvertTo-CdfSemver $Release if ($null -eq $releaseVersion) { return $false } # Exact match if ($Range -match '^\d+\.\d+\.\d+') { if ($Range -notmatch '^[~^><=]') { $rangeVersion = ConvertTo-CdfSemver $Range return ($null -ne $rangeVersion) -and ($releaseVersion -eq $rangeVersion) } } # >=x.y.z if ($Range -match '^>=(.+)$') { $minVersion = ConvertTo-CdfSemver $Matches[1] return ($null -ne $minVersion) -and ($releaseVersion -ge $minVersion) } # ^x.y.z (compatible: same major, >= minor.patch) if ($Range -match '^\^(.+)$') { $baseVersion = ConvertTo-CdfSemver $Matches[1] if ($null -eq $baseVersion) { return $false } return ($releaseVersion.Major -eq $baseVersion.Major) -and ($releaseVersion -ge $baseVersion) } # ~x.y.z (reasonably close: same major.minor, >= patch) if ($Range -match '^~(.+)$') { $baseVersion = ConvertTo-CdfSemver $Matches[1] if ($null -eq $baseVersion) { return $false } return ($releaseVersion.Major -eq $baseVersion.Major) -and ($releaseVersion.Minor -eq $baseVersion.Minor) -and ($releaseVersion -ge $baseVersion) } Write-Warning "Unsupported semver range format: '$Range'" return $false } Function ConvertTo-CdfSemver { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string]$VersionString ) # Strip prerelease/build metadata for comparison $cleanVersion = $VersionString -replace '-.*$', '' -replace '\+.*$', '' try { return [version]$cleanVersion } catch { Write-Warning "Invalid semver: '$VersionString'" return $null } } Function Resolve-CdfBestRelease { <# .SYNOPSIS Given a list of available releases and a semver range, returns the highest matching release. #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string[]]$AvailableReleases, [Parameter(Mandatory = $true)] [string]$Range ) $matching = $AvailableReleases | Where-Object { Test-CdfSemverMatch -Release $_ -Range $Range } if (-not $matching) { return $null } # Sort descending by version and pick the highest $sorted = $matching | Sort-Object { ConvertTo-CdfSemver $_ } -Descending return $sorted | Select-Object -First 1 } |