From e7d1c33cd5fb3570d15da2b9f7feaa50b4d747af Mon Sep 17 00:00:00 2001 From: urieljareth Date: Wed, 8 Jul 2026 00:42:12 -0600 Subject: [PATCH] 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) --- deploy_skill/SKILL.md | 233 +++++++++++++ deploy_skill/TOOLS.md | 246 ++++++++++++++ .../references/coolify-4.1.2-notes.md | 103 ++++++ deploy_skill/references/deploy-flow.md | 72 ++++ .../references/templates/Dockerfile.node | 26 ++ .../references/templates/Dockerfile.python | 24 ++ .../templates/docker-compose.app-db.yml | 52 +++ .../templates/env.local.template.ps1 | 31 ++ .../scripts/Initialize-CoolifyProject.ps1 | 112 ++++++ .../scripts/Invoke-CoolifyRollback.ps1 | 112 ++++++ deploy_skill/scripts/Invoke-GitHubApi.ps1 | 62 ++++ .../scripts/New-CoolifyApplication.ps1 | 241 +++++++++++++ deploy_skill/scripts/New-CoolifyService.ps1 | 319 ++++++++++++++++++ deploy_skill/scripts/New-GitHubRepo.ps1 | 85 +++++ .../scripts/Publish-ProjectToCoolify.ps1 | 156 +++++++++ deploy_skill/scripts/Test-PostDeploy.ps1 | 75 ++++ .../scripts/Test-PreDeployChecklist.ps1 | 177 ++++++++++ deploy_skill/scripts/Test-ServiceOnline.ps1 | 238 +++++++++++++ .../Configure-CoolifyComposeApp.mjs | 94 ++++++ .../scripts/coolify-ui/coolify-login.mjs | 40 +++ deploy_skill/scripts/verify-online.mjs | 128 +++++++ docs/AGENTS-coolify-apps.md | 309 +++++++++++++++++ 22 files changed, 2935 insertions(+) create mode 100644 deploy_skill/SKILL.md create mode 100644 deploy_skill/TOOLS.md create mode 100644 deploy_skill/references/coolify-4.1.2-notes.md create mode 100644 deploy_skill/references/deploy-flow.md create mode 100644 deploy_skill/references/templates/Dockerfile.node create mode 100644 deploy_skill/references/templates/Dockerfile.python create mode 100644 deploy_skill/references/templates/docker-compose.app-db.yml create mode 100644 deploy_skill/references/templates/env.local.template.ps1 create mode 100644 deploy_skill/scripts/Initialize-CoolifyProject.ps1 create mode 100644 deploy_skill/scripts/Invoke-CoolifyRollback.ps1 create mode 100644 deploy_skill/scripts/Invoke-GitHubApi.ps1 create mode 100644 deploy_skill/scripts/New-CoolifyApplication.ps1 create mode 100644 deploy_skill/scripts/New-CoolifyService.ps1 create mode 100644 deploy_skill/scripts/New-GitHubRepo.ps1 create mode 100644 deploy_skill/scripts/Publish-ProjectToCoolify.ps1 create mode 100644 deploy_skill/scripts/Test-PostDeploy.ps1 create mode 100644 deploy_skill/scripts/Test-PreDeployChecklist.ps1 create mode 100644 deploy_skill/scripts/Test-ServiceOnline.ps1 create mode 100644 deploy_skill/scripts/coolify-ui/Configure-CoolifyComposeApp.mjs create mode 100644 deploy_skill/scripts/coolify-ui/coolify-login.mjs create mode 100644 deploy_skill/scripts/verify-online.mjs create mode 100644 docs/AGENTS-coolify-apps.md diff --git a/deploy_skill/SKILL.md b/deploy_skill/SKILL.md new file mode 100644 index 0000000..f3c83fa --- /dev/null +++ b/deploy_skill/SKILL.md @@ -0,0 +1,233 @@ +--- +name: coolify-deploy +description: Deploy new projects to the local self-hosted Coolify instance from end to end. Use when the user wants to take a local project (or a new idea) and ship it to Coolify: scaffold a compliant Dockerfile/compose, create a GitHub repo, push, register the app in Coolify, deploy it, and verify. Also covers rollback to a previous commit and redeploys. Distinct from coolify-agent (which operates already-deployed apps) and the proxmox-agent (host operations). +--- + +# Coolify Deploy Skill + +Use this skill to take a project from "local code" to "live on +`https://.urieljareth.org`". It wraps the existing repo tooling +(Proxmox SSH, Coolify API, Cloudflare API) plus new GitHub scaffolding into a +single end-to-end pipeline. + +## ⚠️ Instance reality check (READ FIRST — verified 2026-07 on coolify.urieljareth.org, v4.1.2) + +This instance's REST API is **partial**: the entire `/applications/*` namespace is +**404** (create public/dockerfile/private-*, list, get, PATCH, `/envs`, `/logs`). +`POST /services` only takes raw compose (no git repo). Consequences: + +- You **cannot create OR configure a git-based build-from-source app via the API** here. + `New-CoolifyApplication.ps1` (POST /applications/public) will 404. +- To configure an app that **builds from a (private) git repo** — set its build pack to + Docker Compose, its compose location, its **env vars**, and its **per-service domains** — + you MUST drive the **web UI with Playwright**. Use `scripts/coolify-ui/*.mjs` + (needs `COOLIFY_EMAIL`/`COOLIFY_PASSWORD` in `.env.local.ps1`). +- What DOES work via API: `/resources` (inventory+status), `/projects`, `/security/keys`, + `/services`, and **`GET /deploy?uuid=&force=true`** (trigger a deploy of any existing app), + `GET /deployments/{uuid}` (status+logs). + +Full playbook + gotchas (UTF-8 BOM breaks Coolify's YAML parser, "Reload Compose File" is +mandatory, don't queue concurrent deploys, PowerShell `ReadAllText` for key payloads, etc.): +**[`references/coolify-4.1.2-notes.md`](references/coolify-4.1.2-notes.md)**. Re-verify the API +surface with `GET /version` if the instance was upgraded. + +## When to use + +- "Deploy this project to Coolify" +- "Create a new GitHub repo and ship it to Coolify" +- "Roll back the Coolify app to yesterday's commit" +- "Generate a Dockerfile that works on this Coolify instance" +- "Validate my repo against the Coolify hard rules before pushing" + +## When NOT to use + +- Operating an already-deployed app (logs, restart, env edits) → use + [`coolify_skill/SKILL.md`](../coolify_skill/SKILL.md) instead. +- Proxmox host or LXC maintenance → use [`agent/SKILL.md`](../agent/SKILL.md). +- Editing Cloudflare tunnel routes → dashboard-managed, see + `docs/runbooks/cloudflare-tunnel.md`. + +## Prerequisites (one-time setup) + +1. `.env.local.ps1` must contain at minimum `COOLIFY_TOKEN` and `GITHUB_TOKEN`. + See `references/templates/env.local.template.ps1`. +2. `GITHUB_TOKEN` is a GitHub PAT (classic with `repo` scope, or fine-grained + with `Contents: Read+Write` + `Metadata: Read`). Generate at + https://github.com/settings/tokens. +3. `git push` to GitHub uses Windows Credential Manager (`wincred`) — no need + to embed the PAT in the git URL. The PAT is only for GitHub REST API calls + (creating repos, listing). +4. The authenticated GitHub account is `urieljarethbusiness-cpu` (verified + via `git credential fill`). Override with `GITHUB_OWNER` if needed. +5. **Operational verification needs Node + Playwright.** Run + `npm install` once in the manager repo root (`H:\MegaSync\Proyectos\Proxmox & Coolify Manager`). + Chromium binaries cache at `%LOCALAPPDATA%\ms-playwright`. If Node is + missing, `Test-ServiceOnline.ps1` falls back to curl-only (browser layer + is skipped, not failed). + +## Pipeline + +There are **two** pipelines, one per deploy strategy. Pick based on whether +your project ships as a single Dockerfile-built app or as a multi-container +Docker Compose stack. + +### A. Single-app (Dockerfile) — `Publish-ProjectToCoolify.ps1` + +``` +local project + → Initialize-CoolifyProject.ps1 (optional: scaffold Dockerfile/compose) + → Test-PreDeployChecklist.ps1 (gate; enforces AGENTS-coolify-apps.md) + → New-GitHubRepo.ps1 (create repo, idempotent) + → git push origin main (uses wincred, not the PAT) + → New-CoolifyApplication.ps1 (project + env + app + envs + deploy) + → Test-PostDeploy.ps1 (Coolify state: containers, proxy logs, sibling DNS) + → Test-ServiceOnline.ps1 (OPERATIONAL: HTTP 200 + Playwright page render) +``` + +Use `Publish-ProjectToCoolify.ps1` to run all seven steps in sequence with one +confirmation per step. + +### B. Multi-container stack (Docker Compose) — `New-CoolifyService.ps1` + +For projects that ship a `docker-compose.coolify.yml` (app + sibling DB + +optional sidecars). Reads the local compose file and POSTs it to Coolify's +`/services` endpoint — the modern replacement for the deprecated +`/applications/dockercompose`. + +``` +local project (with docker-compose.coolify.yml) + → Test-PreDeployChecklist.ps1 (gate; enforces AGENTS-coolify-apps.md) + → New-GitHubRepo.ps1 + git push (only if the repo isn't already on GitHub) + → New-CoolifyService.ps1 (project + env + service + urls + envs + deploy) + → Test-PostDeploy.ps1 (Coolify state: containers, proxy logs, sibling DNS) + → Test-ServiceOnline.ps1 (OPERATIONAL: HTTP 200 + Playwright page render) +``` + +The compose MUST satisfy the hard rules (no published 80/443, DB by service +name, `SERVICE_FQDN__` placeholders so Coolify wires routes, named +volumes, healthchecks). The `urls` payload in `New-CoolifyService.ps1` maps +one service (e.g. `web`) to the primary FQDN; pass `-ExtraUrls` for +multi-public-service stacks. + +The final step (`Test-ServiceOnline.ps1`) is the **definition of done** for +both pipelines: the service is only considered live when (a) the FQDN returns +HTTP 2xx via curl and (b) a real Chromium browser loads the page with no +uncaught JS errors. It retries for ~80 seconds to allow Traefik/TLS warm-up. + +For rollback: `Invoke-CoolifyRollback.ps1` pushes the chosen commit to a +`rollback/` branch (main stays untouched), repoints the Coolify app, and +redeploys. + +## Hard rules (must hold for every app) + +Read [`docs/AGENTS-coolify-apps.md`](../docs/AGENTS-coolify-apps.md) for the +canonical version. Summary: + +1. **Sibling DB by service name, never `localhost`.** +2. **External `coolify` network** if the app needs shared Coolify services. +3. **Never publish host ports `80`/`443`** — Traefik owns them. +4. **FQDN = `https://.urieljareth.org`** set before first deploy. TLS via + DNS challenge, not HTTP-01. +5. **Secrets as env vars only**, never committed or baked. +6. **Healthcheck on a DB-independent endpoint.** +7. **Named volumes** for data that must survive redeploys. + +`Test-PreDeployChecklist.ps1` enforces these automatically. + +## Safety policy + +- **Read-only by default.** The first run of any deploy flow only *inspects*. +- **Confirm before mutating.** Each step that creates a repo, pushes commits, + creates a Coolify resource, or triggers a deploy asks for confirmation + unless `-Force` is supplied. +- **Never write secrets into the repo.** Tokens live in `.env.local.ps1` + (gitignored). The skill scripts accept env files but never echo their values. +- **No force-push to main** during rollback. The rollback branch carries the + target SHA; main is only moved if the operator does it explicitly. +- **State the rollback path** before any risky change. + +## Tooling + +See [`TOOLS.md`](TOOLS.md) for the command reference. The scripts in +`deploy_skill/scripts/` reuse: + +- `scripts/Invoke-ProxmoxSsh.ps1` for any LXC 102 / Docker inspection. +- `coolify_skill/scripts/Invoke-CoolifyApi.ps1` for all Coolify API calls. +- `scripts/Invoke-CloudflareApi.ps1` if the user asks to add a tunnel route + (rare; tunnel is dashboard-managed). + +## Reference docs + +- [`docs/AGENTS-coolify-apps.md`](../docs/AGENTS-coolify-apps.md) — the + compatibility contract every project must satisfy. Read before scaffolding. +- [`docs/proxmox-inventory.md`](../docs/proxmox-inventory.md) — verified + topology. +- [`docs/runbooks/coolify-docker.md`](../docs/runbooks/coolify-docker.md) — + Docker operations inside LXC 102. +- [`coolify_skill/references/ops/`](../coolify_skill/references/ops/) — + per-endpoint Coolify API reference. Search with `rg`, do not load all. +- [`references/templates/`](references/templates/) — Dockerfile/compose/env + templates that already satisfy the hard rules. + +## Common workflows + +### 1. New project, full pipeline + +```powershell +. .\.env.local.ps1 +.\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 ` + -AppPath .\my-new-app ` + -AppName my-new-app ` + -Fqdn https://my-new-app.urieljareth.org ` + -Stack node -AppPort 8080 -Init +``` + +### 2. Existing local repo, just register + deploy + +```powershell +.\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 ` + -AppPath .\existing-app ` + -AppName existing-app ` + -Fqdn https://existing-app.urieljareth.org ` + -AppPort 3000 +``` + +### 2b. Multi-container stack (Docker Compose) — register + deploy + +```powershell +.\deploy_skill\scripts\New-CoolifyService.ps1 ` + -AppPath .\my-stack ` + -AppName my-stack ` + -ComposeFile docker-compose.coolify.yml ` + -Fqdn https://my-stack.urieljareth.org ` + -PrimaryService web ` + -EnvFile .\my-stack\.env.coolify ` + -InstantDeploy +``` + +### 3. Rollback to a previous commit + +```powershell +.\deploy_skill\scripts\Invoke-CoolifyRollback.ps1 ` + -AppPath .\my-app ` + -ApplicationUuid og88wk4 ` + -CommitSha a1b2c3d +``` + +### 4. Only validate, no deploy + +```powershell +.\deploy_skill\scripts\Test-PreDeployChecklist.ps1 -Path .\my-app -ExpectedPort 8080 -Strict +``` + +### 5. Verify an already-deployed service is truly live (HTTP 200 + browser) + +```powershell +.\deploy_skill\scripts\Test-ServiceOnline.ps1 ` + -Fqdn https://my-app.urieljareth.org ` + -ExpectTitle "My App" -Retries 8 +``` + +This is the **operational** gate — the answer to "did the deploy actually ship +a working page?". Use it after any redeploy, env change, or rollback. It +captures a PNG screenshot as evidence. diff --git a/deploy_skill/TOOLS.md b/deploy_skill/TOOLS.md new file mode 100644 index 0000000..1f09b93 --- /dev/null +++ b/deploy_skill/TOOLS.md @@ -0,0 +1,246 @@ +# Coolify Deploy — Tooling + +All commands assume PowerShell from the project root. Load env first: + +```powershell +. .\.env.local.ps1 +``` + +Required env vars (in `.env.local.ps1`, gitignored): + +- `COOLIFY_TOKEN` — for Coolify API. +- `GITHUB_TOKEN` — PAT for GitHub API (create repo, list). `git push` uses + wincred, NOT this PAT. +- (Optional) `GITHUB_OWNER` — override the default `urieljarethbusiness-cpu`. +- `COOLIFY_EMAIL` / `COOLIFY_PASSWORD` — Coolify **web UI** login. Required for the + UI-driven flow below (this instance's `/applications/*` API is 404 — see + `references/coolify-4.1.2-notes.md`). + +## UI-driven config for git build-from-source apps (this instance, v4.1.2) + +Because `/applications/*` is 404 here, configure git-based Docker-Compose apps through the +UI with Playwright (run from the manager repo root so `require('playwright')` resolves): + +```powershell +. .\.env.local.ps1 +$env:CF_EMAIL = $env:COOLIFY_EMAIL; $env:CF_PASS = $env:COOLIFY_PASSWORD +$scratch = "$env:TEMP\cf"; New-Item -ItemType Directory -Force $scratch | Out-Null + +# 1. Log in once, save the session +node .\deploy_skill\scripts\coolify-ui\coolify-login.mjs "$scratch\state.json" + +# 2. Configure an EXISTING app (find its uuid/project/env via GET /resources + /projects/{u}/environments) +$env:CF_STATE = "$scratch\state.json" +$env:CF_APP_URL = "https://coolify.urieljareth.org/project//environment//application/" +$env:CF_BUILDPACK = "dockercompose" # optional +$env:CF_RELOAD = "1" # MANDATORY after buildpack switch / repo compose change +$env:CF_ENVBULK = (Get-Content .\myapp\.env.coolify -Raw) # KEY=VALUE lines -> "Save All Environment Variables" +$env:CF_DOMAINS = '{"web":"https://myapp.urieljareth.org"}' # pin per-service domains (else Coolify assigns random ones -> 503) +node .\deploy_skill\scripts\coolify-ui\Configure-CoolifyComposeApp.mjs + +# 3. Trigger the deploy (API works even though app CRUD is 404) and watch it +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/deploy?uuid=" +# poll GET /deployments/{deployment_uuid} for status+logs; then verify: +.\deploy_skill\scripts\Test-ServiceOnline.ps1 -Fqdn https://myapp.urieljareth.org -ExpectTitle "" +``` + +## Pipeline scripts + +> **One-time setup:** run `npm install` in the manager repo root to enable the +> Playwright-based operational verification (final pipeline step). Chromium +> binaries live at `%LOCALAPPDATA%\ms-playwright`. Without it, +> `Test-ServiceOnline.ps1` falls back to curl-only. + +> **Two pipelines:** `Publish-ProjectToCoolify.ps1` is for single-app +> Dockerfile builds (POST /applications/public). `New-CoolifyService.ps1` is +> for multi-container Docker Compose stacks (POST /services). Use the right +> one for your project shape. + +### Full pipeline (one shot) + +```powershell +.\deploy_skill\scripts\Publish-ProjectToCoolify.ps1 ` + -AppPath .\my-app ` + -AppName my-app ` + -Fqdn https://my-app.urieljareth.org ` + -Stack node -AppPort 8080 -Init +``` + +Runs: init → checklist → create GitHub repo → push → create Coolify app → +deploy → Coolify-state inspection → **operational verification (HTTP 200 + +Playwright browser)**. One confirmation per step unless `-Force`. + +### Step by step + +```powershell +# 1. Scaffold Dockerfile/compose/.dockerignore/.gitignore additions +.\deploy_skill\scripts\Initialize-CoolifyProject.ps1 -Path .\my-app -Stack node -AppPort 8080 + +# 2. Validate against AGENTS-coolify-apps.md hard rules +.\deploy_skill\scripts\Test-PreDeployChecklist.ps1 -Path .\my-app -ExpectedPort 8080 -Strict + +# 3. Create GitHub repo (idempotent; aborts if it already exists) +.\deploy_skill\scripts\New-GitHubRepo.ps1 -Name my-app -Description "..." [-Private] + +# 4. Push (uses wincred, not the PAT) +cd .\my-app +git init -b main +git remote add origin https://github.com/urieljarethbusiness-cpu/my-app.git +git add -A +git commit -m "Initial commit" +git push -u origin main + +# 5. Create Coolify app + push envs + trigger deploy +.\deploy_skill\scripts\New-CoolifyApplication.ps1 ` + -RepoUrl https://github.com/urieljarethbusiness-cpu/my-app.git ` + -Branch main ` + -Fqdn https://my-app.urieljareth.org ` + -PortsExposes 8080 ` + -EnvFile .\.env.coolify + +# 6. Verify (Coolify state: containers, proxy logs, sibling DNS) +.\deploy_skill\scripts\Test-PostDeploy.ps1 ` + -Fqdn https://my-app.urieljareth.org ` + -ContainerName my-app-main ` + -SiblingService postgres + +# 7. Verify (OPERATIONAL: HTTP 200 + Playwright browser render) +.\deploy_skill\scripts\Test-ServiceOnline.ps1 ` + -Fqdn https://my-app.urieljareth.org ` + -ExpectTitle "My App" -Retries 8 +``` + +### Multi-container stack (Docker Compose) — `New-CoolifyService.ps1` + +For projects that ship a `docker-compose.coolify.yml` (app + DB + sidecars). +POSTs the compose to Coolify's `/services` endpoint. The compose must already +satisfy the hard rules (no published 80/443, DB by service name, +`SERVICE_FQDN__` placeholders for Coolify to wire routes, named +volumes, healthchecks). + +```powershell +.\deploy_skill\scripts\New-CoolifyService.ps1 ` + -AppPath .\my-stack ` + -AppName my-stack ` + -ComposeFile docker-compose.coolify.yml ` + -Fqdn https://my-stack.urieljareth.org ` + -PrimaryService web ` + [-ExtraUrls @{ api = 'https://my-stack-api.urieljareth.org' }] ` + [-ProjectName default] [-EnvironmentName production] ` + [-EnvFile .\my-stack\.env.coolify] ` + [-InstantDeploy | -NoDeploy] ` + [-ServiceUuid ] ` + [-Force] +``` + +`-PrimaryService web` maps the FQDN to the `web` service in the compose (via +the `urls` payload). `-ExtraUrls` adds extra service→FQDN mappings for stacks +with multiple public services. On success, prints `service_uuid`, +`primary_fqdn`, `primary_service` — pipe those into `Test-PostDeploy.ps1` and +`Test-ServiceOnline.ps1` for verification. + +## Rollback / redeploy from a previous commit + +```powershell +.\deploy_skill\scripts\Invoke-CoolifyRollback.ps1 ` + -AppPath .\my-app ` + -ApplicationUuid og88wk4 ` + -CommitSha a1b2c3d +``` + +Pushes the SHA to `rollback/` (main untouched), repoints Coolify, deploys. +You will be prompted for the SHA if you omit `-CommitSha`. + +## GitHub API (low level) + +```powershell +# Who am I? +.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/user" + +# List my repos (paginated; first 30) +.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/user/repos?per_page=30" + +# Check if a repo exists +.\deploy_skill\scripts\Invoke-GitHubApi.ps1 -Path "/repos/urieljarethbusiness-cpu/my-app" + +# Anything else: see https://docs.github.com/rest +``` + +## Coolify API (already documented in coolify_skill) + +The deploy skill reuses `coolify_skill/scripts/Invoke-CoolifyApi.ps1`. Useful +endpoints for the deploy flow: + +```powershell +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/projects" +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/servers" +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications" +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications/" +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Path "/applications//deployments" +.\coolify_skill\scripts\Invoke-CoolifyApi.ps1 -Method POST -Path "/deploy" -BodyJson (@{ uuid = "" } | ConvertTo-Json) +``` + +For per-endpoint schemas, search `coolify_skill/references/ops/` with `rg`. + +## Templates + +| File | Use | +|------|-----| +| `references/templates/Dockerfile.node` | Node app with healthcheck on `/health` | +| `references/templates/Dockerfile.python` | Python/gunicorn app | +| `references/templates/docker-compose.app-db.yml` | App + sibling Postgres, external `coolify` network, named volumes | +| `references/templates/env.local.template.ps1` | Template for the operator's `.env.local.ps1` | + +## Operational verification — Test-ServiceOnline.ps1 + +The "definition of done". Confirms the deployed service actually serves a +working page (not just that Coolify reports the container as running). + +```powershell +.\deploy_skill\scripts\Test-ServiceOnline.ps1 ` + -Fqdn https://my-app.urieljareth.org ` + [-Path /] ` + [-ExpectedStatusCode 200] ` + [-ExpectTitle ""] ` + [-Screenshot ] ` + [-SkipBrowser] ` + [-Retries 8] [-TimeoutMs 30000] +``` + +Two layers (both must pass): + +1. **HTTP (curl)**: status code in 2xx/3xx range. Catches Traefik 502, wrong + `ports_exposes`, TLS DNS challenge not done, missing Cloudflare tunnel route. +2. **Browser (Playwright + Chromium)**: real page navigation. Catches + client-side JS crashes (`pageerror`), failed main-frame requests, blank + error pages. Saves a PNG screenshot for evidence. + +Retries default to 8 (≈80s of attempts with 10s gaps) to absorb Traefik/TLS +warm-up after a fresh deploy. Exits 0 on success, non-zero on failure. + +`-SkipBrowser` runs curl-only (for CI without a display, or when Chromium is +unavailable). If Node/Playwright are missing entirely, the browser layer is +auto-skipped with a warning (NOT a failure) — install with `npm install` in +the manager repo root. + +## Commands that always require confirmation + +- Creating a GitHub repo (`New-GitHubRepo.ps1` warns if it already exists). +- Any `git push` (the pipeline asks before pushing). +- Any Coolify `POST`/`PATCH`/`DELETE` (project, environment, application, env, + deploy). +- Rollback (push + repoint + deploy). +- Anything touching Cloudflare tunnel/DNS. + +`-Force` skips the prompts. Use only after manually reviewing the plan. + +## Troubleshooting + +| Symptom | Likely cause | Fix | +|---------|--------------|-----| +| `git push` asks for username/password | wincred cache miss | Run any git HTTPS op once interactively; or `cmdkey /generic:git:https://github.com /user:urieljarethbusiness-cpu /pass:` | +| GitHub API 401 | `GITHUB_TOKEN` not loaded or expired | `. .\.env.local.ps1`; regenerate PAT at https://github.com/settings/tokens | +| Coolify API 401 | `COOLIFY_TOKEN` not loaded | `. .\.env.local.ps1` | +| App at 502 after deploy | `ports_exposes` mismatch (default is 3000) | `New-CoolifyApplication.ps1 -PortsExposes ` | +| App can't reach DB | used `localhost` or wrong network | Use service name + external `coolify` network (see §2.1, §2.2 in AGENTS-coolify-apps.md) | +| FQDN auto-generated UUID | FQDN not set before first deploy | Always pass `-Fqdn https://.urieljareth.org` | diff --git a/deploy_skill/references/coolify-4.1.2-notes.md b/deploy_skill/references/coolify-4.1.2-notes.md new file mode 100644 index 0000000..be19d05 --- /dev/null +++ b/deploy_skill/references/coolify-4.1.2-notes.md @@ -0,0 +1,103 @@ +# Coolify v4.1.2 on this instance — hard realities (learned the hard way) + +> Written while deploying `cotizador` (Next.js + FastAPI + Postgres, Docker Compose +> build pack, private repo). These are behaviors of the **specific** instance at +> `https://coolify.urieljareth.org`, verified empirically with a root-team token. +> Re-check with `GET /api/v1/version` — if it changed, re-verify the API surface. + +## 1. The REST API surface is PARTIAL — `/applications/*` is 404 + +With a valid **root-team** token (`GET /teams/current` → "Root Team"): + +| Endpoint | Result | +|----------|--------| +| `GET /version`, `GET /teams/current` | 200 | +| `GET /projects`, `POST /projects` | 200 / 201 | +| `GET /projects/{uuid}/environments` | 200 | +| `GET /servers` | 200 | +| `POST /security/keys` (create private key) | 201 | +| `GET /resources` | 200 — **the only reliable app/service/db inventory** | +| `GET /services`, `POST /services`, `/services/{uuid}/envs`, `/start` | work | +| `GET /deploy?uuid=[&force=true]` | 200 — **triggers a deploy of any existing resource by uuid** | +| `GET /deployments/{deployment_uuid}` | 200 — status + logs (logs = JSON string) | +| **`/applications` (list)** | **404** | +| **`/applications/{uuid}` (get / PATCH)** | **404** | +| **`/applications/{uuid}/envs` (list/create)** | **404** | +| **`/applications/{uuid}/logs`** | **404** | +| **`POST /applications/public`** | **404** | +| **`POST /applications/dockerfile`** | **404** | +| **`POST /applications/private-deploy-key`** | **404** | +| **`POST /applications/private-github-app`** | **404** | +| **`POST /applications/dockercompose`** | **404** (deprecated upstream anyway) | +| `GET /github-apps` | 404 | + +**Consequence:** you CANNOT create OR configure a **git-based application that builds +from source** (Dockerfile or Docker Compose build pack) via the API here. The skill's +`New-CoolifyApplication.ps1` (POST /applications/public) will 404 on this instance. +`POST /services` only accepts `docker_compose_raw` with **no git repo** → only good for +**prebuilt-image** stacks, not `build:`-from-source composes. + +→ For git+build apps you MUST use the **web UI** (drive it with Playwright). Application +config (build pack, env vars, domains) is UI-only. Only the **deploy trigger** is API-able +(`GET /deploy?uuid=`), and reads via `/resources` + `/deployments/{uuid}`. + +## 2. Inventory & status via `/resources` +`GET /resources` returns every application/service/database with `uuid`, `name`, +`status` (e.g. `running:healthy`, `exited:unhealthy`), `fqdn`, `build_pack`, +`git_repository`, `docker_compose_location`, `docker_compose_raw`, `docker_compose` +(parsed), `source_type`/`source_id`, `environment_id`, `destination`. Map an app's +`environment_id` to a project by scanning `/projects/{uuid}/environments`. + +Build the UI URL as: +`https://coolify.urieljareth.org/project/{project_uuid}/environment/{env_uuid}/application/{app_uuid}` +(+ `/environment-variables`, `/logs`, `/deployment`, `/source`, `/danger`, …) + +## 3. Driving the UI with Playwright (Livewire app) +- Login: `/login`, fill `input[type=email]` + `input[type=password]`, submit; save + `storageState` and reuse it (see `scripts/coolify-ui/coolify-login.mjs`). +- Import Playwright from the manager repo when the script lives elsewhere: + `createRequire('/package.json')` then `require('playwright')`. +- Dismiss recurring popups: buttons "Accept and Close", "Acknowledge & Disable This Popup", + "Maybe next time". +- **Build Pack** is a `