feat(deploy): padroniza rx p/ Build Once->Promote->Deploy (CI registry, compose.prod, badge/health)
- .gitea/workflows/build.yml: push->build rx-scoreodonto:<sha> no registry; tag->promote+deploy - docker-compose.prod.yml: image do registry, APP_VERSION/GIT_COMMIT em runtime - deploy-prod.sh: VPS1 pull + up --no-build (antigo vira deploy-prod-legado-webhook.sh) - /api/health usa APP_VERSION (tag) com fallback version.txt; badge no canto - webhook 9003 aposentado (gatilho agora e CI/tag) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
name: build-and-promote
|
||||
|
||||
# Build Once → Promote → Deploy (modelo da stack — ver Documentos-Unicos/deploy):
|
||||
# • push na main → job BUILD: builda 1x e publica por COMMIT (:<sha> + :latest). NÃO deploya.
|
||||
# • tag vX.Y.Z → job PROMOTE: re-tagueia a imagem do commit como :vX.Y.Z (SEM rebuild),
|
||||
# valida digest e deploya na VPS1 (pull + up --no-build).
|
||||
# A versão semântica NÃO entra no build: nasce da tag e é injetada em runtime (docker-compose.prod.yml).
|
||||
# O React (Vite) é buildado como step ANTES do docker build (o Dockerfile atual espera o dist/ pronto).
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
# ───────────────────────── BUILD (só em push de branch) ─────────────────────────
|
||||
build:
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Vars (commit + data)
|
||||
id: vars
|
||||
run: |
|
||||
echo "commit=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
echo "build_time=$(date -u +'%Y-%m-%d %H:%M UTC')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login no registry
|
||||
run: |
|
||||
if [ -n "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.clube67.com -u ruicesar --password-stdin
|
||||
else
|
||||
echo "REGISTRY_TOKEN ausente — usando a credencial já presente no host."
|
||||
fi
|
||||
|
||||
- name: Build do React (Vite) — gera dental-client/dist/ antes do docker build
|
||||
run: |
|
||||
set -e
|
||||
cp dental-server/version.txt dental-server/dental-client/public/version.txt
|
||||
cd dental-server/dental-client
|
||||
NODE_ENV=development npm install --include=dev --prefer-offline
|
||||
npm run build
|
||||
ls dist/index.html >/dev/null && echo "OK: dist/ pronto"
|
||||
|
||||
- name: Build da imagem (inclui dental-client/dist/)
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: rxscoreodontocom
|
||||
run: docker compose -f docker-compose.yml build app
|
||||
|
||||
- name: Publicar por COMMIT (:<sha> + :latest), com retry p/ 499
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"
|
||||
SRC="rxscoreodontocom-app:latest"
|
||||
DST="${REG}/rx-scoreodonto"
|
||||
docker tag "$SRC" "$DST:$C"
|
||||
docker tag "$SRC" "$DST:latest"
|
||||
for tag in "$C" "latest"; do
|
||||
n=0
|
||||
until docker push "$DST:$tag"; do
|
||||
n=$((n+1)); [ $n -ge 4 ] && { echo "push falhou: $DST:$tag"; exit 1; }
|
||||
echo "retry $n (499 transitório) em $DST:$tag"; sleep 3
|
||||
done
|
||||
done
|
||||
echo "Publicado: rx-scoreodonto:$C (+ :latest). Push NÃO deploya."
|
||||
|
||||
# ─────────────────────── PROMOTE (só em tag vX.Y.Z) ───────────────────────
|
||||
promote:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout (na tag)
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Vars (tag + commit)
|
||||
id: vars
|
||||
run: |
|
||||
echo "version=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "commit=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Login no registry
|
||||
run: |
|
||||
if [ -n "${{ secrets.REGISTRY_TOKEN }}" ]; then
|
||||
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.clube67.com -u ruicesar --password-stdin
|
||||
fi
|
||||
|
||||
- name: Integridade — a imagem do commit existe? (nunca buildar na tag)
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"
|
||||
if ! docker buildx imagetools inspect "${REG}/rx-scoreodonto:$C" >/dev/null 2>&1; then
|
||||
echo "ERRO: ${REG}/rx-scoreodonto:$C não existe no registry."
|
||||
echo " Faça 'git push origin main' (que builda o commit $C) ANTES de criar a tag."
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: rx-scoreodonto:$C presente no registry."
|
||||
|
||||
- name: Promover — re-tag commit → versão (SEM rebuild) + conferir digest
|
||||
run: |
|
||||
set -e
|
||||
REG=git.clube67.com/ruicesar
|
||||
C="${{ steps.vars.outputs.commit }}"; V="${{ steps.vars.outputs.version }}"
|
||||
DST="${REG}/rx-scoreodonto"
|
||||
docker buildx imagetools create -t "$DST:$V" "$DST:$C"
|
||||
dC=$(docker buildx imagetools inspect "$DST:$C" --format '{{.Manifest.Digest}}')
|
||||
dV=$(docker buildx imagetools inspect "$DST:$V" --format '{{.Manifest.Digest}}')
|
||||
[ "$dC" = "$dV" ] || { echo "ERRO: digest divergiu ($V=$dV != $C=$dC)"; exit 1; }
|
||||
echo "Promovido sem rebuild: :$V e :$C apontam para o mesmo artefato (digest $dV)."
|
||||
|
||||
- name: Deploy na VPS1 (pull :versao + up --no-build; versao/commit injetados em runtime)
|
||||
run: |
|
||||
set -e
|
||||
V="${{ steps.vars.outputs.version }}"; C="${{ steps.vars.outputs.commit }}"
|
||||
scp -o StrictHostKeyChecking=accept-new \
|
||||
docker-compose.yml docker-compose.prod.yml deploy-prod.sh \
|
||||
deploy@10.99.0.1:/home/deploy/stack/rx.scoreodonto.com/
|
||||
ssh -o StrictHostKeyChecking=accept-new deploy@10.99.0.1 \
|
||||
"/home/deploy/stack/rx.scoreodonto.com/deploy-prod.sh $V $C"
|
||||
Reference in New Issue
Block a user