Files
reverse-skill/skills/scripts/lib/BootstrapSupplyChain.ps1
T

177 lines
7.1 KiB
PowerShell

function Ensure-Pnpm {
Ensure-NodeRuntime
$dependency = Get-BootstrapDependency -Name 'pnpm'
$pnpm = Get-NodeCommandPath -Name 'pnpm'
$currentVersion = ''
if ($pnpm) {
$previousErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$versionOutput = @(& $pnpm --version 2>$null)
$versionExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
$versionLine = $versionOutput | Select-Object -First 1
if ($versionExitCode -eq 0 -and $null -ne $versionLine) {
$currentVersion = ([string]$versionLine).Trim()
}
}
if ($currentVersion -ne [string]$dependency.version) {
$npm = Get-NodeCommandPath -Name 'npm'
if ([string]::IsNullOrWhiteSpace($npm)) {
throw 'npm is not available after Node.js installation.'
}
& $npm install -g ([string]$dependency.package)
if ($LASTEXITCODE -ne 0) {
throw "Failed to install pinned pnpm dependency $($dependency.package)."
}
}
}
function Assert-GitCheckoutState {
param(
[Parameter(Mandatory = $true)][string]$GitPath,
[Parameter(Mandatory = $true)][string]$CheckoutPath,
[Parameter(Mandatory = $true)][string]$PinnedCommit
)
$previousErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Continue'
$resolvedOutput = @(& $GitPath -C $CheckoutPath rev-parse HEAD 2>$null)
$resolveExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
$resolvedLine = $resolvedOutput | Select-Object -First 1
$resolvedCommit = if ($null -eq $resolvedLine) { '' } else { ([string]$resolvedLine).Trim() }
if ($resolveExitCode -ne 0 -or $resolvedCommit -ne $PinnedCommit) {
throw "Checkout verification failed: expected $PinnedCommit, got $resolvedCommit ($CheckoutPath)"
}
try {
$ErrorActionPreference = 'Continue'
$status = @(& $GitPath -C $CheckoutPath status --porcelain --untracked-files=all 2>$null)
$statusExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($statusExitCode -ne 0) {
throw "Cannot inspect checkout state: $CheckoutPath"
}
if ($status.Count -gt 0) {
throw "Checkout has local changes; refusing to execute it: $CheckoutPath"
}
}
function Move-BootstrapDirectory {
param(
[Parameter(Mandatory = $true)][string]$Source,
[Parameter(Mandatory = $true)][string]$Destination
)
[IO.Directory]::Move($Source, $Destination)
}
function Ensure-GitCloneInstall {
param(
[Parameter(Mandatory = $true)]$Definition,
[Parameter(Mandatory = $true)][string]$TargetPath
)
$git = Get-FirstCommandPath -Names @('git')
if ([string]::IsNullOrWhiteSpace($git)) {
throw 'git is required for git-clone bootstrap definitions.'
}
$pinnedCommit = if ($Definition.PSObject.Properties['pinnedCommit']) { [string]$Definition.pinnedCommit } else { '' }
if ([string]::IsNullOrWhiteSpace($pinnedCommit)) {
throw "Git capability $($Definition.repo) must define pinnedCommit."
}
if ((Test-Path -LiteralPath $TargetPath -PathType Container) -and (Test-Path -LiteralPath (Join-Path $TargetPath '.git'))) {
Assert-GitCheckoutState -GitPath $git -CheckoutPath $TargetPath -PinnedCommit $pinnedCommit
return $true
}
if (Test-Path -LiteralPath $TargetPath) {
throw "Install path exists but is not a git checkout: $TargetPath"
}
$parent = Split-Path -Path $TargetPath -Parent
Ensure-DownloadDirectory -Path $parent
$stagePath = Join-Path $parent ('.reverse-bootstrap-{0}' -f [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $stagePath | Out-Null
try {
& $git init --quiet $stagePath
if ($LASTEXITCODE -ne 0) { throw 'git init failed' }
& $git -C $stagePath remote add origin $Definition.repo
if ($LASTEXITCODE -ne 0) { throw 'git remote add failed' }
& $git -C $stagePath fetch --depth 1 origin $pinnedCommit
if ($LASTEXITCODE -ne 0) { throw 'git fetch failed' }
& $git -C $stagePath checkout --quiet --detach FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw 'git checkout failed' }
Assert-GitCheckoutState -GitPath $git -CheckoutPath $stagePath -PinnedCommit $pinnedCommit
Move-BootstrapDirectory -Source $stagePath -Destination $TargetPath
if (-not (Test-Path -LiteralPath (Join-Path $TargetPath '.git') -PathType Container)) {
throw "Failed to promote staged checkout to $TargetPath"
}
}
finally {
if (Test-Path -LiteralPath $stagePath) {
Remove-Item -LiteralPath $stagePath -Recurse -Force
}
}
return $true
}
function Invoke-AnythingAnalyzerPinnedInstall {
param(
[Parameter(Mandatory = $true)][string]$RepoDir,
[Parameter(Mandatory = $true)][string]$PnpmPath,
[Parameter(Mandatory = $true)][string]$GitPath,
[Parameter(Mandatory = $true)][string]$PinnedCommit,
[string]$VsBuildToolsError = ''
)
$workspacePath = Join-Path $RepoDir 'pnpm-workspace.yaml'
$workspaceExisted = Test-Path -LiteralPath $workspacePath -PathType Leaf
$workspaceBytes = if ($workspaceExisted) { [IO.File]::ReadAllBytes($workspacePath) } else { $null }
Push-Location $RepoDir
try {
Approve-AnythingAnalyzerBuildScripts -RepoDir $RepoDir
if (-not (Test-AnythingAnalyzerElectronHealthy -RepoDir $RepoDir -PnpmPath $PnpmPath)) {
$nodeModules = Join-Path $RepoDir 'node_modules'
Remove-Item -LiteralPath $nodeModules -Recurse -Force -ErrorAction SilentlyContinue
}
& $PnpmPath install --frozen-lockfile
if ($LASTEXITCODE -ne 0) {
if ($VsBuildToolsError) { throw "pnpm install failed for anything-analyzer. Visual Studio Build Tools auto-install also failed earlier: $VsBuildToolsError" }
throw 'pnpm install failed for anything-analyzer.'
}
& $PnpmPath rebuild electron esbuild better-sqlite3
if ($LASTEXITCODE -ne 0) {
if ($VsBuildToolsError) { throw "pnpm rebuild failed for anything-analyzer. Visual Studio Build Tools auto-install also failed earlier: $VsBuildToolsError" }
throw 'pnpm rebuild failed for anything-analyzer.'
}
if (-not (Test-AnythingAnalyzerElectronHealthy -RepoDir $RepoDir -PnpmPath $PnpmPath)) {
throw 'anything-analyzer Electron dependency is still unhealthy after pnpm rebuild.'
}
}
finally {
Pop-Location
if ($workspaceExisted) {
[IO.File]::WriteAllBytes($workspacePath, $workspaceBytes)
}
elseif (Test-Path -LiteralPath $workspacePath) {
Remove-Item -LiteralPath $workspacePath -Force
}
}
Assert-GitCheckoutState -GitPath $GitPath -CheckoutPath $RepoDir -PinnedCommit $PinnedCommit
}