feat(template): Add Step 11 Tier-1 Toolchain — Git + 7-Zip installation

Implement §6.6 minimal viable product: install Git for Windows and 7-Zip
to the template VM. Critical prerequisites for §3.3 (In-VM Git clone).

Step 11 additions:
- Git for Windows (latest LTS configurable) — silent installer, adds to PATH
- 7-Zip (latest stable configurable) — MSI installer to Program Files
- SHA256 hash pinning for both tools (currently unpinned, warn at runtime)
- Assert-Step validation for git/7z command availability
- Final pre-snapshot gate checks for both tools + cleanup of installers

Parameters added:
- -GitVersion (default: 2.47.0.windows.1)
- -SevenZipVersion (default: 24.07)

Rationale:
- Enables §3.3 in-VM Git clone (git clone directly in VM instead of host-zip-transfer)
- Supports cross-platform builds and multi-tool workflows
- Fallback from §3.2 7-Zip compression finally available in template
- Positions for future expansion of Tier-1 tools (NSIS, CMake, Node.js, WiX, etc.)

Updated documentation:
- Setup-WinBuild2025.ps1 docstring: Step 11 added to list + step ordering rationale
- Removed NOTE about Git not being installed
- Final validation now checks: 9 assertions (was 7)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Simone
2026-05-10 20:48:43 +02:00
parent 914072fdd8
commit 971d3dc6e9
+141 -6
View File
@@ -53,10 +53,13 @@
Validation: MSBuild.exe exists, executes, PATH, VC dir present Validation: MSBuild.exe exists, executes, PATH, VC dir present
Step 10 — Toolchain cross-check (dotnet, python, msbuild) Step 10 — Toolchain cross-check (dotnet, python, msbuild)
Throws on any missing tool 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 Cleanup — Disk cleanup: cleanmgr, SoftwareDistribution, CBS, TEMP, Prefetch, DISM
wuauserv already Disabled (Step 6b) — not restarted after cache clear wuauserv already Disabled (Step 6b) — not restarted after cache clear
Validation: wuauserv StartType Disabled 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 Throws if any check fails — prevents snapshot of broken state
Step ordering rationale: Step ordering rationale:
@@ -70,9 +73,10 @@
WU (Step 6) — all prereqs validated; WU runs with Firewall+Defender already off 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 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 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 NOTE: Step 11 installs Git for Windows + 7-Zip (Tier-1 Toolchain §6.6).
for the planned addition. The host clones and copies source via WinRM until then. This enables §3.3 (In-VM Git clone) and cross-platform build support.
╔══════════════════════════════════════════════════════════════════════╗ ╔══════════════════════════════════════════════════════════════════════╗
║ NETWORK REQUIREMENT DURING SETUP ║ ║ NETWORK REQUIREMENT DURING SETUP ║
@@ -137,7 +141,13 @@ param(
# Administrator password — used only to write DefaultPassword for autologin. # Administrator password — used only to write DefaultPassword for autologin.
# Optional: if empty, DefaultPassword registry value is left as-is (Deploy may have set it). # 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 Set-StrictMode -Version Latest
@@ -199,6 +209,14 @@ $script:Hashes = @{
# vs_buildtools.exe bootstrapper from aka.ms/vs/stable/vs_buildtools.exe # vs_buildtools.exe bootstrapper from aka.ms/vs/stable/vs_buildtools.exe
# Stable within a VS release cycle — update when VS major version changes. # Stable within a VS release cycle — update when VS major version changes.
'VSBuildToolsBootstrapper' = '' '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 ─────────────── # ── Pre-flight: remove temp files from any previous partial run ───────────────
@@ -207,7 +225,7 @@ $script:Hashes = @{
$knownTempFiles = @( $knownTempFiles = @(
'C:\CI\wu_worker.ps1', 'C:\CI\wu_result.txt', 'C:\CI\wu_reboot.txt', 'C:\CI\wu_log.txt', '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\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) { foreach ($f in $knownTempFiles) {
if (Test-Path $f) { if (Test-Path $f) {
@@ -960,6 +978,115 @@ if (-not $allOk) {
} }
Write-Host "All toolchain checks passed." -ForegroundColor Green 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\<tool>\ 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 ─────────────────────────────────────────────────────────────────── # ── Cleanup ───────────────────────────────────────────────────────────────────
if ($SkipCleanup) { if ($SkipCleanup) {
Write-Host "`n[Setup] Skipping disk cleanup (-SkipCleanup switch set)." -ForegroundColor Yellow 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\python_installer.exe') -and
-not (Test-Path 'C:\CI\vs_BuildTools.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\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 Write-Host "All pre-snapshot checks passed." -ForegroundColor Green