feat: add Setup-Host.ps1 host bootstrap script (dirs, credentials, act_runner service)

This commit is contained in:
Simone
2026-05-09 00:11:33 +02:00
parent a05855ddfe
commit 71f1aa2e3b
2 changed files with 355 additions and 0 deletions
+2
View File
@@ -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
+353
View File
@@ -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: <CIRoot>\act_runner\act_runner.exe
.PARAMETER ActRunnerConfigYaml
Path to act_runner config.yaml to copy into <CIRoot>\act_runner\.
If not specified, copies runner\config.yaml from the repo (if it exists
next to this script).
Default: <ScriptDir>\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 <VM_IP> -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 ""