fix: close BUG-022..027, BUG-029, BUG-031, BUG-033 from QA sweep #100
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Quality Gate | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| pull_request: | |
| jobs: | |
| # BUG-023: build and lint the React frontend on every PR. Without this job, | |
| # TypeScript errors, unresolved imports, and broken components can merge | |
| # to main with all other checks green. | |
| frontend-build: | |
| name: frontend (build + lint) | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Lint | |
| run: npm run lint | |
| - name: Build | |
| # vite build performs the full production build; nitro then type-checks | |
| # the SSR bundle. Any TypeScript error, missing import, or broken route | |
| # tree fails this step. | |
| run: npm run build | |
| # Fast, deterministic checks — no database, no network beyond pip. | |
| free-tier: | |
| name: free-tier (unit, lint, health, evals-p) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dev dependencies | |
| # api/requirements.txt is needed too: tests/test_api.py imports FastAPI | |
| # at module level, so without it pytest collection fails. | |
| run: pip install -r requirements-dev.txt -r api/requirements.txt | |
| - name: Unit / regression / security / snapshot tests — final result | |
| run: | | |
| python3 scripts/test_report.py \ | |
| --markers "unit or regression or security or snapshot" --strict | |
| - name: Lint (flake8 + bandit) | |
| run: make lint | |
| - name: Component health check | |
| run: make health | |
| - name: Evals — Tier P (offline validator scenarios) | |
| run: python3 evals/runner.py --tiers p --verbose | |
| # Reports (incl. the VCRM gap report) are written to evals/reports/ and | |
| # would otherwise vanish with the runner — keep them for auditability. | |
| - name: Upload eval reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: eval-reports-free-tier | |
| path: evals/reports/ | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| # Full integration surface: Tier I deploys env_dev.sql twice (idempotency), | |
| # Tier S runs the 142-assertion SQL suite, then the PG-gated pytest tiers run. | |
| integration-postgres: | |
| name: integration (postgres service) | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_PASSWORD: postgres | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U postgres" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 10 | |
| env: | |
| PGHOST: localhost | |
| PGPORT: "5432" | |
| PGUSER: postgres | |
| PGPASSWORD: postgres | |
| PGDATABASE: te_mgmt_dev | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dev dependencies | |
| # api/requirements.txt is needed too: tests/test_api.py imports FastAPI | |
| # at module level, so without it pytest collection fails. | |
| run: pip install -r requirements-dev.txt -r api/requirements.txt | |
| - name: Install PostgreSQL client | |
| run: sudo apt-get update && sudo apt-get install -y --no-install-recommends postgresql-client | |
| - name: Write CI database config | |
| run: | | |
| cat > build/config.local.env <<'EOF' | |
| DB_ENGINE="postgresql" | |
| PG_HOST="localhost" | |
| PG_PORT="5432" | |
| PG_SUPERUSER="postgres" | |
| PG_SUPERUSER_PASSWORD="postgres" | |
| PG_DB_DEV="te_mgmt_dev" | |
| PG_SCHEMA_DEV="te_dev" | |
| PG_DB_TEST="te_mgmt_test" | |
| PG_SCHEMA_TEST="te_test" | |
| PG_DB_STAGING="te_mgmt_staging" | |
| PG_SCHEMA_STAGING="te_staging" | |
| PG_DB_PROD="te_mgmt_prod" | |
| PG_SCHEMA_PROD="te_prod" | |
| EOF | |
| # env_*.sql scripts do not create their databases — deploy_all.sh does that | |
| # before invoking them (see build/te_core_schema.sql:41). Mirror it here. | |
| # Only *.example.sql are committed (concrete env_<env>.sql are gitignored), | |
| # so materialise them before anything that deploys an environment. | |
| - name: Materialise environment launchers from templates | |
| run: | | |
| for env in dev test staging prod; do | |
| cp "build/environments/env_${env}.example.sql" \ | |
| "build/environments/env_${env}.sql" | |
| done | |
| ls -1 build/environments/ | |
| - name: Create environment databases | |
| run: | | |
| for db in te_mgmt_dev te_mgmt_test te_mgmt_staging te_mgmt_prod; do | |
| if [ -z "$(psql -d postgres -tA -c "SELECT 1 FROM pg_database WHERE datname = '$db'")" ]; then | |
| psql -v ON_ERROR_STOP=1 -d postgres -c \ | |
| "CREATE DATABASE \"$db\" WITH OWNER = postgres ENCODING = 'UTF8' TEMPLATE = template0 CONNECTION LIMIT = -1" | |
| fi | |
| done | |
| - name: Deploy all environments (for cross-env parity) | |
| run: | | |
| for env in dev test staging prod; do | |
| bash build/deploy_all.sh "$env" | |
| done | |
| - name: Evals — Tiers X, E (post-deploy, before test artifacts) | |
| run: python3 evals/runner.py --tiers x,e --verbose | |
| - name: Evals — Tiers P, I, S | |
| run: python3 evals/runner.py --tiers p,i,s --verbose | |
| # Prints a final block accounting for every test: PASSED / FAILED / | |
| # ERROR / SKIPPED (with reasons) / NOT RUN (deselected). --strict fails | |
| # the build on any skip. | |
| - name: Full test suite — final result with skip accounting | |
| run: python3 scripts/test_report.py --strict | |
| - name: Upload eval reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: eval-reports-integration | |
| path: evals/reports/ | |
| if-no-files-found: ignore | |
| retention-days: 30 | |
| # Windows surface: starts the pre-installed PostgreSQL service, provisions | |
| # databases, and runs the full suite + Tier P evals. Proves the pipeline | |
| # works on Windows and closes GAP_ANALYSIS.md G2. | |
| windows-postgres: | |
| name: windows (postgres, full suite) | |
| runs-on: windows-latest | |
| env: | |
| PGHOST: localhost | |
| PGPORT: "5432" | |
| PGUSER: postgres | |
| PGPASSWORD: postgres | |
| PGDATABASE: te_mgmt_dev | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dev dependencies | |
| run: pip install -r requirements-dev.txt -r api/requirements.txt | |
| - name: Start PostgreSQL and verify connection | |
| shell: pwsh | |
| run: | | |
| # Locate the PostgreSQL bin directory | |
| $pgDir = (Get-ItemProperty 'HKLM:\SOFTWARE\PostgreSQL\Installations\*' -ErrorAction SilentlyContinue | | |
| Select-Object -First 1).Base_Directory | |
| if (-not $pgDir) { | |
| $pgDir = (Get-ChildItem "C:\Program Files\PostgreSQL\*\bin\psql.exe" -ErrorAction SilentlyContinue | | |
| Select-Object -First 1).Directory.Parent.FullName | |
| } | |
| if (-not $pgDir) { | |
| Write-Error "No PostgreSQL installation found on this runner" | |
| exit 1 | |
| } | |
| $binDir = Join-Path $pgDir "bin" | |
| Write-Host "PostgreSQL bin: $binDir" | |
| echo "$binDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| # Use a writable temp directory for the data cluster — Program Files | |
| # is not writable by the runner user. | |
| $dataDir = Join-Path $env:RUNNER_TEMP "pgdata" | |
| Write-Host "Data directory: $dataDir" | |
| # Initialise a fresh cluster owned by the current user | |
| if (-not (Test-Path (Join-Path $dataDir "PG_VERSION"))) { | |
| Write-Host "Running initdb" | |
| & "$binDir\initdb" -U postgres -D $dataDir --encoding=UTF8 --auth=trust | |
| if ($LASTEXITCODE -ne 0) { | |
| Write-Error "initdb failed"; exit 1 | |
| } | |
| } | |
| # Start the server directly with pg_ctl (no Windows service needed) | |
| $logFile = Join-Path $env:RUNNER_TEMP "pg.log" | |
| & "$binDir\pg_ctl" -D $dataDir -l $logFile -o "-p 5432" start | |
| Start-Sleep -Seconds 5 | |
| # Verify it is running | |
| & "$binDir\pg_isready" -p 5432 | |
| if ($LASTEXITCODE -ne 0) { | |
| Get-Content $logFile -Tail 30 | |
| Write-Error "PostgreSQL did not start"; exit 1 | |
| } | |
| # Set password and switch to md5 auth | |
| & "$binDir\psql" -U postgres -d postgres -p 5432 -c "ALTER USER postgres PASSWORD 'postgres';" | |
| $hbaPath = Join-Path $dataDir "pg_hba.conf" | |
| (Get-Content $hbaPath) -replace 'trust$','md5' | Set-Content $hbaPath | |
| & "$binDir\pg_ctl" -D $dataDir reload | |
| Start-Sleep -Seconds 2 | |
| & "$binDir\psql" -U postgres -d postgres -p 5432 -c "SELECT version();" | |
| - name: Write CI database config | |
| shell: pwsh | |
| run: | | |
| @" | |
| DB_ENGINE="postgresql" | |
| PG_HOST="localhost" | |
| PG_PORT="5432" | |
| PG_SUPERUSER="postgres" | |
| PG_SUPERUSER_PASSWORD="postgres" | |
| PG_DB_DEV="te_mgmt_dev" | |
| PG_SCHEMA_DEV="te_dev" | |
| PG_DB_TEST="te_mgmt_test" | |
| PG_SCHEMA_TEST="te_test" | |
| PG_DB_STAGING="te_mgmt_staging" | |
| PG_SCHEMA_STAGING="te_staging" | |
| PG_DB_PROD="te_mgmt_prod" | |
| PG_SCHEMA_PROD="te_prod" | |
| "@ | Out-File -FilePath "build/config.local.env" -Encoding utf8 | |
| - name: Materialise environment launchers from templates | |
| shell: pwsh | |
| run: | | |
| foreach ($env_name in @("dev", "test", "staging", "prod")) { | |
| Copy-Item "build/environments/env_${env_name}.example.sql" ` | |
| "build/environments/env_${env_name}.sql" | |
| } | |
| Get-ChildItem build/environments/ | |
| - name: Create environment databases | |
| shell: pwsh | |
| run: | | |
| foreach ($db in @("te_mgmt_dev", "te_mgmt_test", "te_mgmt_staging", "te_mgmt_prod")) { | |
| $exists = & psql -U postgres -d postgres -tA -c "SELECT 1 FROM pg_database WHERE datname = '$db'" | |
| if (-not $exists) { | |
| & psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c ` | |
| "CREATE DATABASE `"$db`" WITH OWNER = postgres ENCODING = 'UTF8' TEMPLATE = template0 CONNECTION LIMIT = -1" | |
| } | |
| } | |
| - name: Deploy all environments (for cross-env parity) | |
| shell: bash | |
| run: | | |
| for env in dev test staging prod; do | |
| bash build/deploy_all.sh "$env" | |
| done | |
| - name: Evals — Tiers X, E (post-deploy, before test artifacts) | |
| run: python3 evals/runner.py --tiers x,e --verbose | |
| - name: Evals — Tier P (offline validator scenarios) | |
| run: python3 evals/runner.py --tiers p --verbose | |
| - name: Full test suite — final result with skip accounting | |
| run: python3 scripts/test_report.py --strict | |
| - name: Upload eval reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: eval-reports-windows | |
| path: evals/reports/ | |
| if-no-files-found: ignore | |
| retention-days: 30 | |