From 71f1aa2e3b1d8f96bb5a3c315da3d465c377f9c3 Mon Sep 17 00:00:00 2001 From: Simone Date: Sat, 9 May 2026 00:11:33 +0200 Subject: [PATCH] feat: add Setup-Host.ps1 host bootstrap script (dirs, credentials, act_runner service) --- README.md | 2 + Setup-Host.ps1 | 353 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 Setup-Host.ps1 diff --git a/README.md b/README.md index 814bbc4..3b7ffe0 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ Ogni build gira in una VM Windows Server 2025 **usa e getta**, ripristinata dall ## Struttura repository ``` +├── Setup-Host.ps1 # Bootstrap host: dirs F:\CI\, credenziali, act_runner service +│ ├── scripts/ │ ├── Invoke-CIJob.ps1 # Orchestratore principale │ ├── New-BuildVM.ps1 # Linked clone da template diff --git a/Setup-Host.ps1 b/Setup-Host.ps1 new file mode 100644 index 0000000..2359b85 --- /dev/null +++ b/Setup-Host.ps1 @@ -0,0 +1,353 @@ +#Requires -RunAsAdministrator +#Requires -Version 5.1 +<# +.SYNOPSIS + Bootstraps the CI host machine from scratch (run ONCE on the host). + +.DESCRIPTION + Creates the F:\CI\ directory structure, installs prerequisites, registers + Windows Credential Manager entries, and optionally installs + registers + act_runner as a Windows service (via NSSM). + + Run this script elevated (as Administrator) on the Windows host before + provisioning the template VM or running any CI jobs. + + What this script does: + 1. Create F:\CI\ directory tree + 2. Verify VMware Workstation / vmrun.exe is present + 3. Install PowerShell CredentialManager module (if missing) + 4. Store guest VM credentials in Windows Credential Manager + 5. Install act_runner as a Windows service via NSSM (optional) + 6. Configure SSH alias for Gitea (optional) + 7. Print a post-setup checklist + +.PARAMETER CIRoot + Base directory for all CI data. Default: F:\CI + +.PARAMETER GuestCredentialTarget + Windows Credential Manager target name for guest VM credentials. + Default: BuildVMGuest + +.PARAMETER GuestUsername + Build user account inside the VM. Default: ci_build + +.PARAMETER GuestPassword + Password for the guest build account. + CHANGE THIS before production use. + Default: CIBuild!ChangeMe2026 + +.PARAMETER ActRunnerExe + Path to the act_runner binary. If the file does not exist the script + warns and skips the service installation step. + Default: \act_runner\act_runner.exe + +.PARAMETER ActRunnerConfigYaml + Path to act_runner config.yaml to copy into \act_runner\. + If not specified, copies runner\config.yaml from the repo (if it exists + next to this script). + Default: \runner\config.yaml + +.PARAMETER GiteaUrl + Gitea server URL used for runner registration. + Default: http://10.10.20.11:3100 + +.PARAMETER GiteaRunnerToken + Registration token for act_runner. If not supplied the script prints + instructions and skips registration (you can register later manually). + +.PARAMETER SkipRunnerInstall + Skip act_runner service installation entirely. + +.PARAMETER SkipSSHConfig + Skip writing the SSH config alias for Gitea. + +.PARAMETER GiteaSSHHost + Gitea SSH hostname/IP. Default: 10.10.20.11 + +.PARAMETER GiteaSSHPort + Gitea SSH port. Default: 2222 + +.EXAMPLE + # Minimal — creates dirs, stores credentials, skips runner install: + .\Setup-Host.ps1 -SkipRunnerInstall + + # Full setup with runner registration: + .\Setup-Host.ps1 -GiteaRunnerToken 'abc123token' + + # Custom CI root on a different drive: + .\Setup-Host.ps1 -CIRoot 'D:\CI' -GiteaRunnerToken 'abc123token' +#> +[CmdletBinding(SupportsShouldProcess)] +param( + [string] $CIRoot = 'F:\CI', + [string] $GuestCredentialTarget = 'BuildVMGuest', + [string] $GuestUsername = 'ci_build', + [string] $GuestPassword = 'CIBuild!ChangeMe2026', + [string] $ActRunnerExe = '', + [string] $ActRunnerConfigYaml = '', + [string] $GiteaUrl = 'http://10.10.20.11:3100', + [string] $GiteaRunnerToken = '', + [switch] $SkipRunnerInstall, + [switch] $SkipSSHConfig, + [string] $GiteaSSHHost = '10.10.20.11', + [int] $GiteaSSHPort = 2222 +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent + +function Write-Step { + param([string]$Message) + Write-Host "`n=== $Message ===" -ForegroundColor Cyan +} + +function Write-OK { param([string]$m) Write-Host " [OK] $m" -ForegroundColor Green } +function Write-Warn { param([string]$m) Write-Host " [WARN] $m" -ForegroundColor Yellow } +function Write-Fail { param([string]$m) Write-Host " [FAIL] $m" -ForegroundColor Red } + +# Resolve defaults that depend on $CIRoot / $scriptDir +if (-not $ActRunnerExe) { $ActRunnerExe = Join-Path $CIRoot 'act_runner\act_runner.exe' } +if (-not $ActRunnerConfigYaml) { $ActRunnerConfigYaml = Join-Path $scriptDir 'runner\config.yaml' } + +# ── Step 1: Create F:\CI\ directory tree ───────────────────────────────────── +Write-Step "Creating CI directory tree under $CIRoot" + +$dirs = @( + "$CIRoot\BuildVMs", + "$CIRoot\Artifacts", + "$CIRoot\Logs", + "$CIRoot\Templates\WinBuild", + "$CIRoot\act_runner\logs", + "$CIRoot\Cache\NuGet", + "$CIRoot\RunnerWork", + "$CIRoot\ISO" +) + +foreach ($dir in $dirs) { + if (Test-Path $dir) { + Write-OK "$dir (already exists)" + } else { + New-Item -ItemType Directory -Path $dir -Force | Out-Null + Write-OK "Created: $dir" + } +} + +# ── Step 2: Verify vmrun.exe ────────────────────────────────────────────────── +Write-Step "Verifying VMware Workstation (vmrun.exe)" + +$vmrunPath = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe' +if (Test-Path $vmrunPath) { + Write-OK "vmrun.exe found: $vmrunPath" +} else { + Write-Fail "vmrun.exe NOT found at expected path: $vmrunPath" + Write-Host " Install VMware Workstation Pro, then re-run this script." -ForegroundColor Yellow +} + +# ── Step 3: Install CredentialManager module ───────────────────────────────── +Write-Step "Checking PowerShell CredentialManager module" + +$credMgr = Get-Module -ListAvailable -Name CredentialManager -ErrorAction SilentlyContinue +if ($credMgr) { + Write-OK "CredentialManager $($credMgr[0].Version) already installed" +} else { + Write-Host " Installing CredentialManager module (CurrentUser scope)..." + Install-Module -Name CredentialManager -Scope CurrentUser -Force -AllowClobber + Write-OK "CredentialManager installed" +} + +Import-Module CredentialManager -ErrorAction SilentlyContinue + +# ── Step 4: Store guest VM credentials ─────────────────────────────────────── +Write-Step "Storing guest VM credentials in Windows Credential Manager" + +$existingCred = Get-StoredCredential -Target $GuestCredentialTarget -ErrorAction SilentlyContinue +if ($existingCred) { + Write-OK "Credential '$GuestCredentialTarget' already exists (user: $($existingCred.UserName))" + Write-Warn "To update: Remove-StoredCredential -Target '$GuestCredentialTarget', then re-run." +} else { + New-StoredCredential ` + -Target $GuestCredentialTarget ` + -UserName $GuestUsername ` + -Password $GuestPassword ` + -Persist LocalMachine | Out-Null + Write-OK "Credential '$GuestCredentialTarget' stored (user: $GuestUsername)" + if ($GuestPassword -eq 'CIBuild!ChangeMe2026') { + Write-Warn "Using DEFAULT password — change it before production use!" + Write-Warn "Edit -GuestPassword parameter and re-run, or update in Credential Manager manually." + } +} + +# ── Step 5: Copy runner\config.yaml ────────────────────────────────────────── +Write-Step "Copying act_runner config.yaml to $CIRoot\act_runner\" + +$destConfig = Join-Path $CIRoot 'act_runner\config.yaml' +if (Test-Path $ActRunnerConfigYaml) { + if (Test-Path $destConfig) { + Write-Warn "config.yaml already exists at $destConfig — skipping copy." + Write-Warn "Delete it manually if you want to overwrite." + } else { + Copy-Item -Path $ActRunnerConfigYaml -Destination $destConfig -Force + Write-OK "Copied $ActRunnerConfigYaml -> $destConfig" + } +} else { + Write-Warn "Source config.yaml not found at: $ActRunnerConfigYaml" + Write-Warn "Copy runner\config.yaml manually to $destConfig before starting the runner." +} + +# ── Step 6: Install act_runner as Windows service (via NSSM) ───────────────── +if ($SkipRunnerInstall) { + Write-Host "`n=== act_runner service setup SKIPPED (-SkipRunnerInstall) ===" -ForegroundColor Yellow +} +else { + Write-Step "Installing act_runner as Windows service" + + if (-not (Test-Path $ActRunnerExe)) { + Write-Warn "act_runner binary not found at: $ActRunnerExe" + Write-Warn "Download act_runner v1.0.2 from https://gitea.com/gitea/act_runner/releases" + Write-Warn "Place it at $ActRunnerExe, then re-run this script (or install the service manually)." + } + else { + # Check for NSSM + $nssm = Get-Command nssm -ErrorAction SilentlyContinue + if (-not $nssm) { + Write-Warn "NSSM not found in PATH. Attempting install via Chocolatey..." + $choco = Get-Command choco -ErrorAction SilentlyContinue + if ($choco) { + choco install nssm -y | Out-Null + $env:PATH = [System.Environment]::GetEnvironmentVariable('PATH','Machine') + ';' + $env:PATH + $nssm = Get-Command nssm -ErrorAction SilentlyContinue + } + } + + if (-not $nssm) { + Write-Fail "NSSM not available. Install NSSM manually (https://nssm.cc) and re-run." + } + else { + $serviceName = 'act_runner' + $existingSvc = Get-Service -Name $serviceName -ErrorAction SilentlyContinue + + if ($existingSvc) { + Write-OK "Service '$serviceName' already exists (Status: $($existingSvc.Status))" + } + else { + $actRunnerDir = Split-Path $ActRunnerExe -Parent + + # Register act_runner + if ($GiteaRunnerToken) { + Write-Host " Registering act_runner with Gitea ($GiteaUrl)..." + Push-Location $actRunnerDir + & $ActRunnerExe register ` + --no-interactive ` + --instance $GiteaUrl ` + --token $GiteaRunnerToken ` + --name 'local-windows-runner' ` + --labels 'windows-build:host,dotnet:host,msbuild:host' + $regExit = $LASTEXITCODE + Pop-Location + if ($regExit -ne 0) { + Write-Fail "act_runner registration failed (exit $regExit). Check token and Gitea URL." + } else { + Write-OK "act_runner registered with Gitea." + } + } else { + Write-Warn "No -GiteaRunnerToken supplied — skipping registration." + Write-Warn "Register manually: cd $actRunnerDir ; .\act_runner.exe register" + } + + # Install service + Write-Host " Installing service via NSSM..." + & nssm install $serviceName $ActRunnerExe 2>&1 | Out-Null + & nssm set $serviceName AppDirectory $actRunnerDir 2>&1 | Out-Null + & nssm set $serviceName AppParameters "daemon --config `"$destConfig`"" 2>&1 | Out-Null + & nssm set $serviceName DisplayName 'Gitea act_runner' 2>&1 | Out-Null + & nssm set $serviceName Description 'Gitea Actions runner (local CI/CD)' 2>&1 | Out-Null + & nssm set $serviceName Start SERVICE_AUTO_START 2>&1 | Out-Null + & nssm set $serviceName AppStdout (Join-Path $actRunnerDir 'logs\stdout.log') 2>&1 | Out-Null + & nssm set $serviceName AppStderr (Join-Path $actRunnerDir 'logs\stderr.log') 2>&1 | Out-Null + & nssm set $serviceName AppRotateFiles 1 2>&1 | Out-Null + & nssm set $serviceName AppRotateBytes 10485760 2>&1 | Out-Null # 10 MB + + Start-Service -Name $serviceName -ErrorAction SilentlyContinue + $svc = Get-Service -Name $serviceName -ErrorAction SilentlyContinue + if ($svc -and $svc.Status -eq 'Running') { + Write-OK "Service '$serviceName' installed and running." + } else { + Write-Warn "Service installed but not running — check logs at $(Join-Path $actRunnerDir 'logs\')." + } + } + } + } +} + +# ── Step 7: SSH config alias for Gitea ─────────────────────────────────────── +if ($SkipSSHConfig) { + Write-Host "`n=== SSH config SKIPPED (-SkipSSHConfig) ===" -ForegroundColor Yellow +} +else { + Write-Step "Configuring SSH alias for Gitea (gitea-ci)" + + $sshDir = Join-Path $env:USERPROFILE '.ssh' + $sshConfig = Join-Path $sshDir 'config' + + if (-not (Test-Path $sshDir)) { + New-Item -ItemType Directory -Path $sshDir -Force | Out-Null + } + + $aliasBlock = @" + +# Gitea CI alias — added by Setup-Host.ps1 +Host gitea-ci + HostName $GiteaSSHHost + Port $GiteaSSHPort + User git + IdentityFile ~/.ssh/id_rsa +"@ + + $existingConfig = '' + if (Test-Path $sshConfig) { + $existingConfig = Get-Content $sshConfig -Raw -ErrorAction SilentlyContinue + } + + if ($existingConfig -match 'Host gitea-ci') { + Write-OK "SSH alias 'gitea-ci' already present in $sshConfig" + } else { + Add-Content -Path $sshConfig -Value $aliasBlock -Encoding UTF8 + Write-OK "SSH alias 'gitea-ci' written to $sshConfig" + Write-Warn "Ensure your public SSH key is registered in Gitea under User Settings → SSH Keys." + } +} + +# ── Done ────────────────────────────────────────────────────────────────────── +Write-Host "" +Write-Host "============================================================" -ForegroundColor Green +Write-Host " Host setup complete!" -ForegroundColor Green +Write-Host "============================================================" -ForegroundColor Green +Write-Host "" +Write-Host " POST-SETUP CHECKLIST:" -ForegroundColor White +Write-Host "" +Write-Host " 1. Place act_runner binary (if not done):" +Write-Host " $ActRunnerExe" +Write-Host " Download: https://gitea.com/gitea/act_runner/releases/tag/v1.0.2" +Write-Host "" +Write-Host " 2. Place Windows Server 2025 ISO:" +Write-Host " $CIRoot\ISO\" +Write-Host "" +Write-Host " 3. Provision the template VM:" +Write-Host " a. Create VM in VMware Workstation (4 vCPU, 6 GB RAM, 80 GB thin)" +Write-Host " VMX: $CIRoot\Templates\WinBuild\CI-WinBuild.vmx" +Write-Host " NIC: VMnet8 (NAT) — internet required during provisioning" +Write-Host " b. Install Windows Server 2025 + enable WinRM inside VM" +Write-Host " c. From this host run:" +Write-Host " cd $scriptDir\template" +Write-Host " .\Prepare-TemplateSetup.ps1 -VMIPAddress -SkipWindowsUpdate" +Write-Host " d. Shut down VM, take snapshot named exactly: BaseClean" +Write-Host "" +Write-Host " 4. Register SSH key with Gitea:" +Write-Host " Gitea UI → User Settings → SSH Keys → Add Key" +Write-Host " (key: $env:USERPROFILE\.ssh\id_rsa.pub)" +Write-Host "" +Write-Host " 5. Verify act_runner is Online in Gitea:" +Write-Host " $GiteaUrl/-/admin/runners" +Write-Host ""