-
Notifications
You must be signed in to change notification settings - Fork 0
ci(contracts): contracts/06 — add Contract Tests pipeline #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,113 @@ | ||||||||||||||||||||||
| # Contract tests — roda em todo PR que toca contratos | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # 2 jobs: | ||||||||||||||||||||||
| # - unit: vitest contra schemas Zod (sem rede, rápido) | ||||||||||||||||||||||
| # - smoke: supabase functions serve + script HTTP real (precisa do repo, não | ||||||||||||||||||||||
| # roda em forks porque depende de secrets do próprio repositório) | ||||||||||||||||||||||
| # | ||||||||||||||||||||||
| # Política de bloqueio: unit é obrigatório (status check), smoke é advisory | ||||||||||||||||||||||
| # até estabilizar (~2 sprints), depois vira required. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| name: Contract Tests | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| on: | ||||||||||||||||||||||
| pull_request: | ||||||||||||||||||||||
| paths: | ||||||||||||||||||||||
| - 'supabase/functions/_shared/contracts/**' | ||||||||||||||||||||||
| - 'supabase/functions/**/index.ts' | ||||||||||||||||||||||
| - 'tests/contracts/**' | ||||||||||||||||||||||
| - 'scripts/contract-testing.mjs' | ||||||||||||||||||||||
| - 'vitest.config.ts' | ||||||||||||||||||||||
| - 'package.json' | ||||||||||||||||||||||
| - 'package-lock.json' | ||||||||||||||||||||||
| - '.github/workflows/contract-tests.yml' | ||||||||||||||||||||||
| workflow_dispatch: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| permissions: | ||||||||||||||||||||||
| contents: read | ||||||||||||||||||||||
| pull-requests: read | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| concurrency: | ||||||||||||||||||||||
| group: contract-tests-${{ github.ref }} | ||||||||||||||||||||||
| cancel-in-progress: true | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||
| unit: | ||||||||||||||||||||||
| name: Unit — Zod schemas | ||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||
| timeout-minutes: 5 | ||||||||||||||||||||||
| steps: | ||||||||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||||||||
| - uses: actions/setup-node@v4 | ||||||||||||||||||||||
|
Comment on lines
+40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/contract-tests.yml"
echo "== File: $FILE =="
ls -l "$FILE"
sed -n '1,200p' "$FILE" | nl -ba | sed -n '1,200p'Repository: adm01-debug/promo-gifts-v4 Length of output: 266 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/contract-tests.yml"
echo "== File: $FILE =="
sed -n '1,220p' "$FILE" | awk '{printf "%4d\t%s\n", NR, $0}'
echo
echo "== Grep: persist-credentials =="
rg -n "persist-credentials" "$FILE" || true
echo
echo "== Grep: uses: actions/checkout/setup-node/supabase/setup-cli =="
rg -n "uses:\s*(actions/checkout|actions/setup-node|supabase/setup-cli)@" "$FILE" || trueRepository: adm01-debug/promo-gifts-v4 Length of output: 4482 Corrigir pinagem de actions, persistência de credenciais e falhas de confiabilidade no contract-tests
🧰 Tools🪛 zizmor (1.25.2)[warning] 40-40: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [error] 40-40: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) [error] 41-41: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI Agents |
||||||||||||||||||||||
| with: | ||||||||||||||||||||||
| node-version: '20' | ||||||||||||||||||||||
| cache: 'npm' | ||||||||||||||||||||||
| - run: npm ci --no-audit --no-fund | ||||||||||||||||||||||
| - name: Run vitest contract tests | ||||||||||||||||||||||
| run: npm test -- tests/contracts/ --run | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||||||||||||||||||||||
| - name: Report coverage summary | ||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| echo "### Contract test summary" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||
| if [ -f tests/contracts ]; then | ||||||||||||||||||||||
|
Comment on lines
+47
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The summary guard uses Prompt for AI agents
Suggested change
|
||||||||||||||||||||||
| find tests/contracts -name '*.contract.test.ts' | wc -l \ | ||||||||||||||||||||||
| | xargs -I{} echo "{} contract test files" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||
| fi | ||||||||||||||||||||||
|
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Corrija a verificação de existência da pasta de testes. Em Line 52 foi usado Diff sugerido- if [ -f tests/contracts ]; then
+ if [ -d tests/contracts ]; then🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| smoke: | ||||||||||||||||||||||
| name: Smoke — HTTP against supabase functions serve | ||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||
| needs: unit | ||||||||||||||||||||||
| # Só roda em PRs do próprio repo (forks não têm acesso a secrets, e | ||||||||||||||||||||||
| # supabase CLI exige tokens válidos pra subir o stack) | ||||||||||||||||||||||
| if: github.event.pull_request.head.repo.full_name == github.repository | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| timeout-minutes: 15 | ||||||||||||||||||||||
| steps: | ||||||||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - uses: actions/setup-node@v4 | ||||||||||||||||||||||
| with: | ||||||||||||||||||||||
| node-version: '20' | ||||||||||||||||||||||
| cache: 'npm' | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - uses: supabase/setup-cli@v1 | ||||||||||||||||||||||
| with: | ||||||||||||||||||||||
| version: latest | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - run: npm ci --no-audit --no-fund | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - name: Start Supabase stack | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| supabase start | ||||||||||||||||||||||
| # captura anon key local | ||||||||||||||||||||||
| echo "SUPABASE_ANON_KEY=$(supabase status --output json | jq -r '.ANON_KEY // .API.anon_key')" >> "$GITHUB_ENV" | ||||||||||||||||||||||
| echo "SUPABASE_URL=$(supabase status --output json | jq -r '.API_URL // .API.url')" >> "$GITHUB_ENV" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
Comment on lines
+83
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# Locate the workflow file
ls -la .github/workflows/contract-tests.yml 2>/dev/null || true
# Show the relevant section around the reported lines
nl -ba .github/workflows/contract-tests.yml | sed -n '60,120p'
# Find all usages of SUPABASE_ANON_KEY / SUPABASE_URL in the workflow
rg -n "SUPABASE_(ANON_KEY|URL)" .github/workflows/contract-tests.yml
# Show nearby context where they are used (a bit more than matches)
rg -n "SUPABASE_(ANON_KEY|URL)" -n .github/workflows/contract-tests.yml -C 3Repository: adm01-debug/promo-gifts-v4 Length of output: 217 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the file exists
ls -la .github/workflows/contract-tests.yml
# Print with line numbers (nl is unavailable)
cat -n .github/workflows/contract-tests.yml | sed -n '60,120p'
# Find all usages of the env vars in the workflow
rg -n "SUPABASE_(ANON_KEY|URL)" .github/workflows/contract-tests.yml
# Show a wider context around their usages
rg -n "SUPABASE_(ANON_KEY|URL)" -C 3 .github/workflows/contract-tests.ymlRepository: adm01-debug/promo-gifts-v4 Length of output: 3676 Falhar rápido se Diff sugerido - name: Start Supabase stack
run: |
supabase start
- # captura anon key local
- echo "SUPABASE_ANON_KEY=$(supabase status --output json | jq -r '.ANON_KEY // .API.anon_key')" >> "$GITHUB_ENV"
- echo "SUPABASE_URL=$(supabase status --output json | jq -r '.API_URL // .API.url')" >> "$GITHUB_ENV"
+ STATUS_JSON="$(supabase status --output json)"
+ ANON_KEY="$(echo "$STATUS_JSON" | jq -r '.ANON_KEY // .API.anon_key // empty')"
+ API_URL="$(echo "$STATUS_JSON" | jq -r '.API_URL // .API.url // empty')"
+ [ -n "$ANON_KEY" ] || { echo "SUPABASE_ANON_KEY ausente"; exit 1; }
+ [ -n "$API_URL" ] || { echo "SUPABASE_URL ausente"; exit 1; }
+ echo "SUPABASE_ANON_KEY=$ANON_KEY" >> "$GITHUB_ENV"
+ echo "SUPABASE_URL=$API_URL" >> "$GITHUB_ENV"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| - name: Serve Edge Functions | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| nohup supabase functions serve --no-verify-jwt > /tmp/functions.log 2>&1 & | ||||||||||||||||||||||
| echo $! > /tmp/functions.pid | ||||||||||||||||||||||
| # espera readiness | ||||||||||||||||||||||
| for i in $(seq 1 30); do | ||||||||||||||||||||||
| if curl -sf "$SUPABASE_URL/functions/v1/" -o /dev/null; then break; fi | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The readiness loop curls Useful? React with 👍 / 👎. |
||||||||||||||||||||||
| sleep 1 | ||||||||||||||||||||||
| done | ||||||||||||||||||||||
|
Comment on lines
+91
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Faça fail explícito se o endpoint não ficar pronto no tempo limite. O loop de polling pode terminar sem readiness e seguir para Diff sugerido- for i in $(seq 1 30); do
- if curl -sf "$SUPABASE_URL/functions/v1/" -o /dev/null; then break; fi
- sleep 1
- done
+ ready=0
+ for i in $(seq 1 30); do
+ if curl -sf "$SUPABASE_URL/functions/v1/" -o /dev/null; then
+ ready=1
+ break
+ fi
+ sleep 1
+ done
+ if [ "$ready" -ne 1 ]; then
+ echo "Edge Functions não ficaram prontas em 30s"
+ exit 1
+ fi🤖 Prompt for AI Agents
Comment on lines
+91
to
+94
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The readiness loop never errors after timeout, so the job can continue with an unready Supabase Functions server. Prompt for AI agents
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
Comment on lines
+90
to
+95
|
||||||||||||||||||||||
| - name: Run contract smoke tests | ||||||||||||||||||||||
| run: npm run test:contract | ||||||||||||||||||||||
| env: | ||||||||||||||||||||||
| SUPABASE_URL: ${{ env.SUPABASE_URL }} | ||||||||||||||||||||||
| SUPABASE_ANON_KEY: ${{ env.SUPABASE_ANON_KEY }} | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - name: Dump functions log on failure | ||||||||||||||||||||||
| if: failure() | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| echo "::group::supabase functions serve log" | ||||||||||||||||||||||
| cat /tmp/functions.log || true | ||||||||||||||||||||||
| echo "::endgroup::" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - name: Cleanup | ||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||
| run: | | ||||||||||||||||||||||
| [ -f /tmp/functions.pid ] && kill "$(cat /tmp/functions.pid)" || true | ||||||||||||||||||||||
| supabase stop || true | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This workflow only watches
supabase/functions/**/index.ts, so contract-impacting changes in other edge-function files (for example shared helpers undersupabase/functions/_shared/*.tsthat are imported by handlers) will not trigger contract CI at all. That creates silent coverage gaps where a PR can break function request/response contracts without running this status check.Useful? React with 👍 / 👎.