#!/usr/bin/env pwsh <# .SYNOPSIS Complete K-ArtSell Aegis v16.0 Deployment Automation Executes all phases: Backend Deploy → Frontend Build → Nginx Config → Verification .DESCRIPTION Automates the complete service deployment following AGENTS.md v16.0 principles: - Evidence-based (verify each step) - Necessity-driven (only required actions) - Strategic optimal (parallel where possible) - Transparent boundaries (clear status) - Complete automation (minimal manual intervention) .PARAMETER Environment Target environment: Development, Staging, Production .PARAMETER AutoDeploy If $true, deploy to production server automatically. If $false, prepare only. .EXAMPLE .\COMPLETE_DEPLOYMENT_AUTOMATION.ps1 -Environment Production -AutoDeploy $true #> param( [string]$Environment = "Production", [bool]$AutoDeploy = $false ) # ════════════════════════════════════════════════════════════════════════════════════ # CONFIGURATION # ════════════════════════════════════════════════════════════════════════════════════ $script:ProjectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) $script:FrontendRoot = Join-Path $script:ProjectRoot "frontend" $script:BackendRoot = Join-Path $script:ProjectRoot "src" "KArtSell.Host" $script:PublishDir = Join-Path $script:ProjectRoot "publish" $script:LogDir = Join-Path $script:ProjectRoot "logs" $script:EvidenceDir = Join-Path $script:ProjectRoot "evidence" "complete-deployment" # Production Server Configuration $ProductionConfig = @{ Host = "production-server.example.com" # CHANGE THIS User = "deploy" # CHANGE THIS BackendPath = "/opt/kartsell" # Remote path FrontendPath = "/var/www/kartsell/frontend" NginxConfigPath = "/etc/nginx/sites-available/kartsell" Port = 22 } $script:StartTime = Get-Date $script:Status = @{ Phase1 = "pending" Phase2 = "pending" Phase3 = "pending" Phase4 = "pending" Phase5 = "pending" } # ════════════════════════════════════════════════════════════════════════════════════ # FUNCTIONS # ════════════════════════════════════════════════════════════════════════════════════ function Log { param([string]$Message, [string]$Level = "INFO") $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") $color = switch($Level) { "SUCCESS" { "Green" } "ERROR" { "Red" } "WARN" { "Yellow" } "INFO" { "Cyan" } default { "White" } } Write-Host "[$timestamp] [$Level] $Message" -ForegroundColor $color Add-Content -Path (Join-Path $LogDir "deployment.log") -Value "[$timestamp] [$Level] $Message" } function Verify-Prerequisites { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 0: VERIFICATION & PREREQUISITES" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" # Check .NET SDK Log "Checking .NET SDK..." "INFO" $dotnetVersion = dotnet --version if ($LASTEXITCODE -ne 0) { Log "❌ .NET SDK not found" "ERROR" return $false } Log "✅ .NET SDK found: $dotnetVersion" "SUCCESS" # Check Node.js and pnpm Log "Checking Node.js and pnpm..." "INFO" $nodeVersion = node --version 2>$null $pnpmVersion = pnpm --version 2>$null if (-not $nodeVersion -or -not $pnpmVersion) { Log "❌ Node.js or pnpm not found" "ERROR" return $false } Log "✅ Node.js: $nodeVersion, pnpm: $pnpmVersion" "SUCCESS" # Create directories if (-not (Test-Path $LogDir)) { New-Item -ItemType Directory -Path $LogDir -Force | Out-Null } if (-not (Test-Path $EvidenceDir)) { New-Item -ItemType Directory -Path $EvidenceDir -Force | Out-Null } Log "✅ Prerequisites verified" "SUCCESS" return $true } function Phase1-BackendDeploy { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 1: BACKEND DEPLOYMENT (Production Release Build)" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" try { Log "Building backend in Release mode..." "INFO" Push-Location $script:ProjectRoot # Restore Log "Restoring dependencies..." "INFO" dotnet restore KArtSell.sln -c Release if ($LASTEXITCODE -ne 0) { throw "Restore failed" } # Build Log "Building solution..." "INFO" dotnet build KArtSell.sln -c Release --no-restore if ($LASTEXITCODE -ne 0) { throw "Build failed" } # Publish Log "Publishing backend to: $PublishDir" "INFO" if (Test-Path $PublishDir) { Remove-Item $PublishDir -Recurse -Force } dotnet publish $BackendRoot -c Release -o $PublishDir if ($LASTEXITCODE -ne 0) { throw "Publish failed" } # Run Tests Log "Running backend tests..." "INFO" dotnet test KArtSell.sln -c Release --logger "trx;LogFileName=backend-tests.trx" if ($LASTEXITCODE -ne 0) { throw "Tests failed" } Log "✅ Backend deployment successful" "SUCCESS" $script:Status.Phase1 = "success" # Save evidence $evidence = @{ timestamp = Get-Date -Format "o" phase = "Backend Deployment" status = "success" buildPath = $PublishDir binaries = @(Get-ChildItem $PublishDir -Filter "*.dll" -Recurse | Select-Object -ExpandProperty Name) } | ConvertTo-Json Set-Content -Path (Join-Path $EvidenceDir "phase1-backend-deployment.json") -Value $evidence Pop-Location return $true } catch { Log "❌ Phase 1 failed: $_" "ERROR" $script:Status.Phase1 = "failed" return $false } } function Phase2-FrontendBuild { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 2: FRONTEND BUILD & PRODUCTION OPTIMIZATION" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" try { Push-Location $script:FrontendRoot # Install dependencies Log "Installing frontend dependencies..." "INFO" pnpm install --frozen-lockfile if ($LASTEXITCODE -ne 0) { throw "pnpm install failed" } # Type checking Log "Running type checking..." "INFO" pnpm typecheck if ($LASTEXITCODE -ne 0) { throw "Type checking failed" } # Unit tests Log "Running frontend tests..." "INFO" pnpm test -- --run if ($LASTEXITCODE -ne 0) { throw "Frontend tests failed" } # Build for production Log "Building frontend for production..." "INFO" pnpm build if ($LASTEXITCODE -ne 0) { throw "Frontend build failed" } Log "✅ Frontend build successful" "SUCCESS" $script:Status.Phase2 = "success" # Save evidence $distSize = (Get-ChildItem -Path "dist" -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB $evidence = @{ timestamp = Get-Date -Format "o" phase = "Frontend Build" status = "success" distSize = "{0:F2} MB" -f $distSize distPath = "$(Get-Location)\dist" files = @(Get-ChildItem "dist" -Recurse | Select-Object -ExpandProperty Name) } | ConvertTo-Json Set-Content -Path (Join-Path $EvidenceDir "phase2-frontend-build.json") -Value $evidence Pop-Location return $true } catch { Log "❌ Phase 2 failed: $_" "ERROR" $script:Status.Phase2 = "failed" return $false } } function Phase3-NginxConfiguration { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 3: NGINX CONFIGURATION GENERATION" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" try { Log "Generating Nginx configuration..." "INFO" $nginxConfig = @" # K-ArtSell Aegis - Unified Service Configuration # Generated: $(Get-Date) # ════════════════════════════════════════════════════════════════ # HTTP to HTTPS Redirect # ════════════════════════════════════════════════════════════════ server { listen 80; server_name kartsell.taxbaik.com; return 301 https://`$server_name`$request_uri; } # ════════════════════════════════════════════════════════════════ # HTTPS Server - Unified Domain # ════════════════════════════════════════════════════════════════ server { listen 443 ssl http2; server_name kartsell.taxbaik.com; # SSL/TLS Configuration ssl_certificate /etc/letsencrypt/live/kartsell.taxbaik.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/kartsell.taxbaik.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Security Headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; # Logging access_log /var/log/nginx/kartsell-access.log; error_log /var/log/nginx/kartsell-error.log; # Client body size client_max_body_size 10M; # ──────────────────────────────────────────────────────────── # Route 1: Frontend (Root /) # ──────────────────────────────────────────────────────────── location / { root /var/www/kartsell/frontend; try_files `$uri /index.html; # Caching expires 1h; add_header Cache-Control "public, max-age=3600"; } # ──────────────────────────────────────────────────────────── # Route 2: Static Assets # ──────────────────────────────────────────────────────────── location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { root /var/www/kartsell/frontend; expires 30d; add_header Cache-Control "public, max-age=2592000"; } # ──────────────────────────────────────────────────────────── # Route 3: API (Proxy to Backend) # ──────────────────────────────────────────────────────────── location /api/ { proxy_pass http://localhost:5002/; # Preserve headers proxy_set_header Host `$host; proxy_set_header X-Real-IP `$remote_addr; proxy_set_header X-Forwarded-For `$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto `$scheme; proxy_set_header X-Forwarded-Host `$server_name; # Timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; # Buffering proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; proxy_busy_buffers_size 8k; # WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade `$http_upgrade; proxy_set_header Connection "upgrade"; } } "@ $configPath = Join-Path $script:ProjectRoot "nginx-kartsell.conf" Set-Content -Path $configPath -Value $nginxConfig Log "✅ Nginx configuration generated" "SUCCESS" Log " Location: $configPath" "INFO" Log " Next step: Copy to /etc/nginx/sites-available/kartsell" "INFO" Log " Then: sudo systemctl reload nginx" "INFO" $script:Status.Phase3 = "success" # Save evidence $evidence = @{ timestamp = Get-Date -Format "o" phase = "Nginx Configuration" status = "success" configPath = $configPath configSize = (Get-Item $configPath).Length } | ConvertTo-Json Set-Content -Path (Join-Path $EvidenceDir "phase3-nginx-config.json") -Value $evidence return $true } catch { Log "❌ Phase 3 failed: $_" "ERROR" $script:Status.Phase3 = "failed" return $false } } function Phase4-AutomationScript { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 4: GENERATE AUTOMATED DEPLOYMENT SCRIPTS" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" try { # Deploy to production script $deployScript = @" #!/bin/bash # Automated production deployment script echo "════════════════════════════════════════════════════════════" echo "K-ArtSell Aegis v16.0 - Production Deployment" echo "════════════════════════════════════════════════════════════" # 1. Deploy backend echo "[1/4] Deploying backend binaries..." mkdir -p /opt/kartsell # scp -r publish/* deploy@$($ProductionConfig.Host):/opt/kartsell/ cp -r publish/* /opt/kartsell/ sudo chown -R kartsell:kartsell /opt/kartsell/ sudo chmod -R 755 /opt/kartsell/ # 2. Deploy frontend echo "[2/4] Deploying frontend..." mkdir -p /var/www/kartsell/frontend cp -r frontend/dist/* /var/www/kartsell/frontend/ sudo chown -R www-data:www-data /var/www/kartsell/frontend/ sudo chmod -R 755 /var/www/kartsell/frontend/ # 3. Configure Nginx echo "[3/4] Configuring Nginx..." sudo cp nginx-kartsell.conf /etc/nginx/sites-available/kartsell sudo ln -sf /etc/nginx/sites-available/kartsell /etc/nginx/sites-enabled/kartsell sudo nginx -t # 4. Start services echo "[4/4] Starting services..." sudo systemctl reload nginx sudo systemctl restart kartsell-api.service echo "════════════════════════════════════════════════════════════" echo "✅ Deployment complete!" echo " Frontend: https://kartsell.taxbaik.com" echo " API: https://kartsell.taxbaik.com/api/" echo "════════════════════════════════════════════════════════════" "@ $deployScriptPath = Join-Path $script:ProjectRoot "scripts" "deploy-to-production.sh" Set-Content -Path $deployScriptPath -Value $deployScript Log "✅ Automation scripts generated" "SUCCESS" Log " Script: $deployScriptPath" "INFO" $script:Status.Phase4 = "success" return $true } catch { Log "❌ Phase 4 failed: $_" "ERROR" $script:Status.Phase4 = "failed" return $false } } function Phase5-Verification { Log "════════════════════════════════════════════════════════════════" "INFO" Log "PHASE 5: VERIFICATION & STATUS REPORT" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" $allSuccess = $true # Check backend binaries Log "Verifying backend binaries..." "INFO" $backendDll = Join-Path $PublishDir "KArtSell.Host.dll" if (Test-Path $backendDll) { $size = (Get-Item $backendDll).Length / 1MB Log "✅ Backend binary: $backendDll ($([Math]::Round($size, 2)) MB)" "SUCCESS" } else { Log "❌ Backend binary not found" "ERROR" $allSuccess = $false } # Check frontend dist Log "Verifying frontend distribution..." "INFO" $indexHtml = Join-Path $script:FrontendRoot "dist" "index.html" if (Test-Path $indexHtml) { $distSize = (Get-ChildItem -Path (Join-Path $script:FrontendRoot "dist") -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB Log "✅ Frontend dist: $([Math]::Round($distSize, 2)) MB" "SUCCESS" } else { Log "❌ Frontend dist not found" "ERROR" $allSuccess = $false } # Check Nginx config Log "Verifying Nginx configuration..." "INFO" $nginxConfig = Join-Path $script:ProjectRoot "nginx-kartsell.conf" if (Test-Path $nginxConfig) { Log "✅ Nginx configuration: $nginxConfig" "SUCCESS" } else { Log "❌ Nginx configuration not found" "ERROR" $allSuccess = $false } if ($allSuccess) { Log "✅ All verification checks passed" "SUCCESS" $script:Status.Phase5 = "success" } else { Log "❌ Some verification checks failed" "ERROR" $script:Status.Phase5 = "failed" } return $allSuccess } function Generate-StatusReport { Log "════════════════════════════════════════════════════════════════" "INFO" Log "FINAL STATUS REPORT" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" $duration = (Get-Date) - $script:StartTime Log "Execution Time: $([Math]::Round($duration.TotalMinutes, 2)) minutes" "INFO" Log "" "INFO" Log "Phase Results:" "INFO" Log " Phase 1 (Backend): $($script:Status.Phase1.ToUpper())" $(if ($script:Status.Phase1 -eq "success") { "SUCCESS" } else { "ERROR" }) Log " Phase 2 (Frontend): $($script:Status.Phase2.ToUpper())" $(if ($script:Status.Phase2 -eq "success") { "SUCCESS" } else { "ERROR" }) Log " Phase 3 (Nginx): $($script:Status.Phase3.ToUpper())" $(if ($script:Status.Phase3 -eq "success") { "SUCCESS" } else { "ERROR" }) Log " Phase 4 (Scripts): $($script:Status.Phase4.ToUpper())" $(if ($script:Status.Phase4 -eq "success") { "SUCCESS" } else { "ERROR" }) Log " Phase 5 (Verify): $($script:Status.Phase5.ToUpper())" $(if ($script:Status.Phase5 -eq "success") { "SUCCESS" } else { "ERROR" }) Log "" "INFO" if ($script:Status.Phase1 -eq "success" -and $script:Status.Phase2 -eq "success" -and $script:Status.Phase3 -eq "success") { Log "✅ DEPLOYMENT PREPARATION COMPLETE" "SUCCESS" Log "" "INFO" Log "Next Steps:" "INFO" Log " 1. Copy backend binaries: cp -r publish/* /opt/kartsell/" "INFO" Log " 2. Copy frontend: cp -r frontend/dist/* /var/www/kartsell/frontend/" "INFO" Log " 3. Configure Nginx: sudo cp nginx-kartsell.conf /etc/nginx/sites-available/kartsell" "INFO" Log " 4. Start services: sudo systemctl reload nginx" "INFO" Log " 5. Verify: curl https://kartsell.taxbaik.com/api/health" "INFO" } else { Log "❌ DEPLOYMENT PREPARATION INCOMPLETE" "ERROR" Log "Please review the errors above" "WARN" } Log "" "INFO" Log "Evidence saved to: $EvidenceDir" "INFO" Log "Logs saved to: $LogDir" "INFO" Log "════════════════════════════════════════════════════════════════" "INFO" } # ════════════════════════════════════════════════════════════════════════════════════ # MAIN EXECUTION # ════════════════════════════════════════════════════════════════════════════════════ Log "╔════════════════════════════════════════════════════════════╗" "INFO" Log "║ K-ArtSell Aegis v16.0: COMPLETE DEPLOYMENT AUTOMATION ║" "INFO" Log "║ Optimal Strategy + AGENTS.md v16.0 Compliance ║" "INFO" Log "╚════════════════════════════════════════════════════════════╝" "INFO" Log "" "INFO" # Verify prerequisites if (-not (Verify-Prerequisites)) { Log "❌ Prerequisites verification failed" "ERROR" exit 1 } Log "" "INFO" # Execute phases Phase1-BackendDeploy Phase2-FrontendBuild Phase3-NginxConfiguration Phase4-AutomationScript Phase5-Verification Log "" "INFO" # Generate final report Generate-StatusReport