Private/Get-Installer.ps1
|
<#
SPDX-License-Identifier: MIT Copyright (c) 2026 Leon McClatchey, Linktech Engineering LLC Package: VSCode-Updater Author: Leon McClatchey Company: Linktech Engineering LLC Created: 2026-04-16 Modified: 2026-04-16 File: Private/Get-Installer.ps1 Version: 1.0.0 Description: Retrieves the VS Code installer deterministically with support for Skip, Force, and Normal download modes, including hash validation and temp file cleanup. #> function Normalize-Scalar { param($Value) if ($Value -is [System.Array]) { return $Value[-1] } return $Value } function Test-InstallerValid { [OutputType([bool])] [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not (Test-Path $Path)) { return $false } $info = Get-Item $Path -ErrorAction SilentlyContinue if ($info.Length -lt 1MB) { Write-VSCodeUpdaterLog "[INSTALLER] File too small (<1MB): $Path" return $false } if ($info.Extension -ne ".exe") { Write-VSCodeUpdaterLog "[INSTALLER] Invalid extension: $($info.Extension)" return $false } return $true } function Get-Installer { [OutputType([string])] [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Url, [Parameter(Mandatory = $true)] [string]$CachePath, [ValidateSet("Skip", "Force", "Normal")] [string]$DownloadMode = "Normal" ) Write-VSCodeUpdaterLog "[INSTALLER] Mode: $DownloadMode" switch ($DownloadMode) { "Skip" { Write-VSCodeUpdaterLog "[INSTALLER] Skip mode — using cached installer only" if (-not (Test-InstallerValid -Path $CachePath)) { Write-VSCodeUpdaterLog "[INSTALLER] Skip mode failed — cached installer missing or invalid" return $null } return $CachePath } "Force" { Write-VSCodeUpdaterLog "[INSTALLER] Force mode — downloading fresh installer" try { Invoke-WebRequest -Uri $Url -OutFile $CachePath -UseBasicParsing -ErrorAction Stop } catch { Write-VSCodeUpdaterLog "[INSTALLER] Forced download failed: $($_.Exception.Message)" return $null } if (-not (Test-InstallerValid -Path $CachePath)) { Write-VSCodeUpdaterLog "[INSTALLER] Forced download produced invalid installer" return $null } return $CachePath } "Normal" { Write-VSCodeUpdaterLog "[INSTALLER] Normal mode — checking cache" if (Test-InstallerValid -Path $CachePath) { Write-VSCodeUpdaterLog "[INSTALLER] Cached installer exists — checking for update" $temp = Join-Path $env:TEMP "installer.tmp.exe" if (Test-Path $temp) { Remove-Item $temp -Force -ErrorAction SilentlyContinue | Out-Null } try { Invoke-WebRequest -Uri $Url -OutFile $temp -UseBasicParsing -ErrorAction Stop } catch { Write-VSCodeUpdaterLog "[INSTALLER] Failed to download installer: $($_.Exception.Message)" return $null } if (-not (Test-InstallerValid -Path $temp)) { Write-VSCodeUpdaterLog "[INSTALLER] Downloaded installer is invalid" Remove-Item $temp -Force -ErrorAction SilentlyContinue | Out-Null return $null } $cachedHash = Normalize-Scalar(Get-FileHashSafe -Path $CachePath) $newHash = Normalize-Scalar(Get-FileHashSafe -Path $temp) Write-VSCodeUpdaterLog "[INSTALLER] Cached hash: $cachedHash" Write-VSCodeUpdaterLog "[INSTALLER] New hash: $newHash" if ($cachedHash -eq $newHash) { Write-VSCodeUpdaterLog "[INSTALLER] Installer unchanged — keeping cached copy" Remove-Item $temp -Force -ErrorAction SilentlyContinue | Out-Null return $CachePath } Write-VSCodeUpdaterLog "[INSTALLER] Installer updated — replacing cached copy" Copy-Item $temp $CachePath -Force Remove-Item $temp -Force -ErrorAction SilentlyContinue | Out-Null return $CachePath } Write-VSCodeUpdaterLog "[INSTALLER] No cached installer — downloading fresh copy" try { Invoke-WebRequest -Uri $Url -OutFile $CachePath -UseBasicParsing -ErrorAction Stop } catch { Write-VSCodeUpdaterLog "[INSTALLER] Failed to download installer: $($_.Exception.Message)" return $null } if (-not (Test-InstallerValid -Path $CachePath)) { Write-VSCodeUpdaterLog "[INSTALLER] Fresh download produced invalid installer" return $null } return $CachePath } } } |