ci: deploy the three Coolify apps from a manual job on main
Adds the CD half to the merge gate that shipped in 25c4b84c. .gitlab-ci.yml
gains a deploy stage with one manual job per app — deploy:orestar-api,
deploy:api-gateway, deploy:web — driven by ci/coolify-deploy.sh.
WHY THE LAPTOP PATH HAD TO GO
task coolify:deploy from ~/dev/datakit/platform has two defects that only
appear when they matter. The coolify binary is macOS/arm64 only, so it cannot
run on a runner or anyone else's machine: the deploy path was one laptop wide.
And op run blocks on a biometric prompt once 1Password auto-locks, so an
unattended invocation hangs rather than fails — hit twice while authoring this.
WHY MANUAL
docs/runbooks/prod-deploy.md requires prod's schema to land before the code that expects it (migrate prod -> verify -> deploy), and migrations run over ssh outside this pipeline. An auto-deploy on green main would ship code ahead of its schema, which is the current_council=true 500 the runbook already describes. The button keeps the ordering and removes only what was accidental about the old path. Three jobs rather than one because the apps share a repo but not a release cadence; resource_group stops a second click racing the first.
WHY IT WAITS INSTEAD OF FIRING AND FORGETTING
Coolify's endpoint returns when the deployment is queued, so a trigger-only job goes green while the build is still running, or while it fails. The script polls to a terminal state and then asserts two things the trigger cannot:
- The commit. Coolify clones main itself and never receives our checkout, so a deploy fired from an older pipeline lands whatever main points at now. The runbook already records being bitten by this. Coolify's recorded commit is compared against CI_COMMIT_SHA.
- The site. services/orestar-api/README.md requires every deploy path that touches credentials to end in a smoke check against the public URL.
API paths (applications/{uuid}/start, deployments/{uuid}) were read out of the coolify binary's string table rather than guessed, and match the CLI call that deployed 8223678f by hand today.
Also corrected: the header note claiming the instance has zero runners and that this pipeline cannot execute. True when written on 2026-08-31, false by that evening once gitlab-runner-01 came up and pipeline #14 ran green.
OPERATOR GATES BEFORE THE FIRST RUN
- COOLIFY_TOKEN as a masked + protected CI/CD variable. This is the first CI variable on the instance, so it is also the first thing whose loss matters in a GitLab restore; it is recoverable from 1Password, not from a backup.
- main must be protected, or a protected variable is invisible to the job and every call 401s. The script names that failure rather than letting curl report a bare 401.
The deploy jobs do not run on merge request pipelines, so this MR exercises the config without any deploy risk. op was locked while authoring, so the endpoints could not be exercised end to end from a shell — same posture as the merge gate, which was authored against a runner that did not exist yet and verified on first contact.
Substrate plan 5075, hub c072.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com Claude-Session: https://claude.ai/code/session_014xAQu4e4Ek5X7LyYLfcx7Y