deploy_skill: soporte para Coolify v4.1.2 (API /applications 404) + flujo UI Playwright
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]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cd998ce6b0
commit
e7d1c33cd5
@@ -0,0 +1,241 @@
|
||||
<#
|
||||
.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
|
||||
Reference in New Issue
Block a user