diff --git a/template/Setup-WinBuild2025.ps1 b/template/Setup-WinBuild2025.ps1 index 403921f..d7fc0a9 100644 --- a/template/Setup-WinBuild2025.ps1 +++ b/template/Setup-WinBuild2025.ps1 @@ -53,10 +53,13 @@ Validation: MSBuild.exe exists, executes, PATH, VC dir present Step 10 — Toolchain cross-check (dotnet, python, msbuild) Throws on any missing tool + Step 11 — Tier-1 Toolchain (Git for Windows + 7-Zip) — §6.6 + Installs to C:\Program Files (Git default) and C:\Program Files\7-Zip + Validation: git and 7z commands resolvable, executables present Cleanup — Disk cleanup: cleanmgr, SoftwareDistribution, CBS, TEMP, Prefetch, DISM wuauserv already Disabled (Step 6b) — not restarted after cache clear Validation: wuauserv StartType Disabled - Final — Pre-snapshot gate: 7 cross-cutting checks across all steps + Final — Pre-snapshot gate: 9 cross-cutting checks across all steps Throws if any check fails — prevents snapshot of broken state Step ordering rationale: @@ -70,9 +73,10 @@ WU (Step 6) — all prereqs validated; WU runs with Firewall+Defender already off WU disable (Step 6b) — immediately after WU; snapshot captures WU permanently off Toolchain (7-9) — .NET/Python/VS last; WU done, no scan on installer payloads + Tier-1 (Step 11) — Git + 7-Zip after base toolchain; enables §3.3 in-VM clone - NOTE: Git is NOT installed by default. See §6.6 (Tier-1 Toolchain) in TODO.md - for the planned addition. The host clones and copies source via WinRM until then. + NOTE: Step 11 installs Git for Windows + 7-Zip (Tier-1 Toolchain §6.6). + This enables §3.3 (In-VM Git clone) and cross-platform build support. ╔══════════════════════════════════════════════════════════════════════╗ ║ NETWORK REQUIREMENT DURING SETUP ║ @@ -137,7 +141,13 @@ param( # Administrator password — used only to write DefaultPassword for autologin. # Optional: if empty, DefaultPassword registry value is left as-is (Deploy may have set it). - [string] $AdminPassword = '' + [string] $AdminPassword = '', + + # Git for Windows version (latest LTS recommended) + [string] $GitVersion = '2.47.0.windows.1', + + # 7-Zip version (latest stable) + [string] $SevenZipVersion = '24.07' ) Set-StrictMode -Version Latest @@ -199,6 +209,14 @@ $script:Hashes = @{ # vs_buildtools.exe bootstrapper from aka.ms/vs/stable/vs_buildtools.exe # Stable within a VS release cycle — update when VS major version changes. 'VSBuildToolsBootstrapper' = '' + + # Git for Windows installer from https://github.com/git-for-windows/git/releases/download/ + # Update $script:Hashes['GitForWindows'] when $GitVersion changes. + 'GitForWindows' = '' + + # 7-Zip MSI installer from https://www.7-zip.org/download.html + # Update $script:Hashes['SevenZip'] when $SevenZipVersion changes. + 'SevenZip' = '' } # ── Pre-flight: remove temp files from any previous partial run ─────────────── @@ -207,7 +225,7 @@ $script:Hashes = @{ $knownTempFiles = @( 'C:\CI\wu_worker.ps1', 'C:\CI\wu_result.txt', 'C:\CI\wu_reboot.txt', 'C:\CI\wu_log.txt', 'C:\CI\vs_worker.ps1', 'C:\CI\vs_result.txt', 'C:\CI\vs_BuildTools.exe', - 'C:\CI\python_installer.exe' + 'C:\CI\python_installer.exe', 'C:\CI\git_installer.exe', 'C:\CI\7zip_installer.msi' ) foreach ($f in $knownTempFiles) { if (Test-Path $f) { @@ -960,6 +978,115 @@ if (-not $allOk) { } Write-Host "All toolchain checks passed." -ForegroundColor Green +# ── Step 11: Tier-1 Toolchain (Git for Windows + 7-Zip) ────────────────────── +# Critical for §3.3 (In-VM Git clone) and cross-platform build support. +# Installed to C:\BuildTools\\ for clear separation from C:\CI\ (runtime artifacts). + +Write-Step "Installing Tier-1 Toolchain (Git + 7-Zip)" + +# Build tools root directory +$buildToolsRoot = 'C:\BuildTools' +if (-not (Test-Path $buildToolsRoot)) { + New-Item -ItemType Directory -Path $buildToolsRoot -Force | Out-Null +} + +# ── Git for Windows ─────────────────────────────────────────────────────────── +$gitDir = Join-Path $buildToolsRoot 'Git' +$gitExe = Join-Path $gitDir 'bin\git.exe' + +if (Test-Path $gitExe) { + Write-Host "Git for Windows already installed: $(& $gitExe --version 2>&1)" +} else { + $gitUrl = "https://github.com/git-for-windows/git/releases/download/v$GitVersion/Git-$GitVersion-64-bit.exe" + $gitInstaller = 'C:\CI\git_installer.exe' + + Write-Host "Downloading Git for Windows $GitVersion..." + try { + Invoke-WebRequest -Uri $gitUrl -OutFile $gitInstaller -UseBasicParsing + } catch { + throw "Failed to download Git from $gitUrl : $_" + } + + Assert-Hash -FilePath $gitInstaller -Label "Git-$GitVersion-64-bit.exe" ` + -Expected $script:Hashes['GitForWindows'] + + Write-Host "Installing Git for Windows (silent, with PATH)..." + $gitProc = Start-Process -FilePath $gitInstaller -ArgumentList @( + '/VERYSILENT', + '/NORESTART', + '/NOCANCEL', + '/SP-', + '/COMPONENTS=assoc,assoc_sh,gitlfs,windowsterminal' + ) -Wait -PassThru + + Remove-Item $gitInstaller -ErrorAction SilentlyContinue + + if ($gitProc -and $gitProc.ExitCode -ne 0) { + throw "Git installation failed (exit $($gitProc.ExitCode))." + } + + # Git installer adds to PATH automatically; refresh + $env:PATH = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + + [System.Environment]::GetEnvironmentVariable('PATH', 'User') + + Write-Host "Git installed: $(git --version 2>&1)" +} + +# Validation +Assert-Step 'Git' 'git command resolvable' { + [bool](Get-Command git -ErrorAction SilentlyContinue) +} +Assert-Step 'Git' 'git version matches or compatible' { + $v = (& git --version 2>&1 | Select-Object -First 1).ToString() + $v -match 'git version' +} + +# ── 7-Zip ───────────────────────────────────────────────────────────────────── +$sevenZipDir = 'C:\Program Files\7-Zip' +$sevenZipExe = Join-Path $sevenZipDir '7z.exe' + +if (Test-Path $sevenZipExe) { + Write-Host "7-Zip already installed." +} else { + $sevenZipUrl = "https://www.7-zip.org/a/7z$($SevenZipVersion -replace '\.', '')-x64.msi" + $sevenZipMsi = 'C:\CI\7zip_installer.msi' + + Write-Host "Downloading 7-Zip $SevenZipVersion..." + try { + Invoke-WebRequest -Uri $sevenZipUrl -OutFile $sevenZipMsi -UseBasicParsing + } catch { + throw "Failed to download 7-Zip from $sevenZipUrl : $_" + } + + Assert-Hash -FilePath $sevenZipMsi -Label "7z$($SevenZipVersion -replace '.', '')-x64.msi" ` + -Expected $script:Hashes['SevenZip'] + + Write-Host "Installing 7-Zip via MSI..." + $msiProc = Start-Process -FilePath 'msiexec.exe' -ArgumentList @( + '/i', $sevenZipMsi, + '/quiet', + '/norestart' + ) -Wait -PassThru + + Remove-Item $sevenZipMsi -ErrorAction SilentlyContinue + + if ($msiProc -and $msiProc.ExitCode -notin @(0, 3010)) { + throw "7-Zip installation failed (exit $($msiProc.ExitCode))." + } + + Write-Host "7-Zip installed." +} + +# Validation +Assert-Step '7-Zip' '7z.exe exists at expected path' { + Test-Path -Path $sevenZipExe -PathType Leaf +} +Assert-Step '7-Zip' '7z command resolvable' { + [bool](Get-Command 7z -ErrorAction SilentlyContinue) +} + +Write-Host "Tier-1 Toolchain installation complete." -ForegroundColor Green + # ── Cleanup ─────────────────────────────────────────────────────────────────── if ($SkipCleanup) { Write-Host "`n[Setup] Skipping disk cleanup (-SkipCleanup switch set)." -ForegroundColor Yellow @@ -1125,7 +1252,15 @@ Assert-Step 'Final' 'no leftover installer scripts in C:\CI' { -not (Test-Path 'C:\CI\python_installer.exe') -and -not (Test-Path 'C:\CI\vs_BuildTools.exe') -and -not (Test-Path 'C:\CI\wu_worker.ps1') -and - -not (Test-Path 'C:\CI\vs_worker.ps1') + -not (Test-Path 'C:\CI\vs_worker.ps1') -and + -not (Test-Path 'C:\CI\git_installer.exe') -and + -not (Test-Path 'C:\CI\7zip_installer.msi') +} +Assert-Step 'Final' 'git command available' { + [bool](Get-Command git -ErrorAction SilentlyContinue) +} +Assert-Step 'Final' '7z command available' { + [bool](Get-Command 7z -ErrorAction SilentlyContinue) } Write-Host "All pre-snapshot checks passed." -ForegroundColor Green