Files
0pt/.gitea/workflows/prod-smoke.yml
T
admin c01deca048
CI (SIAX Cloud) / sonar (pull_request) Skipped
masterplan-lock / check (push) Successful in 5s
masterplan-lock / check (pull_request) Successful in 10s
CI (SIAX Cloud) / security (pull_request) Successful in 19s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 21s
CI (SIAX Cloud) / integration (pull_request) Successful in 40s
CI (SIAX Cloud) / quality (pull_request) Successful in 2m13s
ci: optimize delivery pipelines and add a production smoke gate
- One run per ref (concurrency) for CI, security, nightly and release; branch
  pushes no longer duplicate pull_request runs (push is main-only).
- Deploys keep their own concurrency group so two runs never race Coolify.
- pnpm store cache on every Node job.
- New /api/version returns the deployed commit (no secrets).
- New scheduled prod-smoke workflow: readiness, db/stripe health, running
  commit == main, and the document/credit routes exist (401/405, never 5xx).
2026-11-02 18:24:45 +01:00

78 lines
3.1 KiB
YAML

name: Production smoke (0pt.app)
# Automated end-to-end delivery verification. Every 30 minutes (and on demand)
# this checks that the live service is actually ready and that the running
# container is the commit that main points at — so a green deploy is never
# confused with a healthy, current service.
on:
workflow_dispatch:
schedule:
- cron: "*/30 * * * *"
concurrency:
group: prod-smoke
cancel-in-progress: true
jobs:
smoke:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Readiness (retry on a fresh deploy)
run: |
set -eu
for attempt in 1 2 3 4 5 6; do
body="$(curl -fsS --max-time 15 https://0pt.app/api/ready || true)"
echo "attempt $attempt: ${body:-<no response>}"
if printf '%s' "$body" | grep -q '"ready":true'; then
exit 0
fi
sleep 20
done
echo "::error::0pt.app/api/ready never reported ready:true"
exit 1
- name: Health envelope
run: |
set -eu
body="$(curl -fsS --max-time 15 https://0pt.app/api/health)"
echo "$body"
printf '%s' "$body" | grep -q '"db":"ok"' || { echo "::error::database is not ok"; exit 1; }
printf '%s' "$body" | grep -q '"stripe":"ok"' || { echo "::error::stripe is not ok"; exit 1; }
- name: Deployed commit equals main
env:
SIAX_CI_TOKEN: ${{ secrets.SIAX_CI_TOKEN }}
run: |
set -eu
version="$(curl -fsS --max-time 15 https://0pt.app/api/version)"
echo "$version"
running="$(printf '%s' "$version" | sed -n 's/.*"commit":"\([0-9a-f]*\)".*/\1/p')"
[ -n "$running" ] || { echo "::error::/api/version returned no commit"; exit 1; }
if [ -z "${SIAX_CI_TOKEN:-}" ]; then
echo "::warning::SIAX_CI_TOKEN is not set — cannot compare with main; commit $running is live"
exit 0
fi
expected="$(curl -fsS --max-time 15 \
-H "Authorization: token ${SIAX_CI_TOKEN}" \
https://git.siax.io/api/v1/repos/admin/0pt/commits/main \
| sed -n 's/.*"sha":"\([0-9a-f]*\)".*/\1/p')"
echo "running=$running expected(main)=$expected"
[ -n "$expected" ] || { echo "::error::could not read main SHA from Gitea"; exit 1; }
[ "$running" = "$expected" ] || {
echo "::error::production is $running but main is $expected — deploy did not land"
exit 1
}
- name: Document pipeline surface responds (auth required, never 404/5xx)
run: |
set -eu
for path in /api/0pt/documents/upload /api/0pt/documents/structure /api/0pt/documents/models /api/0pt/credits/balance; do
code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 -X POST "https://0pt.app${path}")"
echo "$path -> $code"
case "$code" in
401|405) ;; # unauthenticated or method guard: the route exists
*) echo "::error::$path returned $code (expected 401/405)"; exit 1 ;;
esac
done