fix(ci): ждать настоящий Postgres, а не временный сервер фазы initdb
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Successful in 52s
CI Trade-In / frontend-checks (pull_request) Successful in 1m38s
CI / frontend-tests (pull_request) Successful in 1m44s
CI / openapi-codegen-check (pull_request) Successful in 2m20s
CI Trade-In / backend-tests (pull_request) Successful in 4m31s
CI / backend-tests (pull_request) Successful in 15m35s
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Successful in 52s
CI Trade-In / frontend-checks (pull_request) Successful in 1m38s
CI / frontend-tests (pull_request) Successful in 1m44s
CI / openapi-codegen-check (pull_request) Successful in 2m20s
CI Trade-In / backend-tests (pull_request) Successful in 4m31s
CI / backend-tests (pull_request) Successful in 15m35s
Прогон 6859: контейнер поднялся, `pg_isready` сказал «accepting connections», через 1.4с bootstrap упал на `container is not running`. Причина — проба шла через unix-сокет, а по сокету отвечает ВРЕМЕННЫЙ сервер, который образ postgres поднимает на время initdb с listen_addresses=''. Снаружи БД в этот момент ещё не существует, и впереди рестарт: мы поймали окно и приняли его за готовность. Классическая ложная зелень — проверка сказала «готово» про не то. Теперь проба `pg_isready -h 127.0.0.1` (TCP) — зеленеет только на настоящем сервере, том самом, к которому пойдут тесты. Плюс: цикл ждёт до 90с и прерывается, если контейнер вышел; при неудаче печатается статус, код выхода и `docker logs`. Подъём и bootstrap слиты в ОДИН шаг — между шагами контейнер успевал исчезнуть.
This commit is contained in:
parent
a07da68a37
commit
cfde033c2f
2 changed files with 63 additions and 37 deletions
|
|
@ -86,57 +86,69 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Поднять Postgres для тестов
|
||||
- name: Поднять Postgres и собрать схему tradein
|
||||
working-directory: .
|
||||
# ПОЧЕМУ НЕ `services:` И ПОЧЕМУ БЕЗ ПУБЛИКАЦИИ ПОРТА.
|
||||
# Раннер запускает и job, и сервис-контейнеры с `--network host` (видно в
|
||||
# логе прогона: `docker create image=... network="host"`), а на этом же
|
||||
# хосте на 5432 уже слушает ПРОДОВЫЙ Postgres. Первая попытка через
|
||||
# `services:` + `ports: 5432:5432` (прогон #6853) кончилась тем, что
|
||||
# сервис-контейнер не смог занять порт, а psql из job'а ушёл В ПРОД и
|
||||
# получил `password authentication failed for user "tradein"`. То есть на
|
||||
# этом раннере `localhost:5432` — это боевая база, а не тестовая.
|
||||
# логе прогона: `docker create image=... network="host"`), а на 5432 того
|
||||
# же хоста слушает ПРОДОВЫЙ Postgres. Попытка через `services:` +
|
||||
# `ports: 5432:5432` кончилась тем, что сервис-контейнер не смог занять
|
||||
# порт, а psql из job'а ушёл В ПРОД и получил
|
||||
# `password authentication failed for user "tradein"`. То есть
|
||||
# `localhost:5432` из job'а на этом раннере — боевая база, а не тестовая.
|
||||
# Поэтому контейнер поднимаем сами, в bridge-сети, БЕЗ публикации порта,
|
||||
# и ходим по его собственному IP: прод недостижим в принципе, параллельные
|
||||
# прогоны не конфликтуют, psql берём из самого контейнера.
|
||||
#
|
||||
# Поэтому: контейнер поднимаем сами, в дефолтной bridge-сети, БЕЗ
|
||||
# публикации порта, и ходим по его собственному IP. Ноль шансов задеть
|
||||
# прод, ноль конфликтов между параллельными прогонами и никакого
|
||||
# postgresql-client на раннере — psql берём из самого контейнера.
|
||||
# ОДИН шаг, а не два: между шагами контейнер успевал исчезнуть, и
|
||||
# bootstrap падал на `container is not running`.
|
||||
#
|
||||
# `pg_isready -h 127.0.0.1`, а НЕ через unix-сокет: на время initdb образ
|
||||
# поднимает ВРЕМЕННЫЙ сервер с listen_addresses='' — по сокету он уже
|
||||
# отвечает «accepting connections», хотя снаружи БД ещё не существует, а
|
||||
# впереди рестарт. Проба по TCP зеленеет только на настоящем сервере —
|
||||
# том самом, к которому пойдут тесты.
|
||||
#
|
||||
# postgis, не plain postgres: tests/tasks/test_cadastral_geo_match.py
|
||||
# проверяет KNN по geometry (PostGIS_Version() в connectivity-probe).
|
||||
#
|
||||
# Имя БД ОБЯЗАНО отличаться от `test`: `_live_session()` считает DSN с
|
||||
# `localhost:5432/test` заглушкой и возвращает None — иначе контейнер
|
||||
# поднялся бы, а тесты всё равно скипались. Пароль — одноразовый,
|
||||
# CI-локальный, контейнер живёт минуты и наружу не смотрит.
|
||||
# `localhost:5432/test` заглушкой и вернул бы None — контейнер поднялся
|
||||
# бы, а тесты всё равно скипались.
|
||||
run: |
|
||||
set -u
|
||||
docker rm -f "$CI_PG" >/dev/null 2>&1 || true
|
||||
docker run -d --name "$CI_PG" \
|
||||
-e POSTGRES_DB=tradein -e POSTGRES_USER=tradein -e POSTGRES_PASSWORD=tradein \
|
||||
postgis/postgis:16-3.4
|
||||
for _ in $(seq 1 30); do
|
||||
docker exec "$CI_PG" pg_isready -U tradein -q && break
|
||||
|
||||
ready=""
|
||||
for _ in $(seq 1 45); do
|
||||
if docker exec "$CI_PG" pg_isready -h 127.0.0.1 -U tradein -q 2>/dev/null; then
|
||||
ready=1; break
|
||||
fi
|
||||
[ "$(docker inspect -f '{{.State.Status}}' "$CI_PG" 2>/dev/null)" = "running" ] || break
|
||||
sleep 2
|
||||
done
|
||||
docker exec "$CI_PG" pg_isready -U tradein \
|
||||
|| { echo "::error::Postgres не поднялся за 60с"; docker logs "$CI_PG"; exit 1; }
|
||||
if [ -z "$ready" ]; then
|
||||
echo "::error::Postgres не поднялся; статус=$(docker inspect -f '{{.State.Status}} exit={{.State.ExitCode}}' "$CI_PG" 2>&1)"
|
||||
docker logs --tail 50 "$CI_PG" 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$CI_PG")
|
||||
[ -n "$ip" ] || { echo "::error::не удалось узнать IP контейнера $CI_PG"; exit 1; }
|
||||
echo "DATABASE_URL=postgresql+psycopg://tradein:tradein@${ip}:5432/tradein" >> "$GITHUB_ENV"
|
||||
echo "✓ Postgres на ${ip}:5432 (контейнер $CI_PG)"
|
||||
|
||||
- name: Bootstrap схемы tradein (backend/data/sql/*.sql)
|
||||
working-directory: ./tradein-mvp
|
||||
# Тот же порядок и тот же строгий режим, что в deploy-tradein.yml:
|
||||
# `ls | sort` + ON_ERROR_STOP=on, падение любой миграции → job RED.
|
||||
# Никаких «применилось как получилось»: схема в CI либо та же, что на
|
||||
# проде, либо гейта нет.
|
||||
run: |
|
||||
# Тот же порядок и тот же строгий режим, что в deploy-tradein.yml:
|
||||
# `ls | sort` + ON_ERROR_STOP=on, падение любой миграции → job RED.
|
||||
# Никаких «применилось как получилось»: схема в CI либо та же, что на
|
||||
# проде, либо гейта нет.
|
||||
docker exec -i "$CI_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -q -c \
|
||||
"CREATE EXTENSION IF NOT EXISTS postgis;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
CREATE ROLE gendesign_reader;"
|
||||
for sql_file in $(ls -1 backend/data/sql/*.sql | sort); do
|
||||
for sql_file in $(ls -1 tradein-mvp/backend/data/sql/*.sql | sort); do
|
||||
fname=$(basename "$sql_file")
|
||||
# ЕДИНСТВЕННОЕ исключение, и оно названо вслух: 077 — не DDL, а
|
||||
# backfill, читающий foreign table gendesign_rosreestr_deals из БД
|
||||
|
|
@ -147,7 +159,7 @@ jobs:
|
|||
continue
|
||||
fi
|
||||
docker exec -i "$CI_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -q < "$sql_file" \
|
||||
|| { echo "::error::миграция $fname не применилась"; exit 1; }
|
||||
|| { echo "::error::миграция $fname не применилась"; docker logs --tail 20 "$CI_PG" 2>&1 || true; exit 1; }
|
||||
done
|
||||
echo "✓ схема собрана: $(docker exec "$CI_PG" psql -U tradein -d tradein -tAc \
|
||||
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public'") таблиц"
|
||||
|
|
|
|||
|
|
@ -98,26 +98,40 @@ jobs:
|
|||
|
||||
- name: Поднять Postgres для тестов
|
||||
working-directory: .
|
||||
# ПОЧЕМУ НЕ `services:` И ПОЧЕМУ БЕЗ ПУБЛИКАЦИИ ПОРТА — см. подробный
|
||||
# разбор в ci-tradein.yml (тот же раннер). Кратко: job и сервис-контейнеры
|
||||
# запускаются с `--network host`, а на 5432 этого хоста слушает ПРОДОВЫЙ
|
||||
# Postgres, так что `localhost:5432` из job'а — боевая база. Поднимаем
|
||||
# контейнер сами, в bridge-сети, без публикации порта, ходим по его IP.
|
||||
# ПОЧЕМУ НЕ `services:` И ПОЧЕМУ БЕЗ ПУБЛИКАЦИИ ПОРТА — подробный разбор в
|
||||
# ci-tradein.yml (тот же раннер). Кратко: job и сервис-контейнеры идут с
|
||||
# `--network host`, а на 5432 этого хоста слушает ПРОДОВЫЙ Postgres, то
|
||||
# есть `localhost:5432` из job'а — боевая база. Поднимаем контейнер сами,
|
||||
# в bridge-сети, без публикации порта, ходим по его IP.
|
||||
#
|
||||
# `pg_isready -h 127.0.0.1`, а не через unix-сокет: по сокету отвечает
|
||||
# ВРЕМЕННЫЙ сервер фазы initdb (listen_addresses=''), после которой БД
|
||||
# ещё перезапускается. Проба по TCP зеленеет только на настоящем сервере.
|
||||
#
|
||||
# plain postgres:16, БЕЗ PostGIS: тесты tests/sql/ строят себе временные
|
||||
# таблицы и не трогают ни geometry, ни реальную схему — bootstrap схемы
|
||||
# здесь не нужен вовсе, в отличие от tradein-лэйна.
|
||||
run: |
|
||||
set -u
|
||||
docker rm -f "$CI_PG" >/dev/null 2>&1 || true
|
||||
docker run -d --name "$CI_PG" \
|
||||
-e POSTGRES_DB=gendesign_ci -e POSTGRES_USER=gendesign -e POSTGRES_PASSWORD=gendesign \
|
||||
postgres:16
|
||||
for _ in $(seq 1 30); do
|
||||
docker exec "$CI_PG" pg_isready -U gendesign -q && break
|
||||
|
||||
ready=""
|
||||
for _ in $(seq 1 45); do
|
||||
if docker exec "$CI_PG" pg_isready -h 127.0.0.1 -U gendesign -q 2>/dev/null; then
|
||||
ready=1; break
|
||||
fi
|
||||
[ "$(docker inspect -f '{{.State.Status}}' "$CI_PG" 2>/dev/null)" = "running" ] || break
|
||||
sleep 2
|
||||
done
|
||||
docker exec "$CI_PG" pg_isready -U gendesign \
|
||||
|| { echo "::error::Postgres не поднялся за 60с"; docker logs "$CI_PG"; exit 1; }
|
||||
if [ -z "$ready" ]; then
|
||||
echo "::error::Postgres не поднялся; статус=$(docker inspect -f '{{.State.Status}} exit={{.State.ExitCode}}' "$CI_PG" 2>&1)"
|
||||
docker logs --tail 50 "$CI_PG" 2>&1 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$CI_PG")
|
||||
[ -n "$ip" ] || { echo "::error::не удалось узнать IP контейнера $CI_PG"; exit 1; }
|
||||
echo "DATABASE_URL=postgresql+psycopg://gendesign:gendesign@${ip}:5432/gendesign_ci" >> "$GITHUB_ENV"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue