feat: add Setup-Host.ps1 host bootstrap script (dirs, credentials, act_runner service)
This commit is contained in:
@@ -61,6 +61,8 @@ Ogni build gira in una VM Windows Server 2025 **usa e getta**, ripristinata dall
|
|||||||
## Struttura repository
|
## Struttura repository
|
||||||
|
|
||||||
```
|
```
|
||||||
|
├── Setup-Host.ps1 # Bootstrap host: dirs F:\CI\, credenziali, act_runner service
|
||||||
|
│
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ ├── Invoke-CIJob.ps1 # Orchestratore principale
|
│ ├── Invoke-CIJob.ps1 # Orchestratore principale
|
||||||
│ ├── New-BuildVM.ps1 # Linked clone da template
|
│ ├── New-BuildVM.ps1 # Linked clone da template
|
||||||
|
|||||||
+353
@@ -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 ""
|
||||||
Reference in New Issue
Block a user