From 110e9b3aad30485d98edb20a8dbcf4d864d82c69 Mon Sep 17 00:00:00 2001 From: adm01-debug Date: Sat, 23 May 2026 22:40:35 -0300 Subject: [PATCH] ci: guard que exige o gate de tipos como required status check em main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Causa-raiz das regressoes de TS recorrentes (ver #208/#196/#181/#178): a branch protection da main esta com required_status_checks vazio (enforcement_level: off), entao PRs vermelhos mergeiam e cada leva de agente reintroduz erro de tipo mais rapido do que e corrigido reativamente. O job check-protection-config do branch-protection-sentinel.yml so le o booleano `protected` e reporta "protegida" — falso positivo que escondeu o buraco. Este guard le /branches/main/protection de fato e FALHA (nao so avisa) quando: - nao ha branch protection (HTTP 404), ou - o contexto obrigatorio "Lint, Typecheck & Test" nao esta nos required status checks. Tambem sinaliza enforce_admins=false (admins furando o gate). Roda em push para main, diariamente (cron) e via dispatch. Requer permissions: administration:read para ler a proteção. NB: deve entrar JUNTO com a aplicacao da branch protection — sozinho, ele falha de proposito em main ate a protecao existir (forcing function). --- .github/workflows/required-checks-guard.yml | 78 +++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/required-checks-guard.yml diff --git a/.github/workflows/required-checks-guard.yml b/.github/workflows/required-checks-guard.yml new file mode 100644 index 000000000..9c5b3108a --- /dev/null +++ b/.github/workflows/required-checks-guard.yml @@ -0,0 +1,78 @@ +name: Required Checks Guard + +# Garante que o gate de tipos/lint/test e um REQUIRED status check em main. +# Sem isso, PRs vermelhos podem mergear — causa-raiz das regressoes de tipo +# recorrentes (ver #208 e a leva #196/#181/#178). Este guard FALHA (nao so +# avisa) quando a protecao esta ausente ou o contexto obrigatorio sumiu. + +on: + push: + branches: [main] + schedule: + - cron: '0 9 * * *' # diario ~06:00 BRT + workflow_dispatch: + +permissions: + contents: read + administration: read # necessario para ler /branches/main/protection + +jobs: + assert-required-checks: + name: Assert typecheck gate is required on main + runs-on: ubuntu-latest + steps: + - name: Check required_status_checks on main + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + REQUIRED="Lint, Typecheck & Test" + + HTTP=$(curl -sS -o /tmp/prot.json -w "%{http_code}" \ + -H "Authorization: Bearer $GH_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$REPO/branches/main/protection") + + { + echo "### 🔐 Required status checks — \`main\`" + echo "" + } >> "$GITHUB_STEP_SUMMARY" + + if [[ "$HTTP" == "404" ]]; then + echo "::error::Branch protection AUSENTE em main — nenhum check obrigatorio. PRs vermelhos podem mergear." + echo "❌ Sem branch protection em \`main\`." >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi + if [[ "$HTTP" != "200" ]]; then + echo "::error::Falha ao ler a protecao (HTTP $HTTP). O token precisa de administration:read." + exit 1 + fi + + CONTEXTS=$(jq -r '((.required_status_checks.contexts // []) + ((.required_status_checks.checks // []) | map(.context))) | unique | join("\n")' /tmp/prot.json) + ENFORCE=$(jq -r '.enforce_admins.enabled // false' /tmp/prot.json) + + { + echo "**enforce_admins:** \`$ENFORCE\`" + echo "" + echo "**Contexts obrigatorios:**" + if [[ -n "$CONTEXTS" ]]; then printf '%s\n' "$CONTEXTS" | sed 's/^/- /'; else echo "- (nenhum)"; fi + } >> "$GITHUB_STEP_SUMMARY" + + if ! printf '%s\n' "$CONTEXTS" | grep -qxF "$REQUIRED"; then + echo "::error::Required status check '$REQUIRED' ausente — o gate de tipos esta apenas consultivo. Corrija a branch protection." + { + echo "" + echo "❌ **\`$REQUIRED\` nao e required.**" + } >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi + + { + echo "" + echo "✅ \`$REQUIRED\` e required." + } >> "$GITHUB_STEP_SUMMARY" + + if [[ "$ENFORCE" != "true" ]]; then + echo "::warning::enforce_admins=false — admins ainda podem furar o gate (recomendado: true)." + fi