Aprendido desplegando cotizador (Next.js+FastAPI+Postgres, Docker Compose, repo privado) en coolify.urieljareth.org (v4.1.2): - Documenta que /applications/* devuelve 404 en esta instancia y que configurar apps git build-from-source solo es posible por la UI web. - Nuevos scripts scripts/coolify-ui/: coolify-login.mjs (guarda storageState) y Configure-CoolifyComposeApp.mjs (build pack -> Docker Compose, Reload Compose File, env vars por Developer view, dominios por servicio). - references/coolify-4.1.2-notes.md: mapa de API funcional/404, gotchas (BOM UTF-8 rompe el parser YAML de Coolify; Reload Compose File obligatorio; pin de dominios por servicio o 503; no encolar deploys concurrentes; PowerShell [IO.File]::ReadAllText para payloads de llaves). - env.local.template.ps1: COOLIFY_EMAIL/COOLIFY_PASSWORD para la UI. - SKILL.md/TOOLS.md: seccion "instance reality" y flujo UI paso a paso. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
242 lines
8.8 KiB
PowerShell
242 lines
8.8 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Create or update a Coolify application from a Git repository and deploy it.
|
|
|
|
.DESCRIPTION
|
|
Orchestration wrapper around Invoke-CoolifyApi.ps1 that performs the full
|
|
Coolify onboarding flow for an existing Git repository:
|
|
|
|
1. Resolve or create the target Project (by name).
|
|
2. Resolve or create the target Environment (by name within the project).
|
|
3. Resolve the local server UUID (first reachable server).
|
|
4. Create the application (public repo by default, or via deploy key).
|
|
5. Set the FQDN and ports_exposes on the new app.
|
|
6. Push env vars (key=value pairs) onto the application.
|
|
7. Trigger an initial deploy and return the deployment UUIDs.
|
|
|
|
Each mutating step asks for explicit confirmation unless -Force is supplied.
|
|
The script NEVER writes secrets to stdout: env values are sent to the API
|
|
but not echoed.
|
|
|
|
.PARAMETER RepoUrl
|
|
HTTPS clone URL of the Git repository (e.g. https://github.com/user/repo).
|
|
|
|
.PARAMETER Branch
|
|
Branch to deploy. Defaults to 'main'.
|
|
|
|
.PARAMETER Fqdn
|
|
Public URL Coolify will expose, e.g. https://myapp.urieljareth.org.
|
|
|
|
.PARAMETER ProjectName
|
|
Coolify project name. If it does not exist, it will be created. Default: 'default'.
|
|
|
|
.PARAMETER EnvironmentName
|
|
Coolify environment name within the project. Default: 'production'.
|
|
|
|
.PARAMETER PortsExposes
|
|
Internal port the app listens on (Traefik targets this). Default: '3000'.
|
|
|
|
.PARAMETER BuildPack
|
|
Optional Coolify build pack override (nixpacks, dockerfile, dockercompose).
|
|
|
|
.PARAMETER IsStatic
|
|
Switch. Marks the app as a static site (Coolify will use a static_image).
|
|
|
|
.PARAMETER EnvFile
|
|
Optional path to a .env-formatted file. Each KEY=VALUE line is pushed to
|
|
Coolify as an environment variable. Lines starting with '#' are ignored.
|
|
|
|
.PARAMETER ApplicationUuid
|
|
Optional. If the app already exists in Coolify, pass its UUID to update it
|
|
in place (FQDN, ports, env) and redeploy, instead of creating a new one.
|
|
|
|
.PARAMETER Force
|
|
Skip per-step confirmation prompts. Use only after validating the plan.
|
|
|
|
.EXAMPLE
|
|
$envs = "./.env.coolify"
|
|
.\New-CoolifyApplication.ps1 -RepoUrl https://github.com/urieljarethbusiness-cpu/my-app `
|
|
-Fqdn https://myapp.urieljareth.org -PortsExposes 8080 -EnvFile $envs -Force
|
|
#>
|
|
param(
|
|
[Parameter(Mandatory = $true, ParameterSetName = "Create")]
|
|
[string]$RepoUrl,
|
|
|
|
[string]$Branch = "main",
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Fqdn,
|
|
|
|
[string]$ProjectName = "default",
|
|
|
|
[string]$EnvironmentName = "production",
|
|
|
|
[string]$PortsExposes = "3000",
|
|
|
|
[string]$BuildPack,
|
|
|
|
[switch]$IsStatic,
|
|
|
|
[string]$EnvFile,
|
|
|
|
[Parameter(ParameterSetName = "Update")]
|
|
[string]$ApplicationUuid,
|
|
|
|
[switch]$Force
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$coolifyApi = Join-Path $here "..\..\coolify_skill\scripts\Invoke-CoolifyApi.ps1"
|
|
if (-not (Test-Path -LiteralPath $coolifyApi)) {
|
|
throw "Invoke-CoolifyApi.ps1 not found at: $coolifyApi"
|
|
}
|
|
|
|
function Confirm-Step {
|
|
param([string]$Action)
|
|
if ($Force) { return $true }
|
|
$reply = Read-Host "Confirm action: $Action`nProceed? (y/N)"
|
|
return ($reply -match '^(y|yes|s|si|sí)$')
|
|
}
|
|
|
|
function Get-CoolifyResource {
|
|
param([string]$Path)
|
|
& $coolifyApi -Path $Path -ErrorAction Stop
|
|
}
|
|
|
|
function Resolve-Project {
|
|
param([string]$Name)
|
|
$projects = Get-CoolifyResource -Path "/projects"
|
|
$match = $projects | Where-Object { $_.name -eq $Name } | Select-Object -First 1
|
|
if (-not $match) {
|
|
if (-not (Confirm-Step "Create project '$Name' in Coolify")) {
|
|
throw "Aborted by user before creating project."
|
|
}
|
|
$body = @{ name = $Name } | ConvertTo-Json -Compress
|
|
$created = & $coolifyApi -Method POST -Path "/projects" -BodyJson $body -ErrorAction Stop
|
|
Write-Host "Project created: $($created.uuid)" -ForegroundColor Green
|
|
return $created
|
|
}
|
|
return $match
|
|
}
|
|
|
|
function Resolve-Environment {
|
|
param([string]$ProjectUuid, [string]$Name)
|
|
$envs = Get-CoolifyResource -Path "/projects/$ProjectUuid/environments"
|
|
$match = $envs | Where-Object { $_.name -eq $Name } | Select-Object -First 1
|
|
if (-not $match) {
|
|
if (-not (Confirm-Step "Create environment '$Name' in project $ProjectUuid")) {
|
|
throw "Aborted by user before creating environment."
|
|
}
|
|
$body = @{ name = $Name } | ConvertTo-Json -Compress
|
|
$created = & $coolifyApi -Method POST -Path "/projects/$ProjectUuid/environments" -BodyJson $body -ErrorAction Stop
|
|
Write-Host "Environment created: $($created.uuid)" -ForegroundColor Green
|
|
return $created
|
|
}
|
|
return $match
|
|
}
|
|
|
|
function Resolve-ServerUuid {
|
|
$servers = Get-CoolifyResource -Path "/servers"
|
|
if (-not $servers) { throw "No Coolify servers configured." }
|
|
$srv = $servers | Where-Object { $_.settings.is_usable -eq $true } | Select-Object -First 1
|
|
if (-not $srv) { $srv = $servers | Select-Object -First 1 }
|
|
return $srv.uuid
|
|
}
|
|
|
|
function Resolve-AppUuid {
|
|
param([string]$Uuid)
|
|
if ($Uuid) {
|
|
$app = Get-CoolifyResource -Path "/applications/$Uuid"
|
|
return @{ uuid = $app.uuid; created = $false }
|
|
}
|
|
return $null
|
|
}
|
|
|
|
Write-Host "Resolving Coolify project '$ProjectName'..." -ForegroundColor Cyan
|
|
$proj = Resolve-Project -Name $ProjectName
|
|
Write-Host "Resolving environment '$EnvironmentName'..." -ForegroundColor Cyan
|
|
$env = Resolve-Environment -ProjectUuid $proj.uuid -Name $EnvironmentName
|
|
Write-Host "Resolving server..." -ForegroundColor Cyan
|
|
$serverUuid = Resolve-ServerUuid
|
|
Write-Host "Using server: $serverUuid" -ForegroundColor Cyan
|
|
|
|
$existing = Resolve-AppUuid -Uuid $ApplicationUuid
|
|
if ($existing) {
|
|
Write-Host "Updating existing application: $($existing.uuid)" -ForegroundColor Cyan
|
|
$appUuid = $existing.uuid
|
|
$patch = [ordered]@{
|
|
domains = $Fqdn
|
|
ports_exposes = $PortsExposes
|
|
}
|
|
if ($BuildPack) { $patch.build_pack = $BuildPack }
|
|
if (-not (Confirm-Step "PATCH /applications/$appUuid (domain=$Fqdn, ports=$PortsExposes)")) {
|
|
throw "Aborted by user."
|
|
}
|
|
& $coolifyApi -Method PATCH -Path "/applications/$appUuid" -BodyJson ($patch | ConvertTo-Json -Compress) -ErrorAction Stop | Out-Null
|
|
} else {
|
|
$createBody = [ordered]@{
|
|
project_uuid = $proj.uuid
|
|
environment_name = $env.name
|
|
server_uuid = $serverUuid
|
|
git_repository = $RepoUrl
|
|
git_branch = $Branch
|
|
ports_exposes = $PortsExposes
|
|
domains = $Fqdn
|
|
name = Split-Path -Leaf ($RepoUrl -replace '\.git$', '')
|
|
is_static = [bool]$IsStatic
|
|
instant_deploy = $false
|
|
}
|
|
if ($BuildPack) { $createBody.build_pack = $BuildPack }
|
|
if (-not (Confirm-Step "POST /applications/public with repo=$RepoUrl, branch=$Branch, fqdn=$Fqdn, ports=$PortsExposes")) {
|
|
throw "Aborted by user."
|
|
}
|
|
$created = & $coolifyApi -Method POST -Path "/applications/public" -BodyJson ($createBody | ConvertTo-Json -Compress) -ErrorAction Stop
|
|
$appUuid = $created.uuid
|
|
Write-Host "Application created: $appUuid" -ForegroundColor Green
|
|
}
|
|
|
|
if ($EnvFile -and (Test-Path -LiteralPath $EnvFile)) {
|
|
if (-not (Confirm-Step "Push env vars from $EnvFile to application $appUuid")) {
|
|
Write-Host "Skipping env push." -ForegroundColor Yellow
|
|
} else {
|
|
Get-Content -LiteralPath $EnvFile | ForEach-Object {
|
|
$line = $_.Trim()
|
|
if (-not $line -or $line.StartsWith("#")) { return }
|
|
$eq = $line.IndexOf("=")
|
|
if ($eq -le 0) { return }
|
|
$k = $line.Substring(0, $eq).Trim()
|
|
$v = $line.Substring($eq + 1).Trim().Trim('"').Trim("'")
|
|
$body = [ordered]@{
|
|
key = $k
|
|
value = $v
|
|
is_literal = $true
|
|
is_shown_once = $false
|
|
} | ConvertTo-Json -Compress
|
|
& $coolifyApi -Method POST -Path "/applications/$appUuid/envs" -BodyJson $body -ErrorAction Stop | Out-Null
|
|
Write-Host " env: $k set" -ForegroundColor DarkGray
|
|
}
|
|
Write-Host "Env vars pushed." -ForegroundColor Green
|
|
}
|
|
}
|
|
|
|
if (-not (Confirm-Step "Trigger DEPLOY of $appUuid")) {
|
|
Write-Host "Skipping deploy trigger. Application is created but not deployed." -ForegroundColor Yellow
|
|
return
|
|
}
|
|
|
|
$deployResult = & $coolifyApi -Method POST -Path "/deploy" -BodyJson (@{ uuid = $appUuid } | ConvertTo-Json -Compress) -ErrorAction Stop
|
|
Write-Host "Deploy triggered." -ForegroundColor Green
|
|
$deployResult
|
|
|
|
[PSCustomObject]@{
|
|
project_uuid = $proj.uuid
|
|
environment_name = $env.name
|
|
server_uuid = $serverUuid
|
|
application_uuid = $appUuid
|
|
fqdn = $Fqdn
|
|
repo = $RepoUrl
|
|
branch = $Branch
|
|
} | Format-List
|