#!/bin/bash # # Deploy de um ambiente do ConectaSUS V2. # # ./deploy.sh # # Le os parametros do ambiente de docker/deploy/.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 " [ -n "${TAG}" ] || fail "uso: $0 " 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"