9.1 KiB
Impl: CI e2e — paralelizar o job (cadeia serial só em dev + sharding do e2e)
Status: aprovado Atualizado em: 2026-08-10 Issue: #599 Intenção: docs/plans/ci-e2e-paralelizar-job.md Appetite restante: herdado (~1 dia eng; entrega é ~5 arquivos, sem migration/UI)
Leitura da intenção
- Outcome: job
e2edoci.ymlcai de ~10 min para ≤ ~6 min de wall time (mesmo conjunto de testes, mesma confiança); cadeia de projetos + prewarm dosetupcontinuam existindo só em dev; workers do CI explícitos, default local intocado; sem classe nova de flake. - O que NÃO negociar: nenhuma spec de produto removida/alterada (campaign/
frontend/admin); dev local (
pnpm test:e2esem CI/E2E_PROD) idêntico; main e PR full rodam a suite completa; cobertura inalterada. - O que reavaliar: a hipótese listava
scripts/gate-ci.mjsescripts/run-e2e-affected.mjscomo áreas. Não serão tocados: ambos delegam apnpm test:e2e, e a cadeia condicional mora no config — a paridade local com CI vem de graça (gate-ci já setaCI=1 E2E_PROD=1→ modo prod → sem cadeia). Colocar sharding nesses scripts duplicaria política de CI na máquina local sem ganho (shard é paralelismo de runners de CI).
Medição de base (real, run 31412425553 do ci.yml, 2026-08-10)
Job e2e completo: 435 s (17:07:16 → 17:14:31). Decomposição por step:
| Fase | Tempo |
|---|---|
| setup/checkout/tooling | ~17 s |
| pnpm install (cache) | 5 s |
| install playwright chromium | 19 s |
| migrate + seed:minimal | 13 s |
build (.next/e2e) |
172 s |
| testes (suite full, 2 workers) | 196 s |
(A intenção citava 617–635 s — runs mais lentos do mesmo dia; a decomposição acima é a mais recente e é a linha de base usada aqui.)
Abordagem recomendada
flowchart LR
subgraph ci.yml[ci.yml (main) — e2e sempre full]
E2E1[e2e shard 1<br/>build + test --shard=1/2] --> C1[checks]
E2E2[e2e shard 2<br/>build + test --shard=2/2] --> C1
end
subgraph ci-pr.yml[ci-pr.yml (PR)]
S[scope job<br/>e2e_mode + e2e_matrix] --> P1[e2e full: matrix [1,2]<br/>--shard=N/2]
S --> P2[e2e selected: matrix [1]<br/>specs sem shard]
P1 --> CP[checks]
P2 --> CP
end
subgraph config[playwright.config.ts]
C[isProdMode = CI ou E2E_PROD=1<br/>→ sem projeto setup, sem dependencies<br/>dev → cadeia + prewarm atuais]
end
P1 -. roda sobre .-> C
E2E1 -. roda sobre .-> C
Opções consideradas: A) build por shard (cada runner compila) · B) job de
build + artefato .next/e2e compartilhado · C) 3 shards já de início
Recomendação: A com 2 shards — porque a medição real decide: por shard
= 54 s (fixo) + 172 s (build) + ~98 s (196/2) ≈ 324 s ≈ 5,4 min; e com a
cadeia removida os testes encolhem mais (~120–150 s full → ~75 s por shard),
chegando a ~5 min. Medido pós-merge (run 31418206512): 326 s (shard 1) /
369 s (shard 2, com retry da flake conhecida) — dentro do aceite "≤ ~6 min"
e do gatilho de revisão de 6,5 min. O caminho crítico do pipeline (max entre e2e/build/int)
passa de 435 s para ~300–330 s.
Rejeitadas: B porque os shards só podem começar depois do build
terminar: wall = build 172 + upload + download + teste/2 ≫ A (a transferência
de um .next inteiro supera o build paralelo de cada shard); o build job do
ci.yml não serve (compila .next default e o servidor e2e usa
.next/e2e via NEXT_DIST_DIR — mudar isso é mexer no contrato do webServer
para nada). C porque o ganho é marginal (~30 s a mais) ao custo de um
runner e de mais pressão de quota; fica como gatilho de revisitação se o wall
por shard passar de ~6,5 min ou a distribuição de testes ficar desbalanceada.
Componentes / mudanças
playwright.config.ts: hoist deisProdMode = Boolean(process.env.CI) || process.env.E2E_PROD === '1'(mesma condição dowebServer.commandatual — agora reusada lá e nos projects). Em prod mode: projetosetupremovido do array de projects edependenciesde campaign/frontend/ admin viram[](osetup.e2e.spec.tsfica sem project → Playwright ignora arquivo não casado, sem erro). Em dev: estrutura idêntica à de hoje (cadeiasetup → campaign → frontend → admin,fullyParallel: falsedo admin preservado nos dois modos). Comentário no config explica o porquê.scripts/lib/test-affected-core.mjs: novoe2eShardConfig(mode)puro →'full'retorna{ matrix: [1, 2], total: 2 };'selected'/'none'retornam{ matrix: [1], total: 1 }— com comentário da medição e do gatilho de 3 shards. (Módulo já é o dono da seleção e2e da CI, já unitado.)scripts/ci-scope.mjs: output JSON ganhae2e_shards(resultado do helper acima)..github/workflows/ci.yml: jobe2evira matrixshard: [1, 2](fail-fast: false; comentário apontando o single-source em test-affected-core); step de teste virapnpm test:e2e --shard=${{ matrix.shard }}/2— sem o separador--: pnpm repassa o--literal ao playwright e a flag viraria file pattern ("No tests found"); env de jobPLAYWRIGHT_WORKERS: 2explícito (aceite #4)..github/workflows/ci-pr.yml: jobscopeemitee2e_matrix+e2e_shards; jobe2eusastrategy.matrix.shard: ${{ fromJson(...) }}(full →[1,2], selected →[1]— selected continua num runner só: pagar 2 builds por 3 specs é desperdício); step full com--shard=${{ matrix.shard }}/${{ needs.scope.outputs.e2e_shards }}; step selected inalterado; env de jobPLAYWRIGHT_WORKERS: 2.- Migration: nenhuma (sem schema).
- Access / Consent: N/A.
- UI: N/A (Impeccable A — infra de CI/testes).
Nota de aceite — o spec setup e "nenhuma spec removida"
O setup.e2e.spec.ts não é removido nem alterado: ele vira dev-only (já é
documentado como prewarm dev-only no próprio config e no issue). Em CI ele é
custo puro (25 requisições de prewarm contra um build de produção, sem
compile), e nenhum spec de produto depende dele em prod. A suite completa de
produto (campaign/frontend/admin) roda em main e PR full, intacta.
Fases verificáveis
- Infra (config + workflows + helper) — as 5 mudanças acima, num único commit de CI (sem migration, sem UI).
- Verificação local —
pnpm exec playwright test --list(dev): setup presente, deps[setup]/[campaign]/[frontend];CI=1 pnpm exec playwright test --list: sem projeto setup, sem deps.pnpm test:unit(pin novo doe2eShardConfig) +pnpm gate:fast.- Smoke e2e dev de uma spec
campaign*+ umaadmin(prova que o modo dev segue com cadeia e prewarm).
- Verificação CI — o próprio PR exercita o novo matrix em full
(
playwright.config.tsestá emHIGH_RISK_EXACT→ e2e_mode full): conferir 2 shards rodando em paralelo echecksverde. Pós-merge: medir o job e2e do ci.yml e registrar antes/depois no changelog.
Rabbit holes / Não escopo (engenharia)
- Subir workers do CI além de 2 (aceite pede explícito, não maior).
- Shard por família/afinidade (corte da intenção —
--shardpadrão). - Sharding do modo
selecteddo PR (gatilho: wall selected recorrente > 10 min). - Otimizar o job
build/int(fora do escopo da intenção). - Mexer em
gate-ci.mjs/run-e2e-affected.mjs(paridade herda do config). - Reduzir
timeout-minutesdos jobs (sem ganho de aceite, risco de falso-positivo em run lento).
Riscos e mitigação
- Desbalanceamento de shard (Playwright distribui por test ID): aceitável com fullyParallel; gatilho de 3 shards se o wall passar de ~6,5 min.
- Famílias interleaved em prod (antes: campaign → frontend → admin seriadas): servidor é build de produção (sem compile race); specs são auto-contidas (fixtures por spec + advisory locks do testDatabaseLease); o número de workers por run não muda (2) — a classe de flake por carga não cresce. A run do PR full valida.
fromJsonde matrix com output vazio no ci-pr: o jobscopesempre emitee2e_matrixválido ([1]no mínimo) antes doe2eavaliar o matrix (que já depende descope); job come2e_mode == 'none'é skipado peloif:existente — o matrix de[1]nunca dispara run extra.- Drift do número 2 entre ci.yml (literal) e
e2eShardConfig: comentários cruzados nos dois arquivos + entrada do changelog; mudar para 3 é editar os dois pontos apontados.
Aceite de engenharia
- Aceite de produto da intenção ainda coberto (mapa: wall ≤ ~6 min ✓ — medido 326/369 s pós-merge, ver linha de medição acima; suite completa em main/PR-full ✓, dev intocado ✓, workers explícitos ✓, flakes: mesma carga por run)
- Invariantes AGENTS/engineering-standards (sem migration, sem access, sem UI; gate:fast + gate de CI no fechamento)
- Testes previstos: unit do
e2eShardConfig+--listnos dois modos + smoke e2e dev + run do PR full (2 shards)