#!/bin/bash # # Aplica migrations do ConectaSUS para uma lista explicita de municipios. # # ./migrate.sh # # A migration roda em um container descartavel da imagem informada. Assim ela # nao depende de qual versao esta no ar e usa os mesmos .env, # connections.json e storage montados no host do ambiente. set -euo pipefail AMBIENTE="${1:-}" TAG="${2:-}" MUNICIPIOS_CSV="${3:-}" REGISTRY="${REGISTRY:-192.168.0.41:3001}" IMAGE_NAME="${IMAGE_NAME:-rkm/conectasus-api}" AQUI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" log() { echo "[migrate] $*"; } fail() { echo "[migrate][ERRO] $*" >&2; exit 1; } [ -n "${AMBIENTE}" ] || fail "uso: $0 " [ -n "${TAG}" ] || fail "uso: $0 " [ -n "${MUNICIPIOS_CSV}" ] || fail "informe ao menos um municipio" ENV_FILE="${AQUI}/${AMBIENTE}.env" [ -f "${ENV_FILE}" ] || fail "ambiente desconhecido: ${AMBIENTE} (esperado ${ENV_FILE})" # shellcheck disable=SC1090 set -a; . "${ENV_FILE}"; set +a if grep -q 'TROCAR' "${ENV_FILE}"; then fail "${ENV_FILE} ainda tem valores de gabarito (TROCAR)." fi for v in CONFIG_DIR; do [ -n "${!v:-}" ] || fail "${v} nao definido em ${ENV_FILE}" done IMAGE="${REGISTRY}/${IMAGE_NAME}:${TAG}" # Nomes de conexao do Laravel nao devem conter espacos nem caracteres que # possam alterar a linha de comando. A lista e propositalmente explicita: # conexoes tecnicas (por exemplo, sigtap) nao devem receber migrations por # acidente. IFS=',' read -r -a MUNICIPIOS_BRUTOS <<< "${MUNICIPIOS_CSV}" declare -a MUNICIPIOS=() declare -A VISTOS=() for bruto in "${MUNICIPIOS_BRUTOS[@]}"; do municipio="$(echo "${bruto}" | tr -d '[:space:]')" [ -n "${municipio}" ] || fail "ha um municipio vazio na lista: ${MUNICIPIOS_CSV}" [[ "${municipio}" =~ ^[A-Za-z0-9_-]+$ ]] || fail "nome de municipio invalido: ${municipio}" [ -z "${VISTOS[${municipio}]:-}" ] || fail "municipio repetido na lista: ${municipio}" VISTOS["${municipio}"]=1 MUNICIPIOS+=("${municipio}") done log "ambiente : ${AMBIENTE}" log "imagem : ${IMAGE}" log "municipios: ${MUNICIPIOS[*]}" log "puxando ${IMAGE}" docker pull "${IMAGE}" >/dev/null || fail "pull falhou (registry acessivel? credencial presente?)" # --mount valida as fontes no host do Docker. Isso e essencial quando o job # roda em container do act_runner, cujo filesystem nao contem CONFIG_DIR. MOUNTS=( --mount "type=bind,src=${CONFIG_DIR}/.env,dst=/var/www/api/.env,readonly" --mount "type=bind,src=${CONFIG_DIR}/connections.json,dst=/var/www/api/connections.json,readonly" --mount "type=bind,src=${CONFIG_DIR}/storage,dst=/var/www/api/storage" ) PHP_RUN=(docker run --rm "${MOUNTS[@]}" --workdir /var/www/api --entrypoint php "${IMAGE}") # Confere o JSON e todas as conexoes antes de alterar qualquer banco. Nao lista # nem imprime o conteudo do arquivo, que pode conter credenciais. for municipio in "${MUNICIPIOS[@]}"; do if ! "${PHP_RUN[@]}" -r ' $connections = json_decode(file_get_contents("connections.json"), true); if (!is_array($connections) || !array_key_exists($argv[1], $connections)) { fwrite(STDERR, "conexao nao encontrada no connections.json: " . $argv[1] . PHP_EOL); exit(1); } ' "${municipio}"; then fail "validacao da conexao falhou; nenhum banco recebeu migration" fi done MIGRATE_RUN=("${PHP_RUN[@]}") if [ -n "${NETWORK:-}" ]; then MIGRATE_RUN=(docker run --rm --network "${NETWORK}" "${MOUNTS[@]}" --workdir /var/www/api --entrypoint php "${IMAGE}") fi total="${#MUNICIPIOS[@]}" for i in "${!MUNICIPIOS[@]}"; do municipio="${MUNICIPIOS[${i}]}" log "[$((i + 1))/${total}] aplicando migrations em ${municipio}" "${MIGRATE_RUN[@]}" artisan migrate --database="${municipio}" --force done log "migrations concluidas para ${total} municipio(s)"