Files
local-ci-cd-system/docs/ARCHITECTURE.md
T
Simone 68cde01c9d security(sprint1): §1.1/1.3/1.4 — WinRM HTTPS/5986, SHA256 pinning infra, IP regex per-octet
§1.4 — Replace loose IP ValidatePattern with per-octet regex in 4 scripts
  (Invoke-CIJob, Invoke-RemoteBuild, Get-BuildArtifacts, Wait-VMReady)

§1.1 — Migrate all WinRM connections from HTTP/5985 to HTTPS/5986
  - Deploy post-install.ps1: remove AllowUnencrypted=true; self-signed cert + HTTPS listener
    already present; firewall rule restricted to 192.168.79.0/24
  - Setup-WinBuild2025.ps1: assert AllowUnencrypted=false
  - Prepare-WinBuild2025.ps1: preflight uses TCP/5986; Test-WSMan -UseSSL -Port 5986;
    New-PSSession -UseSSL -Port 5986; host AllowUnencrypted save/restore removed (not needed for HTTPS)
  - Invoke-RemoteBuild, Get-BuildArtifacts: New-PSSession -UseSSL -Port 5986 -Authentication Basic
  - Wait-VMReady: New-WSManSessionOption + Test-WSMan -Port 5986 -UseSSL
  - Validate-DeployState, Validate-SetupState: Invoke-Command -UseSSL -Port 5986,
    AllowUnencrypted check → false; AllowUnencrypted host-side removed from both
  - Docs: PLAN, README, ARCHITECTURE, BEST-PRACTICES, HOST-SETUP, WINDOWS-TEMPLATE-SETUP,
    LINUX-TEMPLATE-SETUP, TODO updated

§1.3 — SHA256 hash pinning infrastructure
  - Assert-Hash function + $script:Hashes hashtable added to Setup-WinBuild2025.ps1
  - Assert-Hash called after dotnet-install.ps1, python installer, vs_buildtools.exe downloads
  - Hashes initialized to '' (warn-skip); must be filled before next snapshot refresh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 14:46:41 +02:00

195 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture — Local CI/CD System
## Overview
A fully local, isolated CI/CD pipeline running on a Windows host using:
- **Gitea** as the self-hosted Git server + CI platform
- **act_runner** as the job executor (runs on the Windows host)
- **VMware Workstation** for ephemeral build VMs (linked clones)
- **WinRM / PowerShell Remoting** for communication with build VMs
- **MSBuild / dotnet CLI** executing inside the guest VM only
The runner host **never executes build tools directly**. Its only role is orchestration: creating, driving, and destroying VMs.
---
## System Diagram
```
┌─────────────────────────────────────────────────────────────────────────┐
│ DEVELOPER WORKSTATION │
│ │
│ git push → Gitea (self-hosted, LAN) │
└───────────────────────────┬─────────────────────────────────────────────┘
│ HTTP/HTTPS (Gitea Actions API)
┌─────────────────────────────────────────────────────────────────────────┐
│ WINDOWS HOST (i9-10900X · 64 GB RAM · NVMe SSD) │
│ │
│ ┌───────────────────┐ polls job queue ┌──────────────────┐ │
│ │ Gitea Server │◄────────────────────────►│ act_runner │ │
│ │ (Actions API) │ reports status/logs │ (Windows svc) │ │
│ └───────────────────┘ └────────┬─────────┘ │
│ │ │
│ calls Invoke-CIJob.ps1 │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Orchestrator Scripts (PowerShell) │ │
│ │ │ │
│ │ New-BuildVM.ps1 ──► vmrun.exe (linked clone from template) │ │
│ │ Wait-VMReady.ps1 ─► Test-WSMan poll loop │ │
│ │ Invoke-RemoteBuild.ps1 ► PSSession + Invoke-Command │ │
│ │ Get-BuildArtifacts.ps1 ► Copy-Item -FromSession │ │
│ │ Remove-BuildVM.ps1 ───► vmrun stop + deleteVM │ │
│ └───────────────────────────────┬───────────────────────────────────┘ │
│ │ vmrun.exe (VMware CLI) │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ VMware Workstation │ │
│ │ │ │
│ │ Template VM ──snapshot "BaseClean"──┐ │ │
│ │ ├── Clone_Job_001.vmx │ │
│ │ ├── Clone_Job_002.vmx │ │
│ │ └── Clone_Job_003.vmx │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │ WinRM :5986 HTTPS (192.168.79.0/24 NAT) │
└──────────────────────────────────┼──────────────────────────────────────┘
┌────────────────────────┼──────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Build VM #1 │ │ Build VM #2 │ │ Build VM #N │
│ (ephemeral) │ │ (ephemeral) │ │ (ephemeral) │
│ │ │ │ │ │
│ VS Build Tools │ │ VS Build Tools │ │ VS Build Tools │
│ 2026 (v145) │ │ 2026 (v145) │ │ 2026 (v145) │
│ .NET SDK 10 │ │ .NET SDK 10 │ │ .NET SDK 10 │
│ Python 3.13.3 │ │ Python 3.13.3 │ │ Python 3.13.3 │
│ WinRM enabled │ │ WinRM enabled │ │ WinRM enabled │
│ │ │ │ │ │
│ > unzip src │ │ > unzip src │ │ > unzip src │
│ > python build │ │ > python build │ │ > python build │
│ > dist/ │ │ > dist/ │ │ > dist/ │
│ │ │ │ │ │
│ [DESTROYED] │ │ [DESTROYED] │ │ [DESTROYED] │
└──────────────────┘ └──────────────────┘ └──────────────────┘
```
---
## Component Descriptions
### Gitea (Self-hosted Git + CI)
- Hosts Git repositories
- Provides Gitea Actions (GitHub Actions-compatible YAML workflow engine)
- Manages job queue and reports build status to commits/PRs
- Stores build artifacts uploaded by act_runner
### act_runner (Job Executor)
- Runs as a Windows service on the host
- Polls Gitea Actions API for pending jobs
- Executes `.gitea/workflows/*.yml` steps
- **Configured with `container.enabled: false`** — does NOT use Docker
- Each step runs as a PowerShell command on the host (orchestration only)
- Supports `capacity: N` for concurrent job execution (N parallel VMs)
### VMware Workstation + vmrun CLI
- Manages the template VM and all ephemeral clones
- `vmrun.exe` is the command-line interface for all VM lifecycle operations
- **Linked clones** are used: each build VM is a delta clone from a frozen template snapshot
- Clone creation: ~515 seconds (vs 60120 seconds for full clone)
- Disk footprint: ~25 GB per clone (vs 4080 GB for full clone)
- Tradeoff: template snapshot must remain intact
### WinRM / PowerShell Remoting
- Default transport for remote build execution inside VMs
- Port: **5986** (HTTPS, self-signed cert, `AllowUnencrypted=false`)
- Authentication: **Basic over HTTPS** (`-SkipCACheck` for lab self-signed cert)
- Used for:
- Transferring source: `Compress-Archive` on host → WinRM copy → `Expand-Archive` in guest
- Running build commands (`Invoke-Command`)
- Packaging artifacts (`Compress-Archive` in guest) and collecting via `Copy-Item -FromSession`
### Build Toolchain (inside VM only)
- Visual Studio Build Tools **2026** (MSBuild 18.5.4 / toolset v145)
- .NET SDK **10.0.203**
- Python **3.13.3** (per build script personalizzati come `build_plugin.py`)
- NuGet CLI (optional, dotnet restore covers most cases)
- Git: NOT installed by default — source arrives via WinRM zip transfer. See TODO.md `-UseGitClone` for the in-VM git clone opt-in path.
---
## Network Topology
```
Host NIC (LAN) → 192.168.1.x → Gitea server (10.10.20.11:3100), developer machines
VMware VMnet8 (NAT) → 192.168.79.0/24 → Build VMs (DHCP, internet access via NAT)
Build VMs:
Clone_Job_001 → 192.168.79.131
Clone_Job_002 → 192.168.79.132
...
Clone_Job_N → 192.168.79.1xx
WinRM port 5986 (HTTPS, self-signed) on each VM IP.
VMs have internet access via NAT — required for pip/nuget during build.
```
> **Note:** VMnet8 NAT gives VMs internet access. The host detects the VM IP via
> `vmrun getGuestIPAddress` after boot — no static IP assignment needed.
---
## VM Lifecycle
```
1. TEMPLATE (static, locked)
└─ Snapshot: "BaseClean" ───────────────────────┐
2. CLONE CREATION │
vmrun clone <template.vmx> <clone.vmx> linked ──►│
Duration: ~515 seconds │
3. START │
vmrun start <clone.vmx> nogui │
Duration: ~2040 seconds boot │
4. READINESS CHECK │
Poll: getState=running → ping → Test-WSMan │
Duration: ~3090 seconds total │
5. BUILD │
Copy script → Invoke-Command → collect artifacts │
Duration: varies (seconds to minutes) │
6. DESTROY │
vmrun stop hard -> vmrun deleteVM → Remove-Item │
Duration: ~510 seconds │
Result: zero disk footprint remaining ▼
```
---
## Resource Limits & Parallel Capacity
| Resource | Host Total | Per VM (est.) | Max VMs |
| --------------- | ---------------------- | --------------- | ---------------- |
| RAM | 64 GB | ~68 GB | 810 |
| CPU | 20 threads (i9-10900X) | ~4 vCPU | 5 |
| NVMe IOPS | High | Clone delta I/O | ~68 |
| **Recommended** | — | — | **4 concurrent** |
`act_runner` `capacity: 4` is the conservative default. Raise to 6 after benchmarking.
---
## Security Boundaries
- Build VMs are on VMnet8 NAT (192.168.79.0/24) — host reaches VMs, VMs have internet access
- WinRM credentials are stored in Windows Credential Manager on host (not hardcoded)
- Each VM is fully destroyed after the job — no state carries between builds
- Runner host does not expose any ports to the build VM network
- Source code is transferred via zip (Compress-Archive → WinRM copy → Expand-Archive in guest) — no PAT is ever present inside the VM; VMs have NAT internet for pip/nuget at build time