Files
Antonio Lopes dos Santos afec302c3e
All checks were successful
build-api-image / build (push) Successful in 51s
init commit
2026-08-04 16:52:02 -03:00

160 lines
5.6 KiB
Bash
Executable File

#!/bin/bash
#
# Deploy de um ambiente do ConectaSUS V2.
#
# ./deploy.sh <ambiente> <tag>
#
# Le os parametros do ambiente de docker/deploy/<ambiente>.env, troca a imagem
# do container e so considera o deploy bem-sucedido depois de verificar que a
# aplicacao responde. Se nao responder, volta sozinho para a imagem anterior.
#
# Roda a partir do runner, que usa o docker.sock do host — por isso todos os
# caminhos de mount sao caminhos do HOST, nao do job.
set -euo pipefail
AMBIENTE="${1:-}"
TAG="${2:-}"
REGISTRY="${REGISTRY:-192.168.0.41:3001}"
IMAGE_NAME="${IMAGE_NAME:-rkm/conectasus-api}"
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-90}"
AQUI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
log() { echo "[deploy] $*"; }
fail() { echo "[deploy][ERRO] $*" >&2; exit 1; }
[ -n "${AMBIENTE}" ] || fail "uso: $0 <ambiente> <tag>"
[ -n "${TAG}" ] || fail "uso: $0 <ambiente> <tag>"
ENV_FILE="${AQUI}/${AMBIENTE}.env"
[ -f "${ENV_FILE}" ] || fail "ambiente desconhecido: ${AMBIENTE} (esperado ${ENV_FILE})"
# shellcheck disable=SC1090
set -a; . "${ENV_FILE}"; set +a
# Gabarito nao preenchido: melhor recusar do que implantar em lugar errado.
if grep -q 'TROCAR' "${ENV_FILE}"; then
fail "${ENV_FILE} ainda tem valores de gabarito (TROCAR)."
fi
for v in CONTAINER_NAME CONFIG_DIR PORT_API PORT_SPA PORT_WS; do
[ -n "${!v:-}" ] || fail "${v} nao definido em ${ENV_FILE}"
done
IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}"
log "ambiente : ${AMBIENTE}"
log "container: ${CONTAINER_NAME}"
log "imagem : ${IMAGE}"
# ---------------------------------------------------------------------------
# 1. Guarda a imagem atual para poder voltar. Vale o digest, nao a tag: a tag e
# mutavel e pode ja apontar para outra coisa na hora do rollback.
# ---------------------------------------------------------------------------
ANTERIOR=""
if docker inspect "${CONTAINER_NAME}" >/dev/null 2>&1; then
ANTERIOR=$(docker inspect "${CONTAINER_NAME}" --format '{{.Image}}')
log "imagem atual: ${ANTERIOR}"
else
log "container ainda nao existe; primeiro deploy"
fi
log "puxando ${IMAGE}"
docker pull "${IMAGE}" >/dev/null || fail "pull falhou (registry acessivel? credencial presente?)"
# ---------------------------------------------------------------------------
# 2. Config do host. O shell do job pode rodar dentro de um container do
# act_runner e, portanto, nao enxergar os caminhos do host. A verificacao
# precisa passar pelo daemon Docker: e ele que avaliara o bind mount no
# host e depois subira a aplicacao com esses mesmos arquivos.
#
# --mount, diferentemente de -v, recusa uma origem inexistente em vez de
# criar um diretorio vazio silenciosamente.
# ---------------------------------------------------------------------------
log "validando configuracao no host Docker"
if ! docker run --rm \
--mount "type=bind,src=${CONFIG_DIR},dst=/config,readonly" \
--entrypoint sh \
"${IMAGE}" \
-c '
set -eu
for f in .env connections.json config.js; do
[ -f "/config/${f}" ] || {
echo "arquivo obrigatorio ausente: /config/${f}" >&2
exit 1
}
done
[ -d /config/storage ] || {
echo "diretorio obrigatorio ausente: /config/storage" >&2
exit 1
}
'
then
fail "configuracao invalida ou inacessivel no host Docker: ${CONFIG_DIR}"
fi
subir() {
local img="$1"
docker rm -f "${CONTAINER_NAME}" >/dev/null 2>&1 || true
docker run -d \
--name "${CONTAINER_NAME}" \
--restart unless-stopped \
${NETWORK:+--network "${NETWORK}"} \
-p "${PORT_API}:80" \
-p "${PORT_SPA}:8080" \
-p "${PORT_WS}:6001" \
-v "${CONFIG_DIR}/.env:/var/www/api/.env:ro" \
-v "${CONFIG_DIR}/connections.json:/var/www/api/connections.json:ro" \
-v "${CONFIG_DIR}/config.js:/var/www/frontend/config.js:ro" \
-v "${CONFIG_DIR}/storage:/var/www/api/storage" \
"${img}" >/dev/null
}
saudavel() {
local fim=$((SECONDS + HEALTH_TIMEOUT))
while [ ${SECONDS} -lt ${fim} ]; do
if [ "$(docker inspect -f '{{.State.Running}}' "${CONTAINER_NAME}" 2>/dev/null)" != "true" ]; then
return 1
fi
local spa api
spa=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/ 2>/dev/null || echo 000)
api=$(docker exec "${CONTAINER_NAME}" curl -s -o /dev/null -w '%{http_code}' http://localhost:80/ 2>/dev/null || echo 000)
# A API nao tem rota "/", entao 404 e resposta valida; 000 e que nao e.
if [ "${spa}" = "200" ] && [ "${api}" != "000" ]; then
log "saudavel (spa=${spa} api=${api})"
return 0
fi
sleep 3
done
return 1
}
log "subindo ${CONTAINER_NAME}"
subir "${IMAGE}"
if saudavel; then
log "deploy de ${AMBIENTE} concluido em ${IMAGE}"
exit 0
fi
# ---------------------------------------------------------------------------
# 3. Nao respondeu. Um container no ar mas quebrado e pior do que a versao
# anterior no ar, entao volta.
# ---------------------------------------------------------------------------
echo "::error::deploy de ${AMBIENTE} falhou na verificacao de saude"
docker logs "${CONTAINER_NAME}" 2>&1 | tail -30 || true
if [ -z "${ANTERIOR}" ]; then
fail "sem versao anterior para voltar; container deixado no ar para diagnostico"
fi
log "revertendo para ${ANTERIOR}"
subir "${ANTERIOR}"
if saudavel; then
fail "deploy revertido: ${AMBIENTE} voltou para a versao anterior"
fi
fail "deploy falhou E a reversao tambem nao respondeu — ${AMBIENTE} precisa de atencao manual"