mirror of
https://github.com/GoodOlClint/PSProxmoxVE.git
synced 2026-07-26 07:58:14 +00:00
refactor: redesign dev.ps1 with switch-based CLI and -Tests filter
Replace positional $Command parameter with switches that can be combined: -Provision -Integration -Cleanup -DockerHost 172.16.40.113 New -Tests parameter filters integration tests by area name: -Tests Connection,VMs,Snapshots New -Version parameter (alias for old PveVersion): -Version 9 Integration without Provision checks for config.json and errors if environment is not ready. Actions execute in logical order: Stop → Rebuild → Shell → Build → Test → Provision → Integration → Cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+131
-119
@@ -7,87 +7,111 @@
|
||||
Manages Docker-based dev containers for building, testing, and running
|
||||
integration tests for PSProxmoxVE. Works on Windows, macOS, and Linux.
|
||||
|
||||
Use switches to compose actions: -Provision -Integration -Cleanup can
|
||||
be combined in a single invocation.
|
||||
|
||||
For x86-only commands (integration, provision, cleanup), use -DockerHost
|
||||
to run containers on a remote Docker host via SSH. The script syncs the
|
||||
repo to the remote host automatically.
|
||||
|
||||
.PARAMETER Command
|
||||
The action to perform:
|
||||
shell - Open pwsh in the dev container (default)
|
||||
build - Build the module inside the container
|
||||
test - Run unit tests (ARM + x86)
|
||||
integration - Provision nested PVE VMs, run integration tests, cleanup (x86 only)
|
||||
provision - Provision nested PVE VMs only, no tests (x86 only)
|
||||
cleanup - Destroy provisioned VMs (x86 only)
|
||||
stop - Stop all containers
|
||||
rebuild - Rebuild container image(s)
|
||||
.PARAMETER Shell
|
||||
Open an interactive pwsh shell in the dev container.
|
||||
|
||||
.PARAMETER PveVersion
|
||||
.PARAMETER Build
|
||||
Build the module inside the container.
|
||||
|
||||
.PARAMETER Test
|
||||
Run unit tests (Pester, excluding Integration tag).
|
||||
|
||||
.PARAMETER Provision
|
||||
Provision nested PVE VMs (x86 only). Required before -Integration
|
||||
unless VMs are already running.
|
||||
|
||||
.PARAMETER Integration
|
||||
Run integration tests against provisioned PVE VMs.
|
||||
|
||||
.PARAMETER Cleanup
|
||||
Destroy provisioned VMs (x86 only).
|
||||
|
||||
.PARAMETER Stop
|
||||
Stop all containers.
|
||||
|
||||
.PARAMETER Rebuild
|
||||
Rebuild container images from scratch.
|
||||
|
||||
.PARAMETER Tests
|
||||
Filter integration tests by area name. Comma-separated list of test
|
||||
area names that match the numbered file prefixes. Examples:
|
||||
-Tests Connection,Nodes # runs 00_Connection + 01_Nodes
|
||||
-Tests VMs,Snapshots # runs 06_VMs + 07_Snapshots
|
||||
-Tests Cluster # runs 16_Cluster
|
||||
When omitted, all integration test files are run.
|
||||
|
||||
.PARAMETER Version
|
||||
PVE version to test against (8, 9, or all). Default: all.
|
||||
Only used with 'integration' and 'provision' commands.
|
||||
|
||||
.PARAMETER NoCleanup
|
||||
When used with 'integration', skips cleanup after tests complete.
|
||||
Nested PVE VMs are left running for inspection or re-testing.
|
||||
Run './tests/dev.ps1 cleanup' to destroy them later.
|
||||
|
||||
.PARAMETER DockerHost
|
||||
SSH destination for a remote Docker host (e.g. user@runner-vm).
|
||||
When set, the repo is rsynced to the remote host and all Docker
|
||||
commands run against the remote daemon. Useful for running x86
|
||||
containers from an ARM Mac.
|
||||
SSH destination for a remote Docker host (e.g. 172.16.40.113).
|
||||
|
||||
Prerequisites on the remote host:
|
||||
- Docker installed
|
||||
- SSH user in the 'docker' group (sudo usermod -aG docker $USER)
|
||||
- SSH key-based auth from the local machine
|
||||
.PARAMETER NoCleanup
|
||||
When used with -Integration, skips cleanup after tests complete.
|
||||
|
||||
.EXAMPLE
|
||||
./tests/dev.ps1
|
||||
./tests/dev.ps1 -Shell
|
||||
# Opens a pwsh shell in the dev container
|
||||
|
||||
.EXAMPLE
|
||||
./tests/dev.ps1 test
|
||||
./tests/dev.ps1 -Build -Test
|
||||
# Builds the module and runs unit tests
|
||||
|
||||
.EXAMPLE
|
||||
./tests/dev.ps1 integration -DockerHost user@runner-vm
|
||||
# Syncs repo to runner-vm, provisions nested PVE, runs tests, cleans up
|
||||
./tests/dev.ps1 -Provision -Integration -Cleanup -DockerHost 172.16.40.113
|
||||
# Full lifecycle: provision, test, cleanup on remote host
|
||||
|
||||
.EXAMPLE
|
||||
./tests/dev.ps1 integration 9 -DockerHost user@runner-vm
|
||||
# Same but only for PVE 9
|
||||
./tests/dev.ps1 -Integration -Tests Connection,VMs -Version 9 -DockerHost 172.16.40.113
|
||||
# Run only Connection and VMs integration tests for PVE 9
|
||||
|
||||
.EXAMPLE
|
||||
./tests/dev.ps1 -Provision -Integration -Tests Cluster,HA -Version 9 -Cleanup -DockerHost 172.16.40.113
|
||||
# Provision, run cluster+HA tests for PVE 9, cleanup
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Position = 0)]
|
||||
[ValidateSet('shell', 'build', 'test', 'integration', 'provision', 'cleanup', 'stop', 'rebuild')]
|
||||
[string] $Command = 'shell',
|
||||
[switch] $Shell,
|
||||
[switch] $Build,
|
||||
[switch] $Test,
|
||||
[switch] $Provision,
|
||||
[switch] $Integration,
|
||||
[switch] $Cleanup,
|
||||
[switch] $Stop,
|
||||
[switch] $Rebuild,
|
||||
|
||||
[Parameter(Position = 1)]
|
||||
[string] $PveVersion = 'all',
|
||||
[string[]] $Tests,
|
||||
|
||||
[Alias('PveVersion')]
|
||||
[string] $Version = 'all',
|
||||
|
||||
[Parameter()]
|
||||
[string] $DockerHost,
|
||||
|
||||
[Parameter()]
|
||||
[Alias('k')]
|
||||
[switch] $NoCleanup
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# If no switches specified, default to -Shell
|
||||
$anySwitchSet = $Shell -or $Build -or $Test -or $Provision -or $Integration -or $Cleanup -or $Stop -or $Rebuild
|
||||
if (-not $anySwitchSet) {
|
||||
$Shell = $true
|
||||
}
|
||||
|
||||
# Resolve repo root (parent of tests/)
|
||||
$RepoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Push-Location $RepoRoot
|
||||
try {
|
||||
|
||||
# ── Remote Docker host support ────────────────────────────────────────
|
||||
# When -DockerHost is specified, we rsync the repo (including .env.test)
|
||||
# to the remote host and set DOCKER_HOST so all docker/compose commands
|
||||
# execute on the remote daemon. The compose file's volume mount (..:/repo)
|
||||
# and build context reference the remote copy.
|
||||
|
||||
$RemoteRepoPath = $null
|
||||
|
||||
if ($DockerHost) {
|
||||
@@ -95,11 +119,9 @@ if ($DockerHost) {
|
||||
$env:DOCKER_HOST = "ssh://$DockerHost"
|
||||
|
||||
Write-Host "Syncing repo to ${DockerHost}:${RemoteRepoPath}..."
|
||||
# Create remote directory
|
||||
ssh $DockerHost "mkdir -p $RemoteRepoPath"
|
||||
if ($LASTEXITCODE -ne 0) { throw "Failed to create remote directory" }
|
||||
|
||||
# Rsync repo to remote host, excluding build artifacts
|
||||
rsync -az --delete `
|
||||
--exclude 'bin/' `
|
||||
--exclude 'obj/' `
|
||||
@@ -114,19 +136,12 @@ if ($DockerHost) {
|
||||
Write-Host "Using remote Docker host: $DockerHost"
|
||||
}
|
||||
|
||||
# When running remotely, docker compose reads the compose file locally but
|
||||
# volume mounts resolve on the remote host. We generate a temporary override
|
||||
# file that remaps volume mounts to the rsynced remote path.
|
||||
$ComposeFile = 'tests/docker-compose.test.yml'
|
||||
$ComposeArgs = @('-f', $ComposeFile)
|
||||
$OverrideFile = $null
|
||||
|
||||
if ($RemoteRepoPath) {
|
||||
$OverrideFile = Join-Path ([System.IO.Path]::GetTempPath()) 'docker-compose.remote-override.yml'
|
||||
|
||||
# Override volume mounts to point at the rsynced remote repo path.
|
||||
# env_file is NOT overridden — compose reads it locally and injects
|
||||
# the values as container env vars, which is what we want.
|
||||
@"
|
||||
services:
|
||||
dev:
|
||||
@@ -146,7 +161,6 @@ $InfraContainer = 'psproxmoxve-dev-infra'
|
||||
$RunIntegration = 'tests/infrastructure/scripts/run-integration.sh'
|
||||
|
||||
function Start-DevContainer {
|
||||
# 'up -d' is idempotent: starts if stopped, recreates if config/env changed, no-ops if current
|
||||
docker compose @ComposeArgs up -d dev
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Failed to start dev container' }
|
||||
}
|
||||
@@ -166,78 +180,76 @@ echo 'Module installed to /usr/local/share/powershell/Modules/PSProxmoxVE'
|
||||
if ($LASTEXITCODE -ne 0) { throw "Module build failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
switch ($Command) {
|
||||
'shell' {
|
||||
if ($DockerHost) {
|
||||
Write-Warning "Interactive shell over remote Docker is not supported. Use: ssh $DockerHost 'docker exec -it $DevContainer pwsh -NoProfile'"
|
||||
return
|
||||
}
|
||||
Start-DevContainer
|
||||
docker exec -it $DevContainer pwsh -NoProfile
|
||||
}
|
||||
# Build test filter argument for run-integration.sh
|
||||
$TestFilter = ''
|
||||
if ($Tests) {
|
||||
$TestFilter = ($Tests -join ',')
|
||||
}
|
||||
|
||||
'build' {
|
||||
Start-DevContainer
|
||||
Invoke-BuildModule $DevContainer
|
||||
}
|
||||
# ── Execute actions in order ──────────────────────────────────────────
|
||||
|
||||
'test' {
|
||||
Start-DevContainer
|
||||
Invoke-BuildModule $DevContainer
|
||||
docker exec $DevContainer pwsh -NoProfile -Command @'
|
||||
$config = New-PesterConfiguration
|
||||
$config.Run.Path = 'tests/PSProxmoxVE.Tests'
|
||||
$config.Run.Exit = $true
|
||||
$config.Filter.ExcludeTag = @('Integration')
|
||||
$config.Output.Verbosity = 'Detailed'
|
||||
Invoke-Pester -Configuration $config
|
||||
if ($Stop) {
|
||||
docker compose @ComposeArgs --profile infra down
|
||||
}
|
||||
|
||||
if ($Rebuild) {
|
||||
docker compose @ComposeArgs --profile infra down
|
||||
docker compose @ComposeArgs build --no-cache dev
|
||||
docker compose @ComposeArgs --profile infra build --no-cache dev-infra
|
||||
docker compose @ComposeArgs up -d dev
|
||||
}
|
||||
|
||||
if ($Shell) {
|
||||
if ($DockerHost) {
|
||||
Write-Warning "Interactive shell over remote Docker is not supported. Use: ssh $DockerHost 'docker exec -it $InfraContainer pwsh -NoProfile'"
|
||||
return
|
||||
}
|
||||
Start-DevContainer
|
||||
docker exec -it $DevContainer pwsh -NoProfile
|
||||
}
|
||||
|
||||
if ($Build) {
|
||||
Start-DevContainer
|
||||
Invoke-BuildModule $DevContainer
|
||||
}
|
||||
|
||||
if ($Test) {
|
||||
Start-DevContainer
|
||||
Invoke-BuildModule $DevContainer
|
||||
docker exec $DevContainer pwsh -NoProfile -Command @'
|
||||
$config = New-PesterConfiguration
|
||||
$config.Run.Path = 'tests/PSProxmoxVE.Tests'
|
||||
$config.Run.Exit = $true
|
||||
$config.Filter.ExcludeTag = @('Integration')
|
||||
$config.Output.Verbosity = 'Detailed'
|
||||
Invoke-Pester -Configuration $config
|
||||
'@
|
||||
if ($LASTEXITCODE -ne 0) { throw "Unit tests failed (exit code $LASTEXITCODE)" }
|
||||
if ($LASTEXITCODE -ne 0) { throw "Unit tests failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
if ($Provision) {
|
||||
Start-InfraContainer
|
||||
docker exec $InfraContainer bash $RunIntegration provision
|
||||
if ($LASTEXITCODE -ne 0) { throw "Provisioning failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
if ($Integration) {
|
||||
Start-InfraContainer
|
||||
|
||||
# Verify environment is ready (config file exists from provisioning)
|
||||
$configCheck = docker exec $InfraContainer bash -c 'test -f /tmp/pve-integration/config.json && echo OK || echo MISSING'
|
||||
if ($configCheck.Trim() -eq 'MISSING' -and -not $Provision) {
|
||||
throw "Integration environment not ready. Run with -Provision first, or use -Provision -Integration together."
|
||||
}
|
||||
|
||||
'integration' {
|
||||
# Full lifecycle: provision nested PVE VMs -> test -> cleanup (x86 only)
|
||||
# Requires PVE_ENDPOINT, PVE_API_TOKEN, PVE_TARGET_NODE, PVE_PASSWORD in .env.test
|
||||
# run-integration.sh handles module build if no pre-built artifact exists
|
||||
Start-InfraContainer
|
||||
if ($NoCleanup) {
|
||||
# Provision and test separately — leave VMs running for inspection
|
||||
docker exec $InfraContainer bash $RunIntegration provision
|
||||
if ($LASTEXITCODE -ne 0) { throw "Provisioning failed (exit code $LASTEXITCODE)" }
|
||||
docker exec $InfraContainer bash $RunIntegration test $PveVersion
|
||||
if ($LASTEXITCODE -ne 0) { throw "Integration tests failed (exit code $LASTEXITCODE)" }
|
||||
Write-Host 'VMs left running (-NoCleanup). Run ./tests/dev.ps1 cleanup to destroy them.'
|
||||
} else {
|
||||
docker exec $InfraContainer bash $RunIntegration all $PveVersion
|
||||
if ($LASTEXITCODE -ne 0) { throw "Integration tests failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
}
|
||||
docker exec $InfraContainer bash $RunIntegration test $Version $TestFilter
|
||||
if ($LASTEXITCODE -ne 0) { throw "Integration tests failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
'provision' {
|
||||
# Provision nested PVE VMs only, without running tests (x86 only)
|
||||
# Useful for iterating: provision once, then shell in and run tests manually
|
||||
Start-InfraContainer
|
||||
docker exec $InfraContainer bash $RunIntegration provision
|
||||
if ($LASTEXITCODE -ne 0) { throw "Provisioning failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
'cleanup' {
|
||||
# Destroy provisioned VMs
|
||||
Start-InfraContainer
|
||||
docker exec $InfraContainer bash $RunIntegration cleanup
|
||||
if ($LASTEXITCODE -ne 0) { throw "Cleanup failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
'stop' {
|
||||
docker compose @ComposeArgs --profile infra down
|
||||
}
|
||||
|
||||
'rebuild' {
|
||||
docker compose @ComposeArgs --profile infra down
|
||||
docker compose @ComposeArgs build --no-cache dev
|
||||
docker compose @ComposeArgs --profile infra build --no-cache dev-infra
|
||||
docker compose @ComposeArgs up -d dev
|
||||
}
|
||||
if ($Cleanup) {
|
||||
Start-InfraContainer
|
||||
docker exec $InfraContainer bash $RunIntegration cleanup
|
||||
if ($LASTEXITCODE -ne 0) { throw "Cleanup failed (exit code $LASTEXITCODE)" }
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user