fix(ops OPS57): simplify — veto de draft vale em conversão mid-run; CLI valida base main; docs sem mecanismo fantasma #52
8 changed files with 73 additions and 22 deletions
|
|
@ -78,11 +78,13 @@ Use `--required` — without it, `--watch` also waits for Vercel.
|
|||
|
||||
There is no stage→main promote. Merging to `main` is the delivery; `ci.yml` verifies the full suite and production deploys from the homeserver runbook.
|
||||
|
||||
## Safety net (GitHub Action)
|
||||
## Safety net (workflow)
|
||||
|
||||
`agent-pr-ready-automerge.yml` — PRs same-repo → `main` (todas as branches):
|
||||
se draft `cursor/*` → `gh pr ready`; sempre arma `gh pr merge --auto --rebase`.
|
||||
Cobre o caso em que o agente (ou o default draft do Cloud) esqueceu e PRs
|
||||
humanas/worktree com merge falho silencioso (incidente OPS52). **Draft de
|
||||
branch não-`cursor/*` nunca é tocado** — o draft é o veto do ator (skip do CLI).
|
||||
Não há opt-out por skill (audit incluído).
|
||||
`agent-pr-ready-automerge.yml` — PRs same-repo → `main` (todas as branches).
|
||||
O workflow roda `scripts/forgejo-pr-automerge.mjs` (plain Node, zero-dep): se
|
||||
draft `cursor/*` → marca Ready; espera os checks e mergea por rebase (o
|
||||
equivalente Forgejo do `gh pr merge --auto --rebase`). Cobre o caso em que o
|
||||
agente (ou o default draft do Cloud) esqueceu e PRs humanas/worktree com merge
|
||||
falho silencioso (incidente OPS52). **Draft de branch não-`cursor/*` nunca é
|
||||
tocado** — o draft é o veto do ator (skip do CLI). Não há opt-out por skill
|
||||
(audit incluído).
|
||||
|
|
|
|||
|
|
@ -20,10 +20,13 @@ on:
|
|||
|
||||
jobs:
|
||||
ready-automerge:
|
||||
# Same-repo only: a fork PR merged here would push attacker code to main
|
||||
# (and deploy to production). The draft policy lives in the CLI (skips
|
||||
# non-`cursor/*` drafts) so it reads the PR state from the API, not from
|
||||
# webhook payload fields.
|
||||
# Same-repo only. This MUST stay in the `if:` (pre-checkout): for a fork
|
||||
# PR, actions/checkout checks out the fork's head, so a same-repo guard
|
||||
# inside the CLI would already be running the fork's own script — RCE with
|
||||
# the Actions token. Fork PRs are excluded here because the repo is public
|
||||
# and a merged fork PR would deploy attacker code to production. The draft
|
||||
# policy lives in the CLI (skips non-`cursor/*` drafts) because there the
|
||||
# PR state is re-read from the API, not from webhook payload fields.
|
||||
if: >-
|
||||
github.event.pull_request.state == 'open' &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ Arquivos de "lista" que N agentes escrevem em paralelo (changelog, plans, migra
|
|||
| `ci.yml` | push / dispatch `main` | service Postgres | Full sempre; fail-fast cancela o run no 1º job vermelho; rollup `checks`; job `deploy` (runs-on `host`) publica no homeserver via `scripts/deploy-homeserver.sh` (OPS53) |
|
||||
| `issue-done-on-main-merge.yml` | PR merged → `main` | — | `Closes`/`Fixes` → `done` + `in-prod` |
|
||||
| `plan-issue-ready-on-main-merge.yml` | PR merged → `main` | — | `Related #N` aguardando plano → `ready` (OPS18; soft-skip) |
|
||||
| `agent-pr-ready-automerge.yml` | PR same-repo → `main` (open/sync/…) | — | Safety net: Ready + `gh pr merge --auto --rebase` (todas as branches; draft `cursor/*` → Ready; draft não-`cursor/*` = veto, skip) |
|
||||
| `agent-pr-ready-automerge.yml` | PR same-repo → `main` (open/sync/…) | — | Safety net via `forgejo-pr-automerge.mjs` (plain Node): espera checks e mergea por rebase; draft `cursor/*` → Ready; draft não-`cursor/*` = veto, skip |
|
||||
| `agent-pool.yml` | schedule / PR closed `main` / dispatch | — | Supervisor do pool (`POOL_GITHUB_TOKEN`) |
|
||||
|
||||
Action runtimes: `actions/checkout@v5`, `actions/setup-node@v5`, `pnpm/action-setup@v6` e `styfle/cancel-workflow-action@0.13.1` usam Node 24 nativo — sem `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24`.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
|
|||
|
||||
**Recently resolved (2026-08-18):** **OPS57** — o safety net do `forgejo-pr-automerge` agora cobre **todas as PRs same-repo para `main`**, não só `cursor/*`. Motivação (débito deferido na OPS56): dos 25 PRs mergeados no repo, **zero** eram `cursor/*` — 9 de `plans/*`, 11 de worktrees humanos, 5 de `refs/pull/N/head` (era da migração) — o filtro antigo nunca cobriu nenhum PR real, e o incidente OPS52 (PR #27, humano, merge falhou silenciosamente) era exatamente a classe excluída. Mudanças: `if:` do job em `agent-pr-ready-automerge.yml` → `state == 'open' && head.repo.full_name == github.repository` (guarda de fork — o repo é público e uma PR de fork mergeada dispararia o deploy de prod; campos já usados no payload, sem dependência de `is_draft` do webhook); política de draft movida para o CLI `forgejo-pr-automerge.mjs` com o head ref do **re-read da API** — draft `cursor/*` → Ready (pool inalterado); draft não-`cursor/*` → skip exit 0 (**draft = veto do ator, PR humana nunca é forçada**); PRs ready não-`cursor/*` (humanas/plans) passam a mergear com CI verde — redundante no caminho feliz (o fluxo humano já arma `--auto --rebase`) e é o que pega a classe OPS52; `ready_for_review` em PR draft re-dispara o job e mergea. Docs sincronizadas (`docs/AGENT-OPS.md` tabela CI por alvo, `.agents/rules/agent-pr-workflow.mdc` seção Safety net). Rejeitado: `!is_draft` no YAML (campo do payload não verificado, falha fail-open) e allowlist de prefixos (códigos humanos sem prefixo comum — OPS/S1/PUB5). A própria PR desta entrega ainda usa o workflow de `main` (antigo); a próxima PR exercita o gatilho alargado. Sem migration/access/Consent/UI. Testes: nenhum novo (lógica de veredito já coberta pelos specs da OPS56; branch de draft é 3 linhas no CLI); gates unit/int verdes.
|
||||
|
||||
**Recently resolved (2026-08-18):** **OPS57** — o safety net do `forgejo-pr-automerge` agora cobre **todas as PRs same-repo para `main`**, não só `cursor/*`. Motivação (débito deferido na OPS56): dos 25 PRs mergeados no repo, **zero** eram `cursor/*` — 9 de `plans/*`, 11 de worktrees humanos, 5 de `refs/pull/N/head` (era da migração) — o filtro antigo nunca cobriu nenhum PR real, e o incidente OPS52 (PR #27, humano, merge falhou silenciosamente) era exatamente a classe excluída. Mudanças: `if:` do job em `agent-pr-ready-automerge.yml` → `state == 'open' && head.repo.full_name == github.repository` (guarda de fork — o repo é público e uma PR de fork mergeada dispararia o deploy de prod; campos já usados no payload, sem dependência de `is_draft` do webhook); política de draft movida para o CLI `forgejo-pr-automerge.mjs` com o head ref do **re-read da API** — draft `cursor/*` → Ready (pool inalterado); draft não-`cursor/*` → skip exit 0 (**draft = veto do ator, PR humana nunca é forçada**); PRs ready não-`cursor/*` (humanas/plans) passam a mergear com CI verde — redundante no caminho feliz (o fluxo humano já arma `--auto --rebase`) e é o que pega a classe OPS52; `ready_for_review` em PR draft re-dispara o job e mergea. Docs sincronizadas (`docs/AGENT-OPS.md` tabela CI por alvo, `.agents/rules/agent-pr-workflow.mdc` seção Safety net). Rejeitado: `!is_draft` no YAML (campo do payload não verificado, falha fail-open) e allowlist de prefixos (códigos humanos sem prefixo comum — OPS/S1/PUB5). A própria PR desta entrega ainda usa o workflow de `main` (antigo); a próxima PR exercita o gatilho alargado. Sem migration/access/Consent/UI. Testes: spec nova em `forgejoApi.unit.spec.ts` (waitForChecks para quando o PR vira draft no meio do wait — veto garantido mesmo em conversão mid-run; autoMerge idempotente); gates unit/int verdes. **Validação ao vivo na própria entrega:** o Forgejo roda o workflow do head da PR (não o de main), então a PR #48 foi mergeada pelo safety net alargado no primeiro uso real — o `if:` com `head.repo.full_name` avaliou true em PR real e o script mergeou por rebase. Fixes pós-review do /simplify: `waitForChecks`/`autoMerge` param em PR draft (antes: 405 ruidoso na conversão mid-run, contradizendo o "veto nunca tocado"); CLI valida `base.ref === 'main'` (invocação manual); comentário do fork guard documenta por que ele não pode descer pro CLI (checkout de fork roda o script do fork = RCE com o token do Actions); docs sem o mecanismo fantasma `gh pr` (era GitHub, OPS50 trocou por CLI plain-Node).
|
||||
|
||||
**Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte.
|
||||
|
||||
**Recently resolved (2026-08-17):** **OPS53** — o deploy do site 1313 virou etapa do CI: mergir em `main` com a suite verde == site atualizado em `jorgesolla1313.com.br`, sem passo manual. O job `deploy` em `.forgejo/workflows/ci.yml` roda no host do runner (workstation, `runs-on: host`) e streama `scripts/deploy-homeserver.sh` para o homeserver via SSH (`ssh homeserver "bash -s -- <sha>"`); o script remoto (bash puro, homeserver sem dependências novas) faz: guard de HEAD (`git ls-remote` de `main` == SHA do job, senão skip "stale"), `flock` de serialização, workspace `~/teqo-deploy` (clone do Forgejo local), `docker login localhost:5000` (REGISTRY_USER/PASSWORD de `~/stack/.env`), build dos stages `runner`+`migrator` no homeserver (`--network stack_default` para a geração estática alcançar o `postgres:5432` do compose; secrets de `~/stack/teqo-1313.env` via BuildKit, sem camadas/logs), push no registry local, swap dos tags de imagem no compose (com backup `docker-compose.yml.pre-<sha>`), **migrate pelo serviço de maintenance `teqo-1313-migrate` antes do rollout**, `docker compose up -d`, espera do healthcheck e smoke (`/`, `/campanha/login`, `/admin`, barreira 307, WebAuthn `login-options` com body JSON, `POST /api/revalidate` com o secret real — rehearsal validado contra o site vivo). Falha pós-swap → rollback automático best-effort (restore do backup + `up -d` da imagem anterior) + job vermelho. **Divergência deliberada da intenção (fato novo):** a intenção assumia o runner no homeserver; verificado (`infra-solla/STATE.md` + ao vivo) que o runner é a `workstation-runner` na workstation e o stack de produção vive no homeserver — a abordagem A foi corrigida para "job `runs-on: host` na workstation + todo o deploy no homeserver via SSH", o que manteve **zero secrets novos no Forgejo** e zero mudança de infra no compose. Nenhum secret no repo; envs seguem chmod-600 no homeserver. Runbook (rollback manual, falhas conhecidas, decisão de segurança do host label): `docs/ops/teqo-1313-deploy.md` (novo). Guarda estrutural nova: `tests/unit/deployScript.unit.spec.ts` (bash -n + invariantes do pipeline: HEAD guard, BuildKit secrets, ordem migrate→rollout, smoke). AGENTS.md/AGENT-OPS.md atualizados. Primeiro deploy real = o merge deste próprio item (tracer bullet); infra-solla `STATE.md` anotado à parte.
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
**Recently resolved (2026-08-18):** **OPS57** — o safety net do `forgejo-pr-automerge` agora cobre **todas as PRs same-repo para `main`**, não só `cursor/*`. Motivação (débito deferido na OPS56): dos 25 PRs mergeados no repo, **zero** eram `cursor/*` — 9 de `plans/*`, 11 de worktrees humanos, 5 de `refs/pull/N/head` (era da migração) — o filtro antigo nunca cobriu nenhum PR real, e o incidente OPS52 (PR #27, humano, merge falhou silenciosamente) era exatamente a classe excluída. Mudanças: `if:` do job em `agent-pr-ready-automerge.yml` → `state == 'open' && head.repo.full_name == github.repository` (guarda de fork — o repo é público e uma PR de fork mergeada dispararia o deploy de prod; campos já usados no payload, sem dependência de `is_draft` do webhook); política de draft movida para o CLI `forgejo-pr-automerge.mjs` com o head ref do **re-read da API** — draft `cursor/*` → Ready (pool inalterado); draft não-`cursor/*` → skip exit 0 (**draft = veto do ator, PR humana nunca é forçada**); PRs ready não-`cursor/*` (humanas/plans) passam a mergear com CI verde — redundante no caminho feliz (o fluxo humano já arma `--auto --rebase`) e é o que pega a classe OPS52; `ready_for_review` em PR draft re-dispara o job e mergea. Docs sincronizadas (`docs/AGENT-OPS.md` tabela CI por alvo, `.agents/rules/agent-pr-workflow.mdc` seção Safety net). Rejeitado: `!is_draft` no YAML (campo do payload não verificado, falha fail-open) e allowlist de prefixos (códigos humanos sem prefixo comum — OPS/S1/PUB5). A própria PR desta entrega ainda usa o workflow de `main` (antigo); a próxima PR exercita o gatilho alargado. Sem migration/access/Consent/UI. Testes: nenhum novo (lógica de veredito já coberta pelos specs da OPS56; branch de draft é 3 linhas no CLI); gates unit/int verdes.
|
||||
**Recently resolved (2026-08-18):** **OPS57** — o safety net do `forgejo-pr-automerge` agora cobre **todas as PRs same-repo para `main`**, não só `cursor/*`. Motivação (débito deferido na OPS56): dos 25 PRs mergeados no repo, **zero** eram `cursor/*` — 9 de `plans/*`, 11 de worktrees humanos, 5 de `refs/pull/N/head` (era da migração) — o filtro antigo nunca cobriu nenhum PR real, e o incidente OPS52 (PR #27, humano, merge falhou silenciosamente) era exatamente a classe excluída. Mudanças: `if:` do job em `agent-pr-ready-automerge.yml` → `state == 'open' && head.repo.full_name == github.repository` (guarda de fork — o repo é público e uma PR de fork mergeada dispararia o deploy de prod; campos já usados no payload, sem dependência de `is_draft` do webhook); política de draft movida para o CLI `forgejo-pr-automerge.mjs` com o head ref do **re-read da API** — draft `cursor/*` → Ready (pool inalterado); draft não-`cursor/*` → skip exit 0 (**draft = veto do ator, PR humana nunca é forçada**); PRs ready não-`cursor/*` (humanas/plans) passam a mergear com CI verde — redundante no caminho feliz (o fluxo humano já arma `--auto --rebase`) e é o que pega a classe OPS52; `ready_for_review` em PR draft re-dispara o job e mergea. Docs sincronizadas (`docs/AGENT-OPS.md` tabela CI por alvo, `.agents/rules/agent-pr-workflow.mdc` seção Safety net). Rejeitado: `!is_draft` no YAML (campo do payload não verificado, falha fail-open) e allowlist de prefixos (códigos humanos sem prefixo comum — OPS/S1/PUB5). A própria PR desta entrega ainda usa o workflow de `main` (antigo); a próxima PR exercita o gatilho alargado. Sem migration/access/Consent/UI. Testes: spec nova em `forgejoApi.unit.spec.ts` (waitForChecks para quando o PR vira draft no meio do wait — veto garantido mesmo em conversão mid-run; autoMerge idempotente); gates unit/int verdes. **Validação ao vivo na própria entrega:** o Forgejo roda o workflow do head da PR (não o de main), então a PR #48 foi mergeada pelo safety net alargado no primeiro uso real — o `if:` com `head.repo.full_name` avaliou true em PR real e o script mergeou por rebase. Fixes pós-review do /simplify: `waitForChecks`/`autoMerge` param em PR draft (antes: 405 ruidoso na conversão mid-run, contradizendo o "veto nunca tocado"); CLI valida `base.ref === 'main'` (invocação manual); comentário do fork guard documenta por que ele não pode descer pro CLI (checkout de fork roda o script do fork = RCE com o token do Actions); docs sem o mecanismo fantasma `gh pr` (era GitHub, OPS50 trocou por CLI plain-Node).
|
||||
|
|
|
|||
|
|
@ -46,20 +46,27 @@ try {
|
|||
)
|
||||
process.exit(0)
|
||||
}
|
||||
if (pr.base.ref !== 'main') {
|
||||
console.log(`[forgejo-pr-automerge] PR #${prNumber} base é ${pr.base.ref} (não main) — skip`)
|
||||
process.exit(0)
|
||||
}
|
||||
if (pr.isDraft) {
|
||||
if (pr.head.ref.startsWith('cursor/')) {
|
||||
console.log(`[forgejo-pr-automerge] PR #${prNumber} é draft — marcando Ready`)
|
||||
await api.markPullRequestReady(prNumber)
|
||||
} else {
|
||||
if (!pr.head.ref.startsWith('cursor/')) {
|
||||
console.log(
|
||||
`[forgejo-pr-automerge] PR #${prNumber} é draft fora de cursor/* — skip (não força PR humano)`,
|
||||
`[forgejo-pr-automerge] PR #${prNumber} é draft fora de cursor/* — skip (veto do ator)`,
|
||||
)
|
||||
process.exit(0)
|
||||
}
|
||||
console.log(`[forgejo-pr-automerge] PR #${prNumber} é draft — marcando Ready`)
|
||||
await api.markPullRequestReady(prNumber)
|
||||
} else {
|
||||
console.log(`[forgejo-pr-automerge] PR #${prNumber} já Ready`)
|
||||
}
|
||||
const { attempted, merged } = await api.autoMerge(prNumber, {
|
||||
const {
|
||||
attempted,
|
||||
merged,
|
||||
pr: after,
|
||||
} = await api.autoMerge(prNumber, {
|
||||
log: (line) => console.log(`[forgejo-pr-automerge] ${line}`),
|
||||
})
|
||||
if (merged) {
|
||||
|
|
@ -69,7 +76,11 @@ try {
|
|||
: `[forgejo-pr-automerge] PR #${prNumber} já mergeado`,
|
||||
)
|
||||
} else {
|
||||
console.log(`[forgejo-pr-automerge] PR #${prNumber} fechado sem merge — skip`)
|
||||
console.log(
|
||||
after?.state === 'CLOSED'
|
||||
? `[forgejo-pr-automerge] PR #${prNumber} fechado sem merge — skip`
|
||||
: `[forgejo-pr-automerge] PR #${prNumber} voltou a draft — skip (veto do ator)`,
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[forgejo-pr-automerge] falhou: ${error instanceof Error ? error.message : error}`)
|
||||
|
|
|
|||
|
|
@ -272,12 +272,14 @@ export const createApi = ({ base, token, repository, fetchImpl } = {}) => {
|
|||
/**
|
||||
* All required statuses on the PR head are success (or a failure exists),
|
||||
* and Forgejo finished computing mergeability. Returns the last PR read.
|
||||
* A draft PR stops the wait (converted mid-run): the caller decides what
|
||||
* "no merge" means — the CLI skips non-`cursor/*` drafts (OPS57 veto).
|
||||
*/
|
||||
waitForChecks: async (number, { timeoutMs = 30 * 60 * 1000, pollMs = 15000, log } = {}) => {
|
||||
const started = Date.now()
|
||||
for (;;) {
|
||||
const pr = await api.getPullRequest(number)
|
||||
if (!pr || pr.state !== 'OPEN' || pr.merged) return pr
|
||||
if (!pr || pr.state !== 'OPEN' || pr.merged || pr.isDraft) return pr
|
||||
if (pr.mergeable === false) {
|
||||
throw new Error(`PR #${number} não mergeável (conflito?) — resolver manualmente.`)
|
||||
}
|
||||
|
|
@ -314,7 +316,7 @@ export const createApi = ({ base, token, repository, fetchImpl } = {}) => {
|
|||
*/
|
||||
autoMerge: async (number, options) => {
|
||||
const pr = await api.waitForChecks(number, options)
|
||||
if (!pr || pr.merged || pr.state !== 'OPEN') {
|
||||
if (!pr || pr.merged || pr.state !== 'OPEN' || pr.isDraft) {
|
||||
return { attempted: false, merged: Boolean(pr?.merged), pr }
|
||||
}
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -221,6 +221,37 @@ describe('forgejo-api', () => {
|
|||
await expect(api.waitForChecks(4, { pollMs: 1 })).rejects.toThrow(/não mergeável/)
|
||||
})
|
||||
|
||||
it('waitForChecks stops when the PR is converted to draft mid-wait (OPS57 veto)', async () => {
|
||||
let poll = 0
|
||||
const api = createApi({
|
||||
token: 'tok',
|
||||
fetchImpl: async (url) => {
|
||||
poll += 1
|
||||
if (String(url).includes('/pulls/')) {
|
||||
return ok({
|
||||
number: 4,
|
||||
state: 'open',
|
||||
merged: false,
|
||||
mergeable: true,
|
||||
is_draft: poll >= 3,
|
||||
head: { ref: 'OPS57-x', sha: 'abc' },
|
||||
})
|
||||
}
|
||||
return ok({
|
||||
statuses:
|
||||
poll <= 2
|
||||
? [{ context: 'CI / static', status: 'pending' }]
|
||||
: [{ context: 'CI / static', status: 'success' }],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const pr = await api.waitForChecks(4, { pollMs: 1 })
|
||||
|
||||
expect(poll).toBe(3)
|
||||
expect(pr.isDraft).toBe(true)
|
||||
})
|
||||
|
||||
it('autoMerge verifies the merge by re-reading the PR (POST answers 200 + empty body)', async () => {
|
||||
const calls: string[] = []
|
||||
let poll = 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue