<# .SYNOPSIS Initialize a project directory for compliant Coolify deployment. .DESCRIPTION Copies the right template (Node / Python / multi-container) into the target project: Dockerfile, docker-compose, .dockerignore, plus a Coolify env template. Idempotent: never overwrites existing files unless -Force. .PARAMETER Path Target project directory. Defaults to current directory. .PARAMETER Stack Which template to scaffold. One of: node, python, compose, static. .PARAMETER AppPort Listening port to bake into the Dockerfile and compose. Default 8080. .PARAMETER Force Overwrite existing Dockerfile / docker-compose / .dockerignore. .EXAMPLE .\Initialize-CoolifyProject.ps1 -Path .\my-app -Stack node -AppPort 8080 #> param( [string]$Path = ".", [ValidateSet("node", "python", "compose", "static")] [string]$Stack = "node", [int]$AppPort = 8080, [switch]$Force ) $ErrorActionPreference = "Stop" $here = Split-Path -Parent $MyInvocation.MyCommand.Path $templates = Join-Path $here "..\references\templates" $target = (Resolve-Path -LiteralPath $Path).Path function Copy-Template { param([string]$Source, [string]$Dest) if (-not (Test-Path -LiteralPath $Source)) { throw "Template not found: $Source" } if ((Test-Path -LiteralPath $Dest) -and -not $Force) { Write-Host " skip (exists): $Dest" -ForegroundColor Yellow return } Copy-Item -LiteralPath $Source -Destination $Dest -Force:$Force Write-Host " wrote: $Dest" -ForegroundColor Green } Write-Host "Initializing $target with stack=$Stack port=$AppPort" -ForegroundColor Cyan switch ($Stack) { "node" { Copy-Template (Join-Path $templates "Dockerfile.node") (Join-Path $target "Dockerfile") } "python" { Copy-Template (Join-Path $templates "Dockerfile.python") (Join-Path $target "Dockerfile") } "compose" { Copy-Template (Join-Path $templates "Dockerfile.node") (Join-Path $target "Dockerfile") Copy-Template (Join-Path $templates "docker-compose.app-db.yml") (Join-Path $target "docker-compose.yml") } "static" { # Static sites are served by nginx in Coolify; no Dockerfile required. Write-Host " (static stack: no Dockerfile needed; Coolify uses is_static + static_image)" -ForegroundColor DarkGray } } # .dockerignore (idempotent) $di = Join-Path $target ".dockerignore" if ((-not (Test-Path -LiteralPath $di)) -or $Force) { @( ".git", ".gitignore", ".env*", "node_modules", "vendor", "__pycache__", "*.log", ".vscode", ".idea", "README.md" ) | Set-Content -LiteralPath $di -Encoding utf8 Write-Host " wrote: $di" -ForegroundColor Green } else { Write-Host " skip (exists): $di" -ForegroundColor Yellow } # Recommended .gitignore additions (idempotent, append-only if missing) $gi = Join-Path $target ".gitignore" $existing = if (Test-Path -LiteralPath $gi) { Get-Content -LiteralPath $gi } else { @() } $additions = @(".env", ".env.local", ".env.*.local", "*.key", "*.pem") | Where-Object { $_ -notin $existing } if ($additions) { if (-not (Test-Path -LiteralPath $gi)) { "" | Set-Content -LiteralPath $gi -Encoding utf8 } Add-Content -LiteralPath $gi -Value "`n# Added by deploy_skill" $additions | ForEach-Object { Add-Content -LiteralPath $gi -Value $_ } Write-Host " updated: $gi (+$($additions.Count) lines)" -ForegroundColor Green } else { Write-Host " skip (already covered): $gi" -ForegroundColor Yellow } Write-Host "`nNext steps:" -ForegroundColor Cyan Write-Host " 1. Implement /health in your app (DB-independent, returns 200)." Write-Host " 2. Put real secrets in a local .env.coolify file (not committed)." Write-Host " 3. Run Test-PreDeployChecklist.ps1 to validate." Write-Host " 4. Run New-GitHubRepo.ps1 to create the repo." Write-Host " 5. git push -u origin main." Write-Host " 6. Run New-CoolifyApplication.ps1 to register and deploy." Write-Host " 7. Run Test-PostDeploy.ps1 to verify."