Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions .github/workflows/contract-tests.yml
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'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include non-index function files in path trigger

This workflow only watches supabase/functions/**/index.ts, so contract-impacting changes in other edge-function files (for example shared helpers under supabase/functions/_shared/*.ts that 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 👍 / 👎.

- '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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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" || true

Repository: adm01-debug/promo-gifts-v4

Length of output: 4482


Corrigir pinagem de actions, persistência de credenciais e falhas de confiabilidade no contract-tests

  • Segurança (supply-chain + credenciais): actions/checkout@v4 e actions/setup-node@v4 (lin. 40-41 e 66-68) usam tags mutáveis e o checkout não tem with: persist-credentials: false. supabase/setup-cli@v1 (lin. 73-75) está com version: latest (mutável). Pin por SHA e adicione persist-credentials: false no checkout.
  • Bug funcional no resumo: if [ -f tests/contracts ] (lin. 52) verifica arquivo, mas tests/contracts é diretório → o resumo pode ficar vazio. Trocar para -d (ou checar diretamente os *.contract.test.ts).
  • Confiabilidade do smoke: loop de readiness (lin. 90-94) não falha explicitamente em timeout; o workflow segue e pode quebrar de forma opaca depois. Após o for, verificar se ficou pronto e exit 1 (idealmente anexando /tmp/functions.log).
  • Validação de env ausente: SUPABASE_ANON_KEY/SUPABASE_URL (lin. 83-84) podem virar null/vazio sem abortar cedo. Validar não-empty e falhar antes do npm run test:contract.
🧰 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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/contract-tests.yml around lines 40 - 41, Pin mutable
GitHub Actions by replacing actions/checkout@v4 and actions/setup-node@v4 with
immutable SHA-pinned refs and avoid version: latest for supabase/setup-cli by
using a specific release SHA; also add with: persist-credentials: false to the
actions/checkout step. Change the test-summary existence check from if [ -f
tests/contracts ] to a directory check (e.g., -d) or directly detect the test
files (*.contract.test.ts) so the summary isn't empty. After the readiness loop
(the for loop that polls readiness), add an explicit timeout check that fails
the job (exit 1) if the service never became ready and include or tail
/tmp/functions.log in the failure output. Finally, validate SUPABASE_ANON_KEY
and SUPABASE_URL are non-empty before running npm run test:contract and fail
fast with a clear error if either is missing.

with:
node-version: '20'
cache: 'npm'
- run: npm ci --no-audit --no-fund
- name: Run vitest contract tests
run: npm test -- tests/contracts/ --run
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point unit job to existing contract tests

npm test -- tests/contracts/ --run currently targets a path that does not exist in this repository, so the unit job will fail with “No test files found” whenever this workflow is triggered, blocking the smoke job and making the new pipeline unusable as a status check. Please run Vitest against the actual contract test locations (or add --passWithNoTests if empty suites are intentional).

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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The summary guard uses -f on a directory, so the contract test count is never reported.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/contract-tests.yml, line 52:

<comment>The summary guard uses `-f` on a directory, so the contract test count is never reported.</comment>

<file context>
@@ -0,0 +1,113 @@
+        if: always()
+        run: |
+          echo "### Contract test summary" >> "$GITHUB_STEP_SUMMARY"
+          if [ -f tests/contracts ]; then
+            find tests/contracts -name '*.contract.test.ts' | wc -l \
+              | xargs -I{} echo "{} contract test files" >> "$GITHUB_STEP_SUMMARY"
</file context>
Suggested change
if [ -f tests/contracts ]; then
if [ -d tests/contracts ]; then

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Corrija a verificação de existência da pasta de testes.

Em Line 52 foi usado -f tests/contracts, mas isso é diretório. Do jeito atual o resumo pode nunca contar os arquivos.

Diff sugerido
-          if [ -f tests/contracts ]; then
+          if [ -d tests/contracts ]; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/contract-tests.yml around lines 52 - 55, O if que checa a
existência de tests/contracts usa atualmente "-f tests/contracts" (arquivo), o
que falha para diretórios; troque essa condição para testar diretório (por
exemplo "-d tests/contracts" ou "-e tests/contracts") para que o bloco que
executa find sobre tests/contracts seja executado corretamente; atualize a
condição if envolvendo o caminho "tests/contracts" no trecho que produz o resumo
para garantir que a contagem de arquivos seja sempre registrada quando a pasta
existir.


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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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 3

Repository: 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.yml

Repository: adm01-debug/promo-gifts-v4

Length of output: 3676


Falhar rápido se SUPABASE_URL/SUPABASE_ANON_KEY não vierem do supabase status
Hoje o workflow só faz jq -r ... >> $GITHUB_ENV; quando os campos não existem, jq pode acabar populando null/vazio e o job segue (readiness via curl e depois npm run test:contract) falhando de forma opaca. Faça um guard-rail e exit 1 se estiver ausente.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/contract-tests.yml around lines 83 - 85, The workflow
currently writes SUPABASE_ANON_KEY and SUPABASE_URL directly to $GITHUB_ENV even
when supabase status returns null/empty; change the step that runs the two
supabase status | jq commands to capture each value into a shell variable (e.g.,
anon_key and api_url), validate that they are non-empty/non-null, and if either
is missing echo a clear error to stderr and exit 1; only when both pass the
check should you append them to $GITHUB_ENV (references: the SUPABASE_ANON_KEY
and SUPABASE_URL assignments that call `supabase status --output json | jq -r
...`).

- 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Probe a real function endpoint for readiness

The readiness loop curls $SUPABASE_URL/functions/v1/ which is not a valid function route (Supabase requires a function name after /functions/v1/), so this check never becomes healthy and only waits 30 seconds before continuing. In cases where the runtime is still starting (or failed), the smoke tests run anyway and become flaky/fail for the wrong reason; the probe should hit a real function URL and fail fast if readiness is never reached.

Useful? React with 👍 / 👎.

sleep 1
done
Comment on lines +91 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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 npm run test:contract, gerando falha menos diagnóstica e desperdício de tempo.

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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/contract-tests.yml around lines 91 - 94, O polling com
for/seq e curl pode sair sem garantir readiness; after the loop check the curl
result and fail fast with a non-zero exit and descriptive error before running
npm run test:contract — i.e., capture the success of the readiness check (the
curl call inside the for loop) and if it never succeeded, print an error like
"Functions endpoint did not become ready in time" and exit 1 so the workflow
stops immediately instead of proceeding to npm run test:contract.

Comment on lines +91 to +94
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/contract-tests.yml, line 91:

<comment>The readiness loop never errors after timeout, so the job can continue with an unready Supabase Functions server.</comment>

<file context>
@@ -0,0 +1,113 @@
+          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
+            sleep 1
</file context>
Suggested change
for i in $(seq 1 30); do
if curl -sf "$SUPABASE_URL/functions/v1/" -o /dev/null; then break; fi
sleep 1
done
ready=false
for i in $(seq 1 30); do
if curl -sf "$SUPABASE_URL/functions/v1/" -o /dev/null; then ready=true; break; fi
sleep 1
done
[ "$ready" = true ] || { echo "Supabase Functions did not become ready in time"; exit 1; }


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
Loading