|
|
|
|
@@ -1,5 +1,12 @@
|
|
|
|
|
name: __APP_NAME__ — build & deploy
|
|
|
|
|
|
|
|
|
|
# Workflow self-contained del golden path (ADR-061): calidad + build + push +
|
|
|
|
|
# helm-deploy inline, SIN `uses:` cross-repo al repo privado `coralware/IDP`.
|
|
|
|
|
# El repo del tenant es un repo cliente — su CI no comparte el ciclo de release
|
|
|
|
|
# del IDP ni el GitOps de Fleet; se construye leyendo solo su propio repo.
|
|
|
|
|
# Corre sobre los runners `[self-hosted, buildkit]` de ci-system (executor=host,
|
|
|
|
|
# buildkit remoto — sin Docker daemon local; ADR-043).
|
|
|
|
|
|
|
|
|
|
on:
|
|
|
|
|
push:
|
|
|
|
|
branches: [main]
|
|
|
|
|
@@ -7,29 +14,101 @@ on:
|
|
|
|
|
- 'code/**'
|
|
|
|
|
- 'helm/**'
|
|
|
|
|
- '.gitea/workflows/__APP_NAME__-build.yaml'
|
|
|
|
|
workflow_dispatch: {}
|
|
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
|
# Tests + build + push a Harbor — patrón ADR-038 reusable workflow.
|
|
|
|
|
# ── Calidad + build + push de imagen a Harbor ────────────────────────────
|
|
|
|
|
build:
|
|
|
|
|
uses: coralware/IDP/.gitea/workflows/_python-microservice.yaml@main
|
|
|
|
|
with:
|
|
|
|
|
service_name: __APP_NAME__
|
|
|
|
|
service_dir: code
|
|
|
|
|
uses_metrics: false
|
|
|
|
|
coverage_threshold: 80
|
|
|
|
|
trivy_exit_code: 0
|
|
|
|
|
enforce_rollback: false
|
|
|
|
|
name: Calidad + build + push
|
|
|
|
|
runs-on: [self-hosted, buildkit]
|
|
|
|
|
outputs:
|
|
|
|
|
image_tag: ${{ steps.meta.outputs.tag }}
|
|
|
|
|
defaults:
|
|
|
|
|
run:
|
|
|
|
|
working-directory: code
|
|
|
|
|
steps:
|
|
|
|
|
- name: Checkout
|
|
|
|
|
uses: actions/checkout@v4
|
|
|
|
|
|
|
|
|
|
# Deploy al cluster del tenant — Helm upgrade --install (sin hook Alembic).
|
|
|
|
|
- name: Crear venv e instalar dependencias
|
|
|
|
|
# --system-site-packages reutiliza ruff/black/bandit/pytest ya
|
|
|
|
|
# preinstalados en la imagen del runner; el venv añade las deps de la app.
|
|
|
|
|
run: |
|
|
|
|
|
python3 -m venv --system-site-packages /tmp/venv-__APP_NAME__
|
|
|
|
|
/tmp/venv-__APP_NAME__/bin/pip install --no-cache-dir -r requirements-dev.txt
|
|
|
|
|
|
|
|
|
|
- name: Lint (ruff)
|
|
|
|
|
run: ruff check app
|
|
|
|
|
|
|
|
|
|
- name: Formato (black --check)
|
|
|
|
|
run: black --check app
|
|
|
|
|
|
|
|
|
|
- name: Seguridad (bandit)
|
|
|
|
|
run: bandit -c pyproject.toml -r app --severity-level medium
|
|
|
|
|
|
|
|
|
|
- name: Tests con cobertura
|
|
|
|
|
run: |
|
|
|
|
|
/tmp/venv-__APP_NAME__/bin/python -m pytest tests/ -v \
|
|
|
|
|
--cov=app --cov-report=term-missing --cov-fail-under=80
|
|
|
|
|
|
|
|
|
|
- name: Metadata de imagen
|
|
|
|
|
id: meta
|
|
|
|
|
run: echo "tag=${{ gitea.sha }}" >> "$GITHUB_OUTPUT"
|
|
|
|
|
|
|
|
|
|
- name: Build y push a Harbor
|
|
|
|
|
# Proyecto Harbor del tenant: `t-<tenant-id-short>` (ADR-019 D-03).
|
|
|
|
|
# Credenciales: robot scoped push/pull SOLO a ese proyecto, sembrado
|
|
|
|
|
# por tenant-manager en el repo del tenant (paso 3 del reconcile).
|
|
|
|
|
env:
|
|
|
|
|
REGISTRY: registry.coralsafety.com
|
|
|
|
|
ROBOT_USER: ${{ secrets.HARBOR_ROBOT_USER }}
|
|
|
|
|
ROBOT_TOKEN: ${{ secrets.HARBOR_ROBOT_TOKEN }}
|
|
|
|
|
IMAGE: t-__TENANT_ID_SHORT__/__APP_NAME__
|
|
|
|
|
TAG: ${{ steps.meta.outputs.tag }}
|
|
|
|
|
run: |
|
|
|
|
|
if [ -z "$ROBOT_USER" ] || [ -z "$ROBOT_TOKEN" ]; then
|
|
|
|
|
echo "ERROR: secrets HARBOR_ROBOT_USER/HARBOR_ROBOT_TOKEN no configurados."
|
|
|
|
|
echo "tenant-manager debe sembrarlos al crear el repo (paso 3 del reconcile)."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
printf '%s' "$ROBOT_TOKEN" | docker login "$REGISTRY" -u "$ROBOT_USER" --password-stdin
|
|
|
|
|
# Builder buildx remoto — apunta al buildkit daemon de ci-system
|
|
|
|
|
# (el runner no tiene Docker daemon local).
|
|
|
|
|
if ! docker buildx inspect tenant-builder >/dev/null 2>&1; then
|
|
|
|
|
docker buildx create --name tenant-builder --driver remote --use \
|
|
|
|
|
"${BUILDKIT_HOST:-tcp://buildkitd.ci-system.svc.cluster.local:2375}"
|
|
|
|
|
else
|
|
|
|
|
docker buildx use tenant-builder
|
|
|
|
|
fi
|
|
|
|
|
docker buildx build --platform linux/amd64 \
|
|
|
|
|
-t "${REGISTRY}/${IMAGE}:${TAG}" \
|
|
|
|
|
--push .
|
|
|
|
|
|
|
|
|
|
# ── Deploy al cluster del tenant — Helm upgrade --install (sin hook Alembic) ─
|
|
|
|
|
# El runner corre executor=host: los steps se ejecutan directo sobre el pod
|
|
|
|
|
# runner, sin `container:`. helm se instala en runtime.
|
|
|
|
|
helm-deploy:
|
|
|
|
|
name: Helm upgrade --install
|
|
|
|
|
needs: build
|
|
|
|
|
runs-on: [self-hosted, linux/amd64]
|
|
|
|
|
container: alpine/helm:3.14.4
|
|
|
|
|
runs-on: [self-hosted, buildkit]
|
|
|
|
|
steps:
|
|
|
|
|
- uses: actions/checkout@v4
|
|
|
|
|
|
|
|
|
|
- name: Install kubectl
|
|
|
|
|
run: apk add --no-cache kubectl
|
|
|
|
|
- name: Instalar helm
|
|
|
|
|
run: |
|
|
|
|
|
HELM_VERSION=v3.14.4
|
|
|
|
|
case "$(uname -m)" in
|
|
|
|
|
x86_64) ARCH=amd64 ;;
|
|
|
|
|
aarch64) ARCH=arm64 ;;
|
|
|
|
|
*) echo "Arquitectura no soportada: $(uname -m)"; exit 1 ;;
|
|
|
|
|
esac
|
|
|
|
|
mkdir -p "${HOME}/.local/bin"
|
|
|
|
|
curl -fsSL "https://get.helm.sh/helm-${HELM_VERSION}-linux-${ARCH}.tar.gz" \
|
|
|
|
|
| tar -xz -C /tmp "linux-${ARCH}/helm"
|
|
|
|
|
mv "/tmp/linux-${ARCH}/helm" "${HOME}/.local/bin/helm"
|
|
|
|
|
chmod +x "${HOME}/.local/bin/helm"
|
|
|
|
|
echo "${HOME}/.local/bin" >> "$GITHUB_PATH"
|
|
|
|
|
"${HOME}/.local/bin/helm" version
|
|
|
|
|
|
|
|
|
|
- name: Decodificar kubeconfig del tenant
|
|
|
|
|
env:
|
|
|
|
|
@@ -40,9 +119,9 @@ jobs:
|
|
|
|
|
echo "tenant-manager debe provisionarlo al crear el repo."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
mkdir -p /root/.kube
|
|
|
|
|
echo "$KUBECONFIG_B64" | base64 -d > /root/.kube/config
|
|
|
|
|
chmod 600 /root/.kube/config
|
|
|
|
|
mkdir -p "${HOME}/.kube"
|
|
|
|
|
echo "$KUBECONFIG_B64" | base64 -d > "${HOME}/.kube/config"
|
|
|
|
|
chmod 600 "${HOME}/.kube/config"
|
|
|
|
|
|
|
|
|
|
- name: Helm upgrade --install
|
|
|
|
|
env:
|
|
|
|
|
@@ -67,10 +146,8 @@ jobs:
|
|
|
|
|
|
|
|
|
|
- name: Smoke check post-deploy
|
|
|
|
|
# FQDN con tenant-id CON guiones — coincide con el A record que crea
|
|
|
|
|
# cloudflare-provisioner (Gap 5) y con el host del Ingress NGINX
|
|
|
|
|
# (helm/APP_NAME/values.yaml). Antes usaba TENANT_ID_NODASHES, lo
|
|
|
|
|
# que no resolvía al A record canónico — corregido APERTURA COVE
|
|
|
|
|
# Sesión 3 (TENANT-INGRESS-CONTROLLER-MISMATCH, paridad ADR-053 §11.1).
|
|
|
|
|
# cloudflare-provisioner y con el host del Ingress NGINX
|
|
|
|
|
# (helm/APP_NAME/values.yaml). Paridad ADR-053 §11.1.
|
|
|
|
|
env:
|
|
|
|
|
INGRESS_HOST: __APP_NAME__-${{ vars.TENANT_ID }}.apps.coralware.cloud
|
|
|
|
|
run: |
|
|
|
|
|
|