From 7f2ee3dce4e34c75c3b2e344c5147344a8688119 Mon Sep 17 00:00:00 2001 From: Federico Justus Denkena Date: Sat, 12 Sep 2026 13:20:00 +0200 Subject: [PATCH] windows/bootstrap: skip winget; install portable MinGit and Go DesktopAppInstaller on this host returns 0x8a15000f with an empty winget index; source reset does not recover it. Fetch MinGit and the go.dev windows-amd64 zip into LOCALAPPDATA instead. --- windows/bootstrap.ps1 | 111 +++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/windows/bootstrap.ps1 b/windows/bootstrap.ps1 index 08c1a9a..91cbfa6 100644 --- a/windows/bootstrap.ps1 +++ b/windows/bootstrap.ps1 @@ -11,65 +11,74 @@ $Dest = Join-Path $env:LOCALAPPDATA 'rclone-dropbox-bisync' $Dropbox = 'D:\Dropbox' $Workdir = 'D:\rclone-bisync' -function Refresh-Path { - $env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + - [Environment]::GetEnvironmentVariable('Path', 'User') +# Do not use winget: 0x8a15000f (empty source cache) is common and not recoverable +# from this script. Fetch portable MinGit + Go into LOCALAPPDATA instead. +$Tools = Join-Path $Dest 'tools' + +function Prepend-UserPath($Dir) { + $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') + if ([string]::IsNullOrEmpty($userPath)) { $userPath = '' } + if ($userPath -notlike "*$Dir*") { + [Environment]::SetEnvironmentVariable('Path', $(if ($userPath) { "$Dir;$userPath" } else { $Dir }), 'User') + } + if ($env:Path -notlike "*$Dir*") { $env:Path = "$Dir;$env:Path" } } -# 0x8a15000f: winget source index missing/corrupt (common on a fresh DesktopAppInstaller). -function Repair-WingetSource { - if ($script:WingetSourceRepaired) { return } - $winget = Get-Command winget -ErrorAction SilentlyContinue - if (-not $winget) { return } - $prev = $ErrorActionPreference - $ErrorActionPreference = 'Continue' - try { - & winget source reset --force --disable-interactivity 2>&1 | Out-Null - & winget source update --disable-interactivity 2>&1 | Out-Null - } finally { - $ErrorActionPreference = $prev - } - $script:WingetSourceRepaired = $true +function Get-File($Url, $Out) { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Invoke-WebRequest -Uri $Url -OutFile $Out -UseBasicParsing } -function Install-WithWinget($WingetId) { - Repair-WingetSource - $wingetArgs = @( - 'install', '-e', '--id', $WingetId, - '--accept-package-agreements', '--accept-source-agreements', - '--disable-interactivity', '--source', 'winget' - ) - $prev = $ErrorActionPreference - $ErrorActionPreference = 'Continue' - try { - & winget @wingetArgs - if ($LASTEXITCODE -eq 0) { return } - # Index still empty: reset once more, then retry without pinning the source - # (some hosts only have msstore until cdn.winget.microsoft.com is reachable). - & winget source reset --force --disable-interactivity 2>&1 | Out-Null - & winget source update --disable-interactivity 2>&1 | Out-Null - & winget install -e --id $WingetId --accept-package-agreements --accept-source-agreements --disable-interactivity - if ($LASTEXITCODE -eq 0) { return } - } finally { - $ErrorActionPreference = $prev +function Ensure-Git { + if (Get-Command git -ErrorAction SilentlyContinue) { return } + $root = Join-Path $Tools 'mingit' + $cmd = Join-Path $root 'cmd' + if (-not (Test-Path (Join-Path $cmd 'git.exe'))) { + Write-Host 'Fetching MinGit...' + $rel = Invoke-RestMethod -Uri 'https://api.github.com/repos/git-for-windows/git/releases/latest' + $asset = $rel.assets | + Where-Object { $_.name -match '^MinGit-.+-64-bit\.zip$' -and $_.name -notmatch 'busybox' } | + Select-Object -First 1 + if (-not $asset) { throw 'No MinGit-*-64-bit.zip on git-for-windows latest release' } + $zip = Join-Path $env:TEMP $asset.name + Get-File $asset.browser_download_url $zip + New-Item -ItemType Directory -Force -Path $root | Out-Null + Expand-Archive -Path $zip -DestinationPath $root -Force } - throw "winget failed to install $WingetId (exit $LASTEXITCODE / 0x8a15000f = empty source cache). Run: winget source reset --force; winget source update" -} - -function Ensure-Cmd($Name, $WingetId) { - if (Get-Command $Name -ErrorAction SilentlyContinue) { return } - if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { - throw "Need $Name on PATH (or winget to install $WingetId)" - } - Install-WithWinget $WingetId - Refresh-Path - if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { - throw "$Name still missing after winget install $WingetId; open a new PowerShell and retry" + Prepend-UserPath $cmd + if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + throw "git.exe missing under $cmd" } } -Ensure-Cmd git Git.Git -Ensure-Cmd go GoLang.Go +function Ensure-Go { + if (Get-Command go -ErrorAction SilentlyContinue) { return } + $root = Join-Path $Tools 'go' + $bin = Join-Path $root 'go\bin' + if (-not (Test-Path (Join-Path $bin 'go.exe'))) { + Write-Host 'Fetching Go...' + $dl = Invoke-RestMethod -Uri 'https://go.dev/dl/?mode=json' + $file = $null + foreach ($rel in $dl) { + $file = $rel.files | + Where-Object { $_.os -eq 'windows' -and $_.arch -eq 'amd64' -and $_.kind -eq 'archive' } | + Select-Object -First 1 + if ($file) { break } + } + if (-not $file) { throw 'No windows-amd64 Go archive on go.dev/dl' } + $zip = Join-Path $env:TEMP $file.filename + Get-File "https://go.dev/dl/$($file.filename)" $zip + New-Item -ItemType Directory -Force -Path $root | Out-Null + Expand-Archive -Path $zip -DestinationPath $root -Force + } + Prepend-UserPath $bin + if (-not (Get-Command go -ErrorAction SilentlyContinue)) { + throw "go.exe missing under $bin" + } +} + +Ensure-Git +Ensure-Go New-Item -ItemType Directory -Force -Path $Src, (Join-Path $Dest 'bin') | Out-Null if (-not (Test-Path (Join-Path $Src 'packaging\.git'))) {