OPS16: reduce production deploy cooldown from 30 to 15 minutes
Change DEPLOY_COOLDOWN_MS constant, update ci.yml deferral log, and adjust unit test pins. Closes #259. Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
This commit is contained in:
parent
be2500afa9
commit
cad3556bce
4 changed files with 79 additions and 9 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -230,7 +230,7 @@ jobs:
|
|||
- name: Log deploy deferral
|
||||
if: steps.tip.outputs.stale != 'true' && steps.cooldown.outputs.active == 'true'
|
||||
run: |
|
||||
echo "Deploy deferred: last production deploy is younger than 30 minutes."
|
||||
echo "Deploy deferred: last production deploy is younger than 15 minutes."
|
||||
echo "Requeue will retry in ${{ steps.cooldown.outputs.wait_seconds }}s."
|
||||
# `vercel build` shells out to pnpm (packageManager in package.json); without
|
||||
# these steps the runner only has npm → spawn pnpm ENOENT.
|
||||
|
|
|
|||
70
docs/plans/ci-deploy-cooldown-15min-impl.md
Normal file
70
docs/plans/ci-deploy-cooldown-15min-impl.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# Impl: CI — cooldown de deploy de produção: 30 → 15 min
|
||||
|
||||
Status: aprovado
|
||||
Atualizado em: 2026-08-02
|
||||
Issue: #259
|
||||
Intenção: docs/plans/ci-deploy-cooldown-15min.md
|
||||
Appetite restante: herdado (~0,25 dia) — só constante + pins
|
||||
|
||||
## Leitura da intenção
|
||||
|
||||
- **Outcome:** intervalo mínimo entre deploys READY de produção passa de 30 para **15 minutos**; deferral + requeue inalterados; logs/pins honestos.
|
||||
- **O que NÃO negociar:** não remover cooldown; não mudar fonte da idade (Vercel READY production); não tocar `ci-pr.yml` / required checks / preview Git.
|
||||
- **O que reavaliar:** hipótese do plano de intenção confirma — dono é `DEPLOY_COOLDOWN_MS` em `scripts/lib/vercel-deploy-cooldown.mjs`; CLI já deriva minutos do constante.
|
||||
|
||||
## Abordagem recomendada
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[ci.yml deploy job] --> B[vercel-deploy-cooldown.mjs]
|
||||
B --> C[evaluateDeployCooldown]
|
||||
C --> D{DEPLOY_COOLDOWN_MS}
|
||||
D -->|"< 15 min"| E[defer + requeue]
|
||||
D -->|">= 15 min"| F[vercel deploy --prod]
|
||||
```
|
||||
|
||||
**Opções consideradas:**
|
||||
|
||||
- **A)** Alterar só `DEPLOY_COOLDOWN_MS` + pins — mínimo diff, CLI auto-ajusta log.
|
||||
- **B)** Parametrizar via env `DEPLOY_COOLDOWN_MINUTES` no workflow — flexível, mas YAGNI e duplica fonte da verdade.
|
||||
- **C)** Documentar 15 min só no workflow, constante 30 no script — mente nos testes.
|
||||
|
||||
**Recomendação:** **A** — uma constante nomeada já é o contrato OPS11; workflow só precisa do echo humano.
|
||||
|
||||
**Rejeitadas:** B (over-engineering); C (pins mentirosos).
|
||||
|
||||
### Componentes / mudanças
|
||||
|
||||
- **`DEPLOY_COOLDOWN_MS`** (`scripts/lib/vercel-deploy-cooldown.mjs`): `30 * 60 * 1000` → `15 * 60 * 1000`.
|
||||
- **`.github/workflows/ci.yml`**: echo de deferral "30 minutes" → "15 minutes".
|
||||
- **`tests/unit/vercelDeployCooldown.unit.spec.ts`**: pin da constante + descrições; ajustar `waitSeconds` no caso 10 min atrás (20 min → 5 min residual).
|
||||
- **Migration:** sem migration.
|
||||
- **Access / Consent:** N/A.
|
||||
- **UI:** N/A (Impeccable A).
|
||||
|
||||
### Dados → forma
|
||||
|
||||
N/A — política de infra.
|
||||
|
||||
## Fases verificáveis
|
||||
|
||||
1. **Constante + workflow log** — alterar `DEPLOY_COOLDOWN_MS` e echo em `ci.yml`.
|
||||
2. **Pins unitários** — atualizar `vercelDeployCooldown.unit.spec.ts`.
|
||||
3. **Gates** — `pnpm gate:fast`; entrega com `pnpm push`.
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
||||
- Reescrever `docs/plans/ci-deploy-cooldown-vercel.md` (histórico OPS11).
|
||||
- Parametrizar cooldown por env sem pedido de produto.
|
||||
- Batch de merges / teto diário de deploys.
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
- **429 `api-upload-free` volta:** observar pós-merge; reabrir com evidência (assumido na intenção).
|
||||
- **Echo hardcoded no workflow:** aceito — único lugar; constante é fonte da verdade para lógica.
|
||||
|
||||
## Aceite de engenharia
|
||||
|
||||
- [x] Aceite de produto da intenção ainda coberto
|
||||
- [x] Invariantes AGENTS/engineering-standards
|
||||
- [x] Testes de domínio previstos (unit pins existentes)
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
* deployment and computes whether CI should defer another upload (OPS11).
|
||||
*/
|
||||
|
||||
export const DEPLOY_COOLDOWN_MS = 30 * 60 * 1000
|
||||
export const DEPLOY_COOLDOWN_MS = 15 * 60 * 1000
|
||||
|
||||
const VERCEL_API_BASE = 'https://api.vercel.com'
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ const captureFetch = (response: Response) => {
|
|||
}
|
||||
|
||||
describe('vercelDeployCooldown', () => {
|
||||
it('uses a 30-minute cooldown constant', () => {
|
||||
expect(DEPLOY_COOLDOWN_MS).toBe(30 * 60 * 1000)
|
||||
it('uses a 15-minute cooldown constant', () => {
|
||||
expect(DEPLOY_COOLDOWN_MS).toBe(15 * 60 * 1000)
|
||||
})
|
||||
|
||||
it('lastProductionReadyAtFromDeployment prefers ready over createdAt', () => {
|
||||
|
|
@ -45,25 +45,25 @@ describe('vercelDeployCooldown', () => {
|
|||
})
|
||||
})
|
||||
|
||||
it('computeCooldownWait defers when the last deploy is younger than 30 minutes', () => {
|
||||
it('computeCooldownWait defers when the last deploy is younger than 15 minutes', () => {
|
||||
const now = 1_700_000_000_000
|
||||
const last = now - 10 * 60 * 1000
|
||||
|
||||
expect(computeCooldownWait(last, now)).toEqual({
|
||||
active: true,
|
||||
waitSeconds: 20 * 60,
|
||||
waitSeconds: 5 * 60,
|
||||
ageMs: 10 * 60 * 1000,
|
||||
})
|
||||
})
|
||||
|
||||
it('computeCooldownWait clears when the last deploy is older than 30 minutes', () => {
|
||||
it('computeCooldownWait clears when the last deploy is older than 15 minutes', () => {
|
||||
const now = 1_700_000_000_000
|
||||
const last = now - 31 * 60 * 1000
|
||||
const last = now - 16 * 60 * 1000
|
||||
|
||||
expect(computeCooldownWait(last, now)).toEqual({
|
||||
active: false,
|
||||
waitSeconds: 0,
|
||||
ageMs: 31 * 60 * 1000,
|
||||
ageMs: 16 * 60 * 1000,
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue