KHZ.psm1
|
# KHZ - Sovereign DevOps Pipeline Module # By GRATECH / Khawrizm Labs # https://github.com/Grar00t #region Core Engine $KHZ_VERSION = "1.0.0" $KHZ_AUTHOR = "Sulaiman Alshammari (GRATECH)" function Write-KHZLog { param( [string]$Message, [ValidateSet("INFO","WARN","ERROR","SUCCESS","STEP")] [string]$Level = "INFO" ) $colors = @{ INFO = "Cyan" WARN = "Yellow" ERROR = "Red" SUCCESS = "Green" STEP = "Magenta" } $icons = @{ INFO = "◆" WARN = "▲" ERROR = "✖" SUCCESS = "✔" STEP = "▶" } $ts = Get-Date -Format "HH:mm:ss" Write-Host "[$ts] " -NoNewline -ForegroundColor DarkGray Write-Host "$($icons[$Level]) " -NoNewline -ForegroundColor $colors[$Level] Write-Host $Message -ForegroundColor $colors[$Level] } #endregion #region Environment Doctor function Invoke-KHZDoctor { <# .SYNOPSIS Diagnoses and auto-fixes the local environment before running a pipeline. .DESCRIPTION Checks for required tools (git, dotnet, ssh, etc.) and installs missing ones automatically. .EXAMPLE Invoke-KHZDoctor .EXAMPLE Invoke-KHZDoctor -Fix #> param( [switch]$Fix, [switch]$Quiet ) Write-KHZLog "KHZ Doctor — Environment Scan v$KHZ_VERSION" "STEP" Write-Host "" $results = @() $checks = @( @{ Name = "git"; Cmd = "git --version"; Install = "winget install Git.Git -e --source winget" }, @{ Name = "git-lfs"; Cmd = "git lfs version"; Install = "git lfs install" }, @{ Name = "dotnet"; Cmd = "dotnet --version"; Install = "winget install Microsoft.DotNet.SDK.8" }, @{ Name = "ssh"; Cmd = "ssh -V"; Install = "Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0" }, @{ Name = "powershell"; Cmd = '$PSVersionTable.PSVersion.ToString()'; Install = "winget install Microsoft.PowerShell" } ) foreach ($check in $checks) { try { $output = Invoke-Expression $check.Cmd 2>&1 $ver = ($output | Select-Object -First 1) -replace '^[^\d]*','' | Select-Object -First 1 Write-KHZLog "$($check.Name.PadRight(15)) OK ($ver)" "SUCCESS" $results += [PSCustomObject]@{ Tool=$check.Name; Status="OK"; Version=$ver } } catch { Write-KHZLog "$($check.Name.PadRight(15)) MISSING" "WARN" $results += [PSCustomObject]@{ Tool=$check.Name; Status="MISSING"; Version="" } if ($Fix) { Write-KHZLog "Auto-fixing: $($check.Name)..." "STEP" try { Invoke-Expression $check.Install | Out-Null Write-KHZLog "$($check.Name) installed successfully" "SUCCESS" } catch { Write-KHZLog "Failed to auto-install $($check.Name): $_" "ERROR" } } } } Write-Host "" $missing = $results | Where-Object { $_.Status -eq "MISSING" } if ($missing.Count -eq 0) { Write-KHZLog "All checks passed. Environment is ready." "SUCCESS" } else { Write-KHZLog "$($missing.Count) tool(s) missing. Run: Invoke-KHZDoctor -Fix" "WARN" } return $results } #endregion #region Pipeline Engine function Invoke-KHZPipeline { <# .SYNOPSIS Runs a sovereign DevOps pipeline — no cloud, no YAML hell. .DESCRIPTION Executes pipeline stages: Validate → Build → Test → Deploy → Notify Each stage is a standalone PowerShell node. .PARAMETER ProjectPath Path to the project directory. .PARAMETER Stages Array of stages to run. Default: Validate, Build, Test, Deploy .PARAMETER Target Deployment target (local path or SSH host). .PARAMETER Config Path to pipeline JSON config file. .EXAMPLE Invoke-KHZPipeline -ProjectPath D:\Casper.DataForge .EXAMPLE Invoke-KHZPipeline -ProjectPath D:\myapp -Stages Build,Test -Target 192.168.0.241 #> param( [Parameter(Mandatory=$true)] [string]$ProjectPath, [string[]]$Stages = @("Validate","Build","Test","Deploy"), [string]$Target = "local", [string]$Config = "", [switch]$DryRun, [switch]$StopOnFail ) $startTime = Get-Date $results = @() Write-Host "" Write-Host " ╔══════════════════════════════════════╗" -ForegroundColor Cyan Write-Host " ║ KHZ Sovereign Pipeline v$KHZ_VERSION ║" -ForegroundColor Cyan Write-Host " ╚══════════════════════════════════════╝" -ForegroundColor Cyan Write-Host "" # Load config if provided $cfg = @{} if ($Config -and (Test-Path $Config)) { $cfg = Get-Content $Config | ConvertFrom-Json -AsHashtable Write-KHZLog "Config loaded: $Config" "INFO" } # Validate project path if (-not (Test-Path $ProjectPath)) { Write-KHZLog "Project path not found: $ProjectPath" "ERROR" Write-KHZLog "Tip: Check the path and try again." "WARN" return } Write-KHZLog "Project : $ProjectPath" "INFO" Write-KHZLog "Target : $Target" "INFO" Write-KHZLog "Stages : $($Stages -join ' → ')" "INFO" Write-KHZLog "DryRun : $DryRun" "INFO" Write-Host "" # Draw pipeline $pipelineStr = $Stages -join " ──▶ " Write-Host " Pipeline: " -NoNewline -ForegroundColor DarkGray Write-Host $pipelineStr -ForegroundColor Magenta Write-Host "" foreach ($stage in $Stages) { $stageStart = Get-Date Write-Host " ┌─ Stage: " -NoNewline -ForegroundColor DarkGray Write-Host $stage -ForegroundColor Magenta Write-Host " │" -ForegroundColor DarkGray $status = "SKIPPED" try { switch ($stage.ToUpper()) { "VALIDATE" { Write-KHZLog " Validating project structure..." "STEP" $files = Get-ChildItem $ProjectPath -Recurse -File | Measure-Object Write-KHZLog " Files found: $($files.Count)" "INFO" # Check for common project files $markers = @("*.csproj","*.sln","*.ps1","package.json","Makefile","Cargo.toml") $found = $false foreach ($m in $markers) { if (Get-ChildItem $ProjectPath -Filter $m -Recurse -ErrorAction SilentlyContinue) { Write-KHZLog " Project type detected: $m" "SUCCESS" $found = $true break } } if (-not $found) { Write-KHZLog " No standard project marker found — proceeding anyway" "WARN" } $status = "SUCCESS" } "BUILD" { if ($DryRun) { Write-KHZLog " [DryRun] Would run: dotnet build" "INFO" $status = "DRYRUN" } else { $csproj = Get-ChildItem $ProjectPath -Filter "*.csproj" -Recurse | Select-Object -First 1 if ($csproj) { Write-KHZLog " Building: $($csproj.Name)" "STEP" $buildOut = & dotnet build $csproj.FullName --nologo 2>&1 if ($LASTEXITCODE -eq 0) { Write-KHZLog " Build succeeded" "SUCCESS" $status = "SUCCESS" } else { Write-KHZLog " Build failed" "ERROR" $buildOut | Select-Object -Last 5 | ForEach-Object { Write-KHZLog " $_" "ERROR" } $status = "FAILED" } } else { Write-KHZLog " No .csproj found — skipping dotnet build" "WARN" $status = "SKIPPED" } } } "TEST" { if ($DryRun) { Write-KHZLog " [DryRun] Would run: dotnet test" "INFO" $status = "DRYRUN" } else { $testProj = Get-ChildItem $ProjectPath -Filter "*.Tests.csproj" -Recurse | Select-Object -First 1 if ($testProj) { Write-KHZLog " Running tests: $($testProj.Name)" "STEP" $testOut = & dotnet test $testProj.FullName --nologo 2>&1 if ($LASTEXITCODE -eq 0) { Write-KHZLog " Tests passed" "SUCCESS" $status = "SUCCESS" } else { Write-KHZLog " Tests failed" "ERROR" $status = "FAILED" } } else { Write-KHZLog " No test project found — skipping" "WARN" $status = "SKIPPED" } } } "DEPLOY" { if ($DryRun) { Write-KHZLog " [DryRun] Would deploy to: $Target" "INFO" $status = "DRYRUN" } else { if ($Target -eq "local") { Write-KHZLog " Deploying locally — nothing to do" "INFO" $status = "SUCCESS" } else { Write-KHZLog " Deploying to: $Target via SSH" "STEP" try { $sshTest = & ssh -o ConnectTimeout=5 -o BatchMode=yes $Target "echo KHZ_OK" 2>&1 if ($sshTest -match "KHZ_OK") { Write-KHZLog " SSH connection OK" "SUCCESS" $status = "SUCCESS" } else { Write-KHZLog " SSH connection failed" "ERROR" $status = "FAILED" } } catch { Write-KHZLog " SSH error: $_" "ERROR" $status = "FAILED" } } } } "NOTIFY" { Write-KHZLog " Pipeline notification sent" "SUCCESS" $status = "SUCCESS" } default { Write-KHZLog " Unknown stage: $stage" "WARN" $status = "SKIPPED" } } } catch { Write-KHZLog " Stage error: $_" "ERROR" $status = "FAILED" } $stageDuration = ((Get-Date) - $stageStart).TotalSeconds $statusColor = switch ($status) { "SUCCESS" { "Green" } "FAILED" { "Red" } "DRYRUN" { "Cyan" } default { "Yellow" } } Write-Host " └─ " -NoNewline -ForegroundColor DarkGray Write-Host "$status" -NoNewline -ForegroundColor $statusColor Write-Host " (${stageDuration}s)" -ForegroundColor DarkGray Write-Host "" $results += [PSCustomObject]@{ Stage = $stage Status = $status Duration = [math]::Round($stageDuration, 2) } if ($StopOnFail -and $status -eq "FAILED") { Write-KHZLog "StopOnFail triggered — aborting pipeline" "ERROR" break } } # Summary $totalDuration = ((Get-Date) - $startTime).TotalSeconds $failed = ($results | Where-Object { $_.Status -eq "FAILED" }).Count $success = ($results | Where-Object { $_.Status -eq "SUCCESS" }).Count Write-Host " ════════════════════════════════════════" -ForegroundColor DarkGray Write-Host " Summary" -ForegroundColor White Write-Host " ────────────────────────────────────────" -ForegroundColor DarkGray $results | ForEach-Object { $c = switch ($_.Status) { "SUCCESS"{"Green"} "FAILED"{"Red"} "DRYRUN"{"Cyan"} default{"Yellow"} } Write-Host " $($_.Stage.PadRight(12))" -NoNewline -ForegroundColor White Write-Host " $($_.Status.PadRight(10))" -NoNewline -ForegroundColor $c Write-Host " $($_.Duration)s" -ForegroundColor DarkGray } Write-Host " ────────────────────────────────────────" -ForegroundColor DarkGray Write-Host " Total: ${totalDuration}s | " -NoNewline -ForegroundColor DarkGray Write-Host "Success: $success" -NoNewline -ForegroundColor Green Write-Host " Failed: $failed" -ForegroundColor $(if($failed -gt 0){"Red"}else{"Green"}) Write-Host "" if ($failed -eq 0) { Write-KHZLog "Pipeline completed successfully" "SUCCESS" } else { Write-KHZLog "Pipeline completed with $failed failure(s)" "ERROR" } return $results } #endregion #region Utilities function Get-KHZVersion { <# .SYNOPSIS Returns the current KHZ module version. #> Write-KHZLog "KHZ Sovereign Pipeline Module v$KHZ_VERSION" "INFO" Write-KHZLog "Author: $KHZ_AUTHOR" "INFO" Write-KHZLog "Gallery: https://www.powershellgallery.com/packages/KHZ" "INFO" return $KHZ_VERSION } function New-KHZConfig { <# .SYNOPSIS Generates a starter pipeline config JSON file. .PARAMETER OutputPath Where to save the config file. .EXAMPLE New-KHZConfig -OutputPath D:\myproject\pipeline.json #> param( [string]$OutputPath = ".\khz-pipeline.json" ) $config = @{ project = "MyProject" version = "1.0.0" stages = @("Validate","Build","Test","Deploy") target = "local" notify = $false } | ConvertTo-Json -Depth 3 $config | Out-File $OutputPath -Encoding UTF8 Write-KHZLog "Config created: $OutputPath" "SUCCESS" } #endregion Export-ModuleMember -Function @( 'Invoke-KHZDoctor', 'Invoke-KHZPipeline', 'Get-KHZVersion', 'New-KHZConfig', 'Write-KHZLog' ) |