mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
* fix(cloud): bootstrap Postgres nativo no Cursor Cloud VMs Cloud não têm Docker (sem daemon/systemd; overlayfs falha em docker compose). Substitui compose por ensure-postgres.sh (PG 16 via apt, pg_ctlcluster) e corrige placeholders do .env (BLOB token no formato vercel_blob_rw_* exigido pelo adapter). Testado: cloud-setup.sh completa migrate + seed em teqo e teqo_test. * fix(ci): define GH_REPO no job migration-lock gh pr list falha sem checkout quando não há repositório git no runner (fatal: not a git repository). GH_REPO aponta explicitamente para o repo. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
339 lines
10 KiB
YAML
339 lines
10 KiB
YAML
# PR gate: feature → stage (and stage → main promote PRs).
|
|
# Parallel jobs with `needs` only where a real dependency exists (Postgres for
|
|
# int/build/e2e). Build runs here (local service DB only) and NEVER in
|
|
# ci-stage.yml — `pnpm build` applies migrations to whatever DATABASE_URL it
|
|
# sees, so it is forbidden anywhere near stage/prod URLs.
|
|
#
|
|
# Test selection (OPS5): unit/int run `vitest --changed origin/$GITHUB_BASE_REF`
|
|
# and e2e runs the manifest-selected specs; a diff touching a high-risk path
|
|
# (schema, lockfile, test harness, seed manifest — see
|
|
# scripts/lib/test-affected-core.mjs) falls back to the FULL suites. Push to
|
|
# stage/main always runs everything (ci-stage.yml / ci.yml).
|
|
name: CI (PR)
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [stage, main]
|
|
|
|
concurrency:
|
|
group: ci-pr-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
DATABASE_URL: postgresql://teqo:teqo@localhost:5432/teqo_test
|
|
PAYLOAD_SECRET: test-only-secret-not-used-in-production
|
|
NEXT_PUBLIC_SITE_URL: http://localhost:3000
|
|
|
|
jobs:
|
|
lint:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm lint
|
|
|
|
format:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm format:check
|
|
|
|
typecheck:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm typecheck
|
|
|
|
knip:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm exec knip
|
|
|
|
cycles:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm check:cycles
|
|
|
|
unit:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
- name: Compute affected test scope
|
|
id: scope
|
|
env:
|
|
GITHUB_BASE_REF: ${{ github.base_ref }}
|
|
run: |
|
|
result=$(node scripts/test-affected.mjs)
|
|
echo "scope: $(echo "$result" | jq -r .reason)"
|
|
echo "mode=$(echo "$result" | jq -r .mode)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Unit tests (full suite)
|
|
if: steps.scope.outputs.mode == 'full'
|
|
run: pnpm test:unit
|
|
|
|
- name: Unit tests (changed since PR base)
|
|
if: steps.scope.outputs.mode == 'changed'
|
|
run: pnpm test:unit -- --changed origin/${{ github.base_ref }} --passWithNoTests
|
|
|
|
- name: Unit tests skipped
|
|
if: steps.scope.outputs.mode == 'none'
|
|
run: echo "No src/ or tests/ changes in this PR — nothing to run."
|
|
|
|
int:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
services:
|
|
postgres:
|
|
# Matches production Neon major (17) and docker-compose.yml.
|
|
image: postgres:17-alpine
|
|
env:
|
|
POSTGRES_USER: teqo
|
|
POSTGRES_PASSWORD: teqo
|
|
POSTGRES_DB: teqo_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd "pg_isready -U teqo"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
- name: Compute affected test scope
|
|
id: scope
|
|
env:
|
|
GITHUB_BASE_REF: ${{ github.base_ref }}
|
|
run: |
|
|
result=$(node scripts/test-affected.mjs)
|
|
echo "scope: $(echo "$result" | jq -r .reason)"
|
|
echo "mode=$(echo "$result" | jq -r .mode)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Migrate test database
|
|
if: steps.scope.outputs.mode != 'none'
|
|
run: pnpm migrate
|
|
|
|
- name: Seed minimal test database
|
|
if: steps.scope.outputs.mode != 'none'
|
|
run: pnpm db:seed:minimal
|
|
|
|
- name: Integration tests (full suite)
|
|
if: steps.scope.outputs.mode == 'full'
|
|
run: pnpm test:int
|
|
|
|
- name: Integration tests (changed since PR base)
|
|
if: steps.scope.outputs.mode == 'changed'
|
|
run: pnpm test:int -- --changed origin/${{ github.base_ref }} --passWithNoTests
|
|
|
|
- name: Integration tests skipped
|
|
if: steps.scope.outputs.mode == 'none'
|
|
run: echo "No src/ or tests/ changes in this PR — nothing to run."
|
|
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 25
|
|
services:
|
|
postgres:
|
|
image: postgres:17-alpine
|
|
env:
|
|
POSTGRES_USER: teqo
|
|
POSTGRES_PASSWORD: teqo
|
|
POSTGRES_DB: teqo_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd "pg_isready -U teqo"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build (against the local service database)
|
|
run: pnpm build
|
|
|
|
e2e:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
services:
|
|
postgres:
|
|
image: postgres:17-alpine
|
|
env:
|
|
POSTGRES_USER: teqo
|
|
POSTGRES_PASSWORD: teqo
|
|
POSTGRES_DB: teqo_test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd "pg_isready -U teqo"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
|
|
- name: Install Playwright chromium
|
|
run: pnpm exec playwright install --with-deps chromium
|
|
|
|
- name: Compute affected e2e scope
|
|
id: scope
|
|
env:
|
|
GITHUB_BASE_REF: ${{ github.base_ref }}
|
|
run: |
|
|
result=$(node scripts/e2e-affected.mjs)
|
|
echo "scope: $(echo "$result" | jq -r .reason)"
|
|
echo "mode=$(echo "$result" | jq -r .mode)" >> "$GITHUB_OUTPUT"
|
|
echo "specs=$(echo "$result" | jq -r '[.specs[] | "tests/e2e/" + . + ".e2e.spec.ts"] | join(" ")')" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Migrate test database
|
|
if: steps.scope.outputs.mode != 'none'
|
|
run: pnpm migrate
|
|
|
|
- name: Seed minimal test database
|
|
if: steps.scope.outputs.mode != 'none'
|
|
run: pnpm db:seed:minimal
|
|
|
|
# CI e2e runs against the production build (see playwright.config.ts
|
|
# webServer.command); the dist dir must match the webServer env.
|
|
- name: Build for e2e
|
|
if: steps.scope.outputs.mode != 'none'
|
|
run: pnpm build
|
|
env:
|
|
NEXT_DIST_DIR: .next/e2e
|
|
|
|
- name: E2E tests (full suite)
|
|
if: steps.scope.outputs.mode == 'full'
|
|
run: pnpm test:e2e
|
|
|
|
- name: E2E tests (manifest-selected specs)
|
|
if: steps.scope.outputs.mode == 'selected'
|
|
run: pnpm test:e2e -- ${{ steps.scope.outputs.specs }}
|
|
|
|
- name: E2E tests skipped
|
|
if: steps.scope.outputs.mode == 'none'
|
|
run: echo "No e2e-relevant changes in this PR — nothing to run."
|
|
|
|
# Branch protection on `stage`/`main` still requires the legacy context name
|
|
# `checks` from the pre-parallel monolithic job. This rollup satisfies that
|
|
# contract without re-serializing the suite.
|
|
checks:
|
|
name: checks
|
|
runs-on: ubuntu-latest
|
|
needs:
|
|
- lint
|
|
- format
|
|
- typecheck
|
|
- knip
|
|
- cycles
|
|
- unit
|
|
- int
|
|
- build
|
|
- e2e
|
|
- migration-lock
|
|
if: always()
|
|
steps:
|
|
- name: All parallel checks green
|
|
env:
|
|
NEEDS: ${{ toJSON(needs) }}
|
|
run: |
|
|
set -euo pipefail
|
|
echo "$NEEDS" | jq -e '
|
|
to_entries
|
|
| all(.value.result == "success" or .value.result == "skipped")
|
|
'
|
|
|
|
migration-lock:
|
|
# At most ONE open PR may touch schema (migrations or generated types).
|
|
# Two in flight guarantees a broken rebase and a poisoned drizzle snapshot
|
|
# chain; the second PR must wait or rebase.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
# gh pr list against the default GITHUB_TOKEN.
|
|
contents: read
|
|
pull-requests: read
|
|
steps:
|
|
# gh expands --json files via git, so it needs the repo checked out.
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Count open PRs touching schema files
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GH_REPO: ${{ github.repository }}
|
|
run: |
|
|
set -euo pipefail
|
|
count=$(gh pr list --state open --limit 100 --json number,files \
|
|
--jq '[.[] | select(.files != null and ([.files[].path | test("^(src/migrations/|src/payload-types.ts$|payload-types.ts$)")] | any))] | length')
|
|
echo "Open schema-touching PRs: $count"
|
|
if [ "$count" -gt 1 ]; then
|
|
echo "::error::More than one open PR touches src/migrations/ or payload-types.ts. Merge or close the other first."
|
|
exit 1
|
|
fi
|