src/_Install-CciModuleFromBlob.ps1
|
function _Install-CciModuleFromBlob { <# .SYNOPSIS Installs a module (and its RequiredModules, recursively) from the tenant distribution store. .DESCRIPTION The feed path gets dependency resolution for free from PSResourceGet; the store path resolves it explicitly, by reading RequiredModules from the installed manifest and fetching each dependency that is not already available. $Seen guards against cycles and repeat work. #> [CmdletBinding()] param( [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)]$Feed, [Parameter(Mandatory)]$Context, [Parameter(Mandatory)]$Index, [string]$Version, [ValidateSet('CurrentUser','AllUsers')][string]$Scope = 'CurrentUser', [switch]$Reinstall, [System.Collections.Generic.HashSet[string]]$Seen ) if (-not $Seen) { $Seen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) } if (-not $Seen.Add($Name)) { return } $entry = $Index.modules.PSObject.Properties[$Name] if (-not $entry) { throw "cciget: '$Name' is not present in the tenant distribution store index." } if (-not $Version) { $Version = $entry.Value.latest } $root = _Get-CciModulesRoot -Scope $Scope $target = Join-Path (Join-Path $root $Name) $Version if ((Test-Path $target) -and -not $Reinstall) { Write-Host "cciget: $Name $Version already installed." } else { New-Item -ItemType Directory -Path $target -Force | Out-Null $prefix = "$($Feed.blobPrefix)/$Name/$Version/" $blobs = @(_Invoke-CciBlobRequest -Context $Context -Operation List ` -Container $Feed.blobContainer -Prefix $prefix) if (-not $blobs) { throw "cciget: no content under '$prefix' in the distribution store." } Write-Host "cciget: downloading $Name $Version ($($blobs.Count) files)..." # A partial install is worse than no install - it looks importable but # is missing source files. Any failure removes the target so the next # attempt is not short-circuited by the "already installed" check. try { $expected = 0 foreach ($b in $blobs) { $rel = $b.Substring($prefix.Length) if (-not $rel) { continue } $dest = Join-Path $target ($rel -replace '/', '\') $null = _Invoke-CciBlobRequest -Context $Context -Operation Download ` -Container $Feed.blobContainer -Blob $b -Path $dest $expected++ } $actual = @(Get-ChildItem $target -Recurse -File).Count if ($actual -ne $expected) { throw "cciget: incomplete install of $Name $Version - expected $expected files, found $actual." } } catch { Remove-Item $target -Recurse -Force -ErrorAction SilentlyContinue throw } Write-Host "cciget: installed $Name $Version -> $target" } $psd1 = Get-ChildItem $target -Filter '*.psd1' | Select-Object -First 1 if ($psd1) { $manifest = Import-PowerShellDataFile $psd1.FullName $required = if ($manifest.ContainsKey('RequiredModules')) { @($manifest.RequiredModules) } else { @() } foreach ($dep in $required) { if (-not $dep) { continue } $depName = if ($dep -is [hashtable]) { $dep.ModuleName } else { "$dep" } if (-not $depName) { continue } if (Get-Module -ListAvailable -Name $depName) { Write-Verbose "cciget: dependency $depName already available." continue } Write-Host "cciget: resolving dependency $depName..." _Install-CciModuleFromBlob -Name $depName -Feed $Feed -Context $Context -Index $Index ` -Scope $Scope -Reinstall:$Reinstall -Seen $Seen } } } |