From f59be3adf9fbfcb0cc187045af8d8bb2fbc1920a Mon Sep 17 00:00:00 2001 From: grok Date: Sat, 26 Sep 2026 14:29:47 +0700 Subject: [PATCH] =?UTF-8?q?ci:=20PostgreSQL=2017=20=D1=81=20=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D1=8F=D0=BC=D0=B8=20=D0=BA=D0=B0=D0=BA=20=D0=B2=20pilot,?= =?UTF-8?q?=20roundtrip=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D0=B9?= =?UTF-8?q?,=20e2e,=20SHA-=D0=BF=D0=B8=D0=BD=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Зачем: прежний CI запускал миграции и тесты под POSTGRES_USER сервиса (суперпользователь), не проверял откат миграций, не запускал e2e на реальной БД, держал плавающие теги actions (F13) и выполнял docker build в контейнере без Docker daemon. - verify: bootstrap-админ сервиса только создаёт роли (provision-pg.mjs, пароли случайные); миграции up -> откат всех -> up -> up (no-op) ролью krona_migrator без SUPERUSER; `npm run check` с DATABASE_TEST_URL (app-роль) и DATABASE_TEST_MIGRATION_URL (очистка, см. N5); npm audit. - format: prettier вынесен в отдельную задачу — он красный по 25 файлам (F14) и не должен маскировать статус verify/e2e. - e2e: реальный сервер + PostgreSQL 17 через scripts/test/run-e2e.mjs. Выбран «храповик» known-defects.json, а не отдельная задача с continue-on-error: так CI зелёный, только если стенд работает и каждый красный сценарий — известный открытый дефект; новый красный сценарий (регрессия) или исправленный, но не вычеркнутый дефект делают задачу красной. continue-on-error скрыл бы и поломку самого стенда. - docker-build: отдельная задача на метке `docker-host` только для push, после verify; без такого раннера она ждёт в очереди, не блокируя остальные. - actions/checkout v4.4.0 и actions/setup-node v4.4.0 закреплены SHA (git ls-remote; одинаковы на github.com и code.forgejo.org). Проверено: actionlint 1.7.12 (метки docker/docker-host объявлены как self-hosted) — 0 замечаний; scripts/test/ci-local.sh повторяет шаги verify/format/e2e на локальной PostgreSQL 17.11 (Forgejo-раннер реально не запускался — не проверено). --- .forgejo/workflows/ci.yml | 98 +++++++++++++++++++++++++++++++++------ scripts/test/ci-local.sh | 30 ++++++++++++ 2 files changed, 113 insertions(+), 15 deletions(-) create mode 100755 scripts/test/ci-local.sh diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 1beabd2..a543a37 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -3,6 +3,16 @@ on: push: branches: [main, codex/bootstrap-platform-foundation] pull_request: + +# Все actions закреплены полным SHA коммита (F13); SHA получены `git ls-remote` +# и совпадают на github.com и code.forgejo.org (зеркало), поэтому закрепление +# работает при любом DEFAULT_ACTIONS_URL раннера. +# +# Роли БД как в pilot, но с минимальными привилегиями: POSTGRES_USER сервиса — +# только bootstrap-админ (роль DBA/entrypoint образа), им scripts/test/provision-pg.mjs +# создаёт krona_migrator (NOSUPERUSER, владелец БД) и krona_staff_app (NOBYPASSRLS) +# со случайными паролями. Ни миграции, ни тесты, ни сервер под bootstrap-админом не работают. + jobs: verify: runs-on: docker @@ -10,27 +20,85 @@ jobs: postgres: image: postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73 env: - POSTGRES_DB: krona_staff - POSTGRES_USER: krona_migrator - POSTGRES_PASSWORD: ci-migration-password - ports: ['5432:5432'] + POSTGRES_DB: postgres + POSTGRES_USER: ci_bootstrap + POSTGRES_PASSWORD: ci-bootstrap-only options: >- - --health-cmd "pg_isready -U krona_migrator -d krona_staff" + --health-cmd "pg_isready -U ci_bootstrap -d postgres" --health-interval 5s --health-timeout 3s --health-retries 20 env: - DATABASE_MIGRATION_URL: postgres://krona_migrator:ci-migration-password@postgres:5432/krona_staff - DATABASE_TEST_URL: postgres://krona_staff_app:ci-app-password@postgres:5432/krona_staff - POSTGRES_APP_PASSWORD: ci-app-password + E2E_PG_ADMIN_URL: postgres://ci_bootstrap:ci-bootstrap-only@postgres:5432/postgres steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: { node-version: 22, cache: npm } - run: npm ci - - run: node scripts/create-app-role.mjs - - run: node scripts/migrate.mjs up - - run: npm run check - - run: npm run format:check + - name: provision roles (migrator NOSUPERUSER, app NOBYPASSRLS) + run: node scripts/test/provision-pg.mjs --out "$RUNNER_TEMP/db.env" --db krona_staff + - name: migrations up -> rollback all -> up -> up (no-op) + run: | + set -a; . "$RUNNER_TEMP/db.env"; set +a + node scripts/test/migrate-roundtrip.mjs roundtrip + - name: npm run check (tenant-isolation on real PostgreSQL as app role) + run: | + set -a; . "$RUNNER_TEMP/db.env"; set +a + unset DATABASE_URL POSTGRES_APP_PASSWORD + npm run check - run: npm audit --audit-level=high - - run: docker build -t krona-staff:${{ forgejo.sha }} . + + # Отдельная задача: prettier сейчас красный по 25 файлам (F14), и это не должно + # маскировать статус verify/e2e. + format: + runs-on: docker + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: { node-version: 22, cache: npm } + - run: npm ci + - run: npm run format:check + + # Приёмочные сценарии на реальном сервере и PostgreSQL 17. Красные сценарии + # известных открытых дефектов перечислены в tests/e2e/known-defects.json: + # задача зелёная, если стенд работает ([HARNESS]/[CTRL] зелёные) и каждый + # красный сценарий есть в списке; новый красный сценарий или исправленный, + # но не вычеркнутый дефект делают задачу красной. + e2e: + runs-on: docker + services: + postgres: + image: postgres:17-alpine@sha256:18cfe3ef5e6815560c98237d6216d1e5119702fb0f3894c8785dd58b8bbe5d73 + env: + POSTGRES_DB: postgres + POSTGRES_USER: ci_bootstrap + POSTGRES_PASSWORD: ci-bootstrap-only + options: >- + --health-cmd "pg_isready -U ci_bootstrap -d postgres" + --health-interval 5s + --health-timeout 3s + --health-retries 20 + env: + E2E_PG_ADMIN_URL: postgres://ci_bootstrap:ci-bootstrap-only@postgres:5432/postgres + E2E_MIGRATIONS: roundtrip + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: { node-version: 22, cache: npm } + - run: npm ci + - name: e2e (known-defects ratchet) + run: node scripts/test/run-e2e.mjs + - name: e2e table + if: always() + run: cat tests/e2e/evidence/e2e-table.md || true + + # docker build требует Docker daemon, которого нет в контейнере метки `docker`. + # Нужен раннер с меткой `docker-host` (хост с доступом к Docker). Пока такого + # раннера нет, задача ждёт в очереди и не блокирует verify/format/e2e. + docker-build: + runs-on: docker-host + needs: [verify] + if: github.event_name == 'push' + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - run: docker build -t "krona-staff:${GITHUB_SHA}" . diff --git a/scripts/test/ci-local.sh b/scripts/test/ci-local.sh new file mode 100755 index 0000000..aec3f08 --- /dev/null +++ b/scripts/test/ci-local.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Локальный эквивалент задач verify/format/e2e из .forgejo/workflows/ci.yml +# (без Docker: PostgreSQL 17 уже запущен, E2E_PG_ADMIN_URL указывает на bootstrap-админа). +# docker-build локально не воспроизводится: Docker в этой среде нет. +set -uo pipefail +cd "$(dirname "$0")/../.." +: "${E2E_PG_ADMIN_URL:?E2E_PG_ADMIN_URL (bootstrap-админ PostgreSQL 17) обязателен}" +tmp="$(mktemp -d)" +trap 'rm -rf "$tmp"' EXIT +status=0 +step() { + local name="$1" + shift + echo "::: [$name] $*" + if "$@"; then echo "::: [$name] OK"; else local code=$?; echo "::: [$name] FAIL exit=$code"; status=1; fi +} +with_db_env() { (set -a; . "$tmp/db.env"; set +a; unset DATABASE_URL POSTGRES_APP_PASSWORD; "$@"); } + +echo "=== job verify" +step npm-ci npm ci --no-audit --no-fund +step provision node scripts/test/provision-pg.mjs --out "$tmp/db.env" --db krona_staff +step migrate-roundtrip with_db_env node scripts/test/migrate-roundtrip.mjs roundtrip +step check with_db_env npm run check +step audit npm audit --audit-level=high +echo "=== job format" +step format-check npm run format:check +echo "=== job e2e" +step e2e env E2E_MIGRATIONS=roundtrip node scripts/test/run-e2e.mjs +echo "=== итог: status=$status" +exit $status