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