Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -860,14 +860,30 @@ jobs:
# workspaces, in dependency order, with --provenance, idempotent against the
# registry, and rehearsable via a dry run that defaults to on.

# GT-679 — the image build now runs on the PR, not after it.
#
# This job was gated `if: ref == main || tags`, which meant the question "does
# this still build?" was asked only AFTER every gate that could have stopped
# the change had already let it through. That is not a theoretical ordering
# complaint: a broken agent-runtime Dockerfile crossed PRs #669, #670 and #673
# carrying 46, 78 and 47 green checks, and was found on main, three promotions
# later, by a human reading the registry.
#
# So the BUILD runs everywhere and the PUSH stays ref-guarded. A pull request
# proves all three images compile; only main and version tags publish to GHCR,
# which is the same publishing behaviour this job had before.
docker-services:
name: Build & Push Services (GHCR)
name: Build Services (GHCR)
needs: [test, test-core-domain, test-core, test-mcp-server, test-core-api, test-sdk-client, test-infra-providers]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
permissions:
contents: read
packages: write
env:
# Single source of truth for "is this a publishing ref?", read by both the
# login step and the push flag below. On a pull_request `github.ref` is
# refs/pull/N/merge, so this is false and nothing reaches the registry.
PUBLISH: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }}
strategy:
fail-fast: false
matrix:
Expand All @@ -894,22 +910,58 @@ jobs:
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
if: env.PUBLISH == 'true'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push ${{ matrix.service }}
- name: Build ${{ matrix.service }} (push only from main/tags)
uses: docker/build-push-action@v5
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: true
push: ${{ env.PUBLISH == 'true' }}
# Now that this runs on every PR, an uncached three-image rebuild would
# be the slowest thing in the pipeline. Scoped per service so the three
# matrix legs do not evict each other.
cache-from: type=gha,scope=${{ matrix.service }}
cache-to: type=gha,mode=max,scope=${{ matrix.service }}
tags: |
ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:latest
ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}:${{ github.sha }}

# A matrix job reports one check per leg, and each context carries its matrix
# values -- "Build Services (GHCR) (core-api, ., ./src/apps/core-api/Dockerfile)".
# Those names change whenever a path or a service does, so they are a poor thing
# for branch protection to name. This job collapses the three into one stable
# context that CAN be marked required.
docker-services-gate:
name: Services build (GHCR)
needs: [docker-services]
runs-on: ubuntu-latest
if: always()
steps:
- name: Assert every service image built
run: |
result='${{ needs.docker-services.result }}'
echo "docker-services: ${result}"
case "${result}" in
success)
echo "las tres imagenes construyen"
;;
skipped)
# An upstream test suite failed, so the build never ran. That
# failure already has its own red check; do not double-report it.
echo "no se construyo: una suite previa fallo y lo reporta su propio check"
;;
*)
echo "::error::al menos una imagen de servicio no construye"
exit 1
;;
esac

# GT-324 — deploy the freshly-pushed images to the Coolify runtime.
#
# REWRITTEN 2026-08-22. The previous version fanned out to THREE per-service deploy
Expand Down
20 changes: 13 additions & 7 deletions reference/core/control-center/deploy/README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,16 @@ Fuente de verdad: `.github/workflows/ci-cd.yml`.
1. **Build & test** — los jobs `Test`, `Test core-domain`, `Test core`,
`Test mcp-server`, `Test core-api`, `Test sdk-client`, `Test contract`,
`Test infra-providers` corren en cada PR a `main`/`develop`.
2. **`Build & Push Services (GHCR)`** (job `docker-services`) — corre solo en
`main` o tags `v*`. Construye `core-api`, `mcp-server`, `agent-runtime` y
publica `:latest` + `:<sha>` en GHCR usando el `GITHUB_TOKEN` incorporado (sin
secret extra).
2. **`Build Services (GHCR)`** (job `docker-services`) — construye `core-api`,
`mcp-server` y `agent-runtime` en **cada PR**, y publica `:latest` + `:<sha>`
en GHCR solo desde `main` o tags `v*`, usando el `GITHUB_TOKEN` incorporado
(sin secret extra). El build era solo-main hasta GT-679; un Dockerfile roto
cruzó tres PRs en verde porque el único job que lo habría detenido corría
después de que ya hubieran mergeado. Las tres ramas del matrix se colapsan en
un único check estable, **`Services build (GHCR)`** (job
`docker-services-gate`), que es el contexto que nombra la protección de rama:
los contextos de matrix llevan sus parámetros y cambian cada vez que cambia
una ruta.
3. **`Deploy services (Coolify)`** (job `deploy`, `needs: [docker-services]`) —
el paso de promoción. Su guarda es:

Expand Down Expand Up @@ -194,9 +200,9 @@ el go-live irreversible y le corresponde ejecutarlo al usuario.**
Con estos definidos y `VPS_DEPLOY_ENABLED` aún sin definir, el job de deploy sigue
saltándose — nada se despliega todavía.
3. **Mergea un cambio normal a `main`** (o vuelve a ejecutar el workflow) y confirma
que el job `Build & Push Services (GHCR)` está en verde y las tres imágenes aparecen
en GHCR. El job `deploy` debería seguir mostrándose como **skipped** (la guarda está
apagada).
que el job `Build Services (GHCR)` está en verde y las tres imágenes aparecen en
GHCR (en un PR el mismo job va en verde sin publicar nada). El job `deploy` debería
seguir mostrándose como **skipped** (la guarda está apagada).
4. **Opcionalmente despliega una vez manualmente desde la UI de Coolify** (cada app →
Deploy) para probar que las imágenes arrancan y el env de runtime es correcto, antes
de cablear el disparador automático. Observa que cada `/health` pase a verde.
Expand Down
18 changes: 12 additions & 6 deletions reference/core/control-center/deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,15 @@ Source of truth: `.github/workflows/ci-cd.yml`.
1. **Build & test** — jobs `Test`, `Test core-domain`, `Test core`,
`Test mcp-server`, `Test core-api`, `Test sdk-client`, `Test contract`,
`Test infra-providers` run on every PR to `main`/`develop`.
2. **`Build & Push Services (GHCR)`** (job `docker-services`) — runs only on
`main` or `v*` tags. Builds `core-api`, `mcp-server`, `agent-runtime` and
pushes `:latest` + `:<sha>` to GHCR using the built-in `GITHUB_TOKEN` (no
extra secret).
2. **`Build Services (GHCR)`** (job `docker-services`) — builds `core-api`,
`mcp-server` and `agent-runtime` on **every PR**, and pushes `:latest` +
`:<sha>` to GHCR only from `main` or `v*` tags, using the built-in
`GITHUB_TOKEN` (no extra secret). The build was main-only until GT-679; a
broken Dockerfile crossed three PRs with green checks because the only job
that would have caught it ran after they had already merged. The three matrix
legs are collapsed into one stable check, **`Services build (GHCR)`** (job
`docker-services-gate`), which is the context branch protection names —
matrix contexts carry their parameters and change whenever a path does.
3. **`Deploy services (Coolify)`** (job `deploy`, `needs: [docker-services]`) —
the promotion step. Its guard is:

Expand Down Expand Up @@ -188,8 +193,9 @@ irreversible go-live and is the user's to run.**
With these set and `VPS_DEPLOY_ENABLED` still unset, the deploy job stays
skipped — nothing deploys yet.
3. **Merge a normal change to `main`** (or re-run the workflow) and confirm the
`Build & Push Services (GHCR)` job is green and the three images appear in
GHCR. The `deploy` job should still show as **skipped** (guard is off).
`Build Services (GHCR)` job is green and the three images appear in GHCR (on a
PR the same job is green without publishing anything). The `deploy` job should
still show as **skipped** (guard is off).
4. **Optionally deploy once manually from the Coolify UI** (each app → Deploy) to
prove the images boot and the runtime env is correct, before wiring the
automatic trigger. Watch each `/health` go green.
Expand Down
Loading