Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2687d87
feat(regua): fauna-shots — a fauna esta viva na PARTIDA, nao so no re…
rubenmarcus Aug 21, 2026
7403b5f
perf(preload): a partida carrega o elenco DELA — 63 GLBs viram 9
rubenmarcus Aug 21, 2026
0bb19c3
feat(tela-04): duas colunas na escolha de mapa e varredura do i18n (#…
rubenmarcus Aug 21, 2026
2c6736c
chore(release): v2.0.0-alpha.170
csbrasil-BOT Aug 21, 2026
e6e7b0e
feat: kill replay cam com hit-stop (#364)
EmersonGarrido Aug 21, 2026
a22af13
chore(release): v2.0.0-alpha.171
csbrasil-BOT Aug 21, 2026
bbe397c
fix: importmap do Layout e CVEs altas nas dependências (#363)
EmersonGarrido Aug 21, 2026
935dda2
chore(release): v2.0.0-alpha.172
csbrasil-BOT Aug 21, 2026
8f401a8
Merge remote-tracking branch 'origin/feat/times-e-mapas-completo' int…
rubenmarcus Aug 21, 2026
f544082
feat(preload): carga tardia só da facção inimiga + PLR sem colisão de id
rubenmarcus Aug 21, 2026
5313abe
perf(armas): a partida carrega as armas dela — 26 no bloqueante viram…
rubenmarcus Aug 21, 2026
60c158f
chore(release): v2.0.0-alpha.173
csbrasil-BOT Aug 21, 2026
d0eceba
fix(regua): DOCSAUT deixa de reprovar branch de PR com autor novo (#415)
rubenmarcus Aug 22, 2026
3666653
chore(release): v2.0.0-alpha.174
csbrasil-BOT Aug 22, 2026
c1ca5b8
fix(régua): prova de mordida que não morde passa a REPROVAR (#416)
rubenmarcus Aug 22, 2026
198e9f5
chore(release): v2.0.0-alpha.175
csbrasil-BOT Aug 22, 2026
2e96089
fix(ci): classify não reprova PR de fork por secret que o gatilho não…
rubenmarcus Aug 22, 2026
c65a8ed
chore(release): v2.0.0-alpha.176
csbrasil-BOT Aug 22, 2026
d1fd60f
feat(ci): o que ficou de fora do #408 — fase 5, varredura pós-release…
rubenmarcus Aug 22, 2026
21d7d2c
chore(release): v2.0.0-alpha.177
csbrasil-BOT Aug 22, 2026
a90d9af
fix(ci): o bot para de reprovar o PR que ele acabou de consertar (#424)
rubenmarcus Aug 22, 2026
021789e
chore(release): v2.0.0-alpha.178
csbrasil-BOT Aug 22, 2026
c9adaab
Merge da main (alpha.176) em perf/preload-roster
rubenmarcus Aug 22, 2026
49b6f7b
fix(397): o merge automático comeu o cache-buster do CSS no serve.mjs
rubenmarcus Aug 22, 2026
c73620c
fix(ci): o merge limpo do bot também leva os trailers (destrava o #40…
rubenmarcus Aug 22, 2026
9b72e17
chore(release): v2.0.0-alpha.179
csbrasil-BOT Aug 22, 2026
8ba0194
Merge da base (conflito só em arquivo gerado, resolvido pelo autofix)
csbrasil-BOT Aug 22, 2026
f8dec1e
chore(docs): regenera bloco derivado (autofix)
csbrasil-BOT Aug 22, 2026
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
242 changes: 242 additions & 0 deletions .github/workflows/autofix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
# autofix — o bot conserta o MECÂNICO e deixa o PR pronto. Ele NÃO mergeia.
#
# POR QUE EXISTE (medido em 21/08/2026, nos últimos 29 PRs): nenhum bot deste
# repositório jamais commitou um conserto. O #405 ficou vermelho por
# `DOCS1: documentação DESATUALIZADA`, cujo conserto é `npm run docs` mais um
# commit — trabalho sem julgamento nenhum, que hoje ou espera um mantenedor ou
# devolve o PR para o autor.
#
# A TRAVA: `scripts/ci/autofix_allowlist.py`. O bot só commita se TUDO que ele
# mexeu for arquivo gerado. Encostou em qualquer outro caminho, aborta e comenta.
# Sem essa lista, o primeiro conserto errado reescreve o mapa de um colaborador e
# ninguém repara — e a confiança de todo mundo de fora vai junto.
#
# `pull_request_target` porque é o único gatilho que enxerga secret em PR de fork
# (ver a régua WSEC1). O checkout é do MERGE do PR e roda apenas ferramentas do
# repositório base sobre ele — nunca `npm run` de script vindo do fork.
name: autofix

on:
pull_request_target:
types: [opened, reopened, synchronize, ready_for_review]
# A MAIN ANDANDO é o que desatualiza PR, não o PR se mexendo. Foram 9 `chore(release)`
# em 20 horas, e cada um reescreve README/STATUS/ARCH/docs — reabrindo conflito em TODO
# PR aberto ao mesmo tempo. Sem este gatilho o autofix só acorda quando o autor empurra,
# ou seja, nunca para quem está parado esperando revisão, que é justamente quem precisa.
push:
branches: [main]
workflow_dispatch:
inputs:
pr_number:
description: PR a consertar
required: true
type: number

permissions:
contents: read
pull-requests: write

concurrency:
group: autofix-${{ github.event.pull_request.number || inputs.pr_number || github.sha }}
cancel-in-progress: true

jobs:
# Varredura pós-release: descobre quem ficou para trás e dispara o autofix de cada um
# pelo `workflow_dispatch`, que é o caminho que enxerga secret. Barato — quem já está
# em dia sai no primeiro passo do autofix, sem clonar nada pesado.
varredura:
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
env:
GH_TOKEN: ${{ secrets.CSBRASIL_BOT_TOKEN }}
REPO: ${{ github.repository }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Dispara o autofix de quem ficou para trás
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::warning::CSBRASIL_BOT_TOKEN ausente — varredura não roda, e não reprova ninguém."
exit 0
fi
gh pr list --repo "$REPO" --state open --base main \
--json number,mergeable,isDraft \
--jq '.[] | select(.isDraft | not) | select(.mergeable != "MERGEABLE") | .number' > /tmp/atrasados.txt
echo "PRs desatualizados: $(wc -l < /tmp/atrasados.txt)"
while read -r pr; do
[ -n "$pr" ] || continue
echo " → #$pr"
gh workflow run autofix.yml --repo "$REPO" -f pr_number="$pr" \
|| echo "::warning::não deu para disparar o autofix do #$pr"
done < /tmp/atrasados.txt

autofix:
if: github.event_name != 'push' && github.event.pull_request.draft != true
runs-on: ubuntu-latest
timeout-minutes: 15
env:
GH_TOKEN: ${{ secrets.CSBRASIL_BOT_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
REPO: ${{ github.repository }}
steps:
- name: Guard de identidade
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::warning::CSBRASIL_BOT_TOKEN ausente — autofix não roda, e não reprova ninguém por isso."
echo "pular=1" >> "$GITHUB_ENV"
fi

- name: Dados do PR
if: env.pular != '1'
run: |
gh pr view "$PR_NUMBER" --repo "$REPO" \
--json headRefName,headRepository,headRepositoryOwner,maintainerCanModify > /tmp/pr.json
{
echo "HEAD_REF=$(jq -r .headRefName /tmp/pr.json)"
echo "HEAD_REPO=$(jq -r '.headRepositoryOwner.login + "/" + .headRepository.name' /tmp/pr.json)"
# `maintainerCanModify` é FALSE por definição quando o PR não vem de fork —
# a opção nem existe ali. Sem esta conta o bot nunca empurrava em PR de casa,
# que é justamente onde ele sempre pode (medido no #425).
echo "PODE_EDITAR=$(jq -r 'if (.headRepositoryOwner.login + "/" + .headRepository.name) == env.REPO then "true" else (.maintainerCanModify|tostring) end' /tmp/pr.json)"
} >> "$GITHUB_ENV"

# Checa o código do PR para poder REGERAR a doc a partir dele. Nada do fork é
# executado: os geradores abaixo são os do repositório base, restaurados logo
# depois do checkout.
- name: Checkout do PR
if: env.pular != '1'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
repository: ${{ env.HEAD_REPO }}
ref: ${{ env.HEAD_REF }}
token: ${{ secrets.CSBRASIL_BOT_TOKEN }}
fetch-depth: 0

- name: Traz a base e resolve conflito de arquivo gerado
if: env.pular != '1'
id: merge
run: |
git config user.name "csbrasil-bot"
git config user.email "csbrasil-bot@users.noreply.github.com"
git remote add base "https://github.com/$REPO.git" 2>/dev/null || true
BASE="${{ github.event.pull_request.base.ref || 'main' }}"
git fetch base "$BASE"
# A mensagem vai EXPLÍCITA mesmo quando o merge não conflita: `--no-edit` usa a
# mensagem automática do git, que não leva `Agent:` nem `Signed-off-by` — e aí o
# bot conserta o PR e o dco reprova o commit que ele mesmo fez (medido no #406).
if git merge --no-edit -m "Merge da base (autofix)" \
-m "Agent: csbrasil-bot (autofix)" \
-m "Signed-off-by: csbrasil-bot <csbrasil-bot@users.noreply.github.com>" FETCH_HEAD; then
git diff --quiet HEAD@{1} HEAD || echo "mesclou=1" >> "$GITHUB_OUTPUT"
echo "PR já está em cima da base (ou mesclou limpo)."
exit 0
fi
git diff --name-only --diff-filter=U > /tmp/conflitos.txt
echo "conflitos:"; cat /tmp/conflitos.txt
# A trava é lida da BASE, não do PR: um PR que reescrevesse o allowlist
# liberaria a si mesmo. Extrair para /tmp mantém a árvore limpa para o merge.
git show FETCH_HEAD:scripts/ci/autofix_allowlist.py > /tmp/allowlist-base.py
if ! python3 /tmp/allowlist-base.py --caminhos < /tmp/conflitos.txt; then
git merge --abort
echo "humano=1" >> "$GITHUB_OUTPUT"
exit 0
fi
# Só gerado: fica o da BASE, e os geradores re-derivam logo abaixo.
xargs -a /tmp/conflitos.txt -r git checkout --theirs --
xargs -a /tmp/conflitos.txt -r git add --
git commit --no-edit -m "Merge da base (conflito só em arquivo gerado, resolvido pelo autofix)" \
-m "Agent: csbrasil-bot (autofix)" \
-m "Signed-off-by: csbrasil-bot <csbrasil-bot@users.noreply.github.com>"
echo "mesclou=1" >> "$GITHUB_OUTPUT"

# As ferramentas que vão rodar têm de ser as da BASE, nunca as do fork — senão
# um PR malicioso reescreve o gerador e o bot executa o que ele mandar.
- name: Restaura as ferramentas da base
if: env.pular != '1'
run: |
git fetch base "${{ github.event.pull_request.base.ref || 'main' }}"
# O lock vem JUNTO com o package.json: `npm ci` exige que os dois batam, e
# restaurar só um deles quebra a instalação (medido em 22/08, 2 de 10 runs).
git checkout FETCH_HEAD -- tools/ scripts/ package.json package-lock.json

- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
if: env.pular != '1'
with: { node-version: 22 }
- name: Instala dependências
if: env.pular != '1'
# `npm ci` morre se lock e manifesto divergirem; aqui a instalação é meio para
# rodar o gerador, não o alvo da medição, então cair para `install` é aceitável.
run: npm ci --ignore-scripts || npm install --ignore-scripts --no-audit --no-fund

# RESOLVE CONFLITO DE ARQUIVO GERADO. Todo `chore(release)` na main reescreve
# README, STATUS, ARCH e docs/, e reabre conflito em TODO PR aberto ao mesmo
# tempo — medido em 21/08: o alpha.173 sozinho pôs quatro PRs em CONFLICTING, e
# os nove arquivos do #400 eram gerados, nenhum de código. Resolver isso à mão é
# trabalho de Sísifo: o release seguinte desfaz.
#
# A trava é a MESMA do resto do autofix. Sobrou UM conflito fora da lista, o bot
# aborta o merge e devolve para gente — conflito de código é julgamento, e
# julgamento não é dele.
- name: Comenta quando o conflito é de gente
if: env.pular != '1' && steps.merge.outputs.humano == '1'
run: |
gh pr comment "$PR_NUMBER" --repo "$REPO" --body \
"🤖 **autofix**: a base andou e o conflito encostou em arquivo de código — não é meu para resolver. Os arquivos em conflito estão no log do run; rode \`git merge origin/${{ github.event.pull_request.base.ref || 'main' }}\` e decida."

- name: Conserta o mecânico
if: env.pular != '1' && steps.merge.outputs.humano != '1'
run: |
node tools/gen-docs.mjs || echo "::warning::gen-docs falhou"
node tools/gen-arch.mjs || echo "::warning::gen-arch falhou"

# As ferramentas foram restauradas da base só para rodar; o que volta ao PR é
# apenas o resultado delas.
- name: Devolve as ferramentas ao estado do PR
if: env.pular != '1' && steps.merge.outputs.humano != '1'
run: git checkout HEAD -- tools/ scripts/ package.json package-lock.json

- name: A trava — só arquivo gerado pode virar commit
if: env.pular != '1' && steps.merge.outputs.humano != '1'
id: trava
run: |
git status --porcelain > /tmp/mexidos.txt
if [ ! -s /tmp/mexidos.txt ]; then
echo "nada=1" >> "$GITHUB_OUTPUT"
echo "PR já está em dia — nada a consertar."
exit 0
fi
if ! python3 scripts/ci/autofix_allowlist.py < /tmp/mexidos.txt > /tmp/permitidos.txt; then
cat /tmp/permitidos.txt
echo "bloqueado=1" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "commitar=1" >> "$GITHUB_OUTPUT"

- name: Comenta quando a trava bloqueia
if: env.pular != '1' && steps.trava.outputs.bloqueado == '1'
run: |
gh pr comment "$PR_NUMBER" --repo "$REPO" --body \
"🤖 **autofix**: o conserto encostou em arquivo fora da lista de permissão, então **não commitei nada**. Rode \`npm run docs && node tools/gen-arch.mjs\` e confira o que mudou."

- name: Empurra o conserto
if: env.pular != '1' && (steps.trava.outputs.commitar == '1' || steps.merge.outputs.mesclou == '1') && env.PODE_EDITAR == 'true'
run: |
git config user.name "csbrasil-bot"
git config user.email "csbrasil-bot@users.noreply.github.com"
if [ -s /tmp/permitidos.txt ]; then
xargs -a /tmp/permitidos.txt -r git add --
fi
git diff --cached --quiet || git commit -m "chore(docs): regenera bloco derivado (autofix)" \
-m "Rodado pelo autofix: só arquivo gerado, conferido pela lista de permissão." \
-m "Signed-off-by: csbrasil-bot <csbrasil-bot@users.noreply.github.com>"
git push origin "HEAD:$HEAD_REF"
gh pr comment "$PR_NUMBER" --repo "$REPO" --body \
"🤖 **autofix**: regenerei os blocos derivados e empurrei o commit. O PR não precisa de mais nada disso."

# Fork com "allow edits by maintainers" desligado: o bot não tem como empurrar.
# Comentar o comando é melhor que um vermelho que o autor não entende.
- name: Sem permissão de push — comenta o comando
if: env.pular != '1' && (steps.trava.outputs.commitar == '1' || steps.merge.outputs.mesclou == '1') && env.PODE_EDITAR != 'true'
run: |
gh pr comment "$PR_NUMBER" --repo "$REPO" --body \
"🤖 **autofix**: dá para consertar sozinho, mas este PR está com *allow edits by maintainers* desligado. Rode \`npm run docs && node tools/gen-arch.mjs\` e commite — ou ligue a opção e eu faço."
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ jobs:
# Régua se prova antes de medir o PR: portão quebrado passa calado.
- name: Autoteste das réguas de CI
run: |
python3 scripts/ci/ensure_labels.py --selftest
python3 scripts/ci/autofix_allowlist.py --selftest
python3 scripts/ci/dco_check.py --selftest
python3 scripts/ci/agente_check.py --selftest
- name: Check Signed-off-by
Expand Down
19 changes: 16 additions & 3 deletions .github/workflows/csbrasil-bot-automerge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: '3.13'
- name: Garante a etiqueta de pronto
run: python3 scripts/ci/ensure_labels.py pronto-pra-merge
- name: Fetch PR status
run: gh pr view "$PR_NUMBER" --json isDraft,labels,reviewDecision,mergeStateStatus,statusCheckRollup,files > /tmp/automerge.json
- name: Check eligibility
Expand All @@ -53,13 +55,24 @@ jobs:
run: |
python3 scripts/ci/check_automerge.py --selftest
python3 scripts/ci/check_automerge.py < /tmp/automerge.json > /tmp/automerge-result.json
- name: Merge when eligible
# DEIXAR PRONTO NÃO É MERGEAR (decisão do dono, 21/08). Este passo chamava
# `gh pr merge --squash --auto` e fechava o PR sozinho. A intenção declarada dos
# bots é outra: autocorreção e autorrevisão para deixar o PR PRONTO, com o botão
# de merge continuando humano. O que era merge vira etiqueta e um comentário que
# diz por que o PR está pronto.
- name: Marca como pronto quando elegível
run: |
python3 - <<'PY'
import json, os, subprocess, sys
data = json.load(open('/tmp/automerge-result.json'))
pr = os.environ['PR_NUMBER']
if not data.get('eligible'):
print('automerge: not eligible')
print('pronto-pra-merge: not eligible')
subprocess.run(['gh', 'pr', 'edit', pr, '--remove-label', 'pronto-pra-merge'], check=False)
sys.exit(0)
subprocess.run(['gh', 'pr', 'merge', os.environ['PR_NUMBER'], '--squash', '--delete-branch', '--auto'], check=True)
subprocess.run(['gh', 'pr', 'edit', pr, '--add-label', 'pronto-pra-merge'], check=True)
subprocess.run(['gh', 'pr', 'comment', pr, '--body',
'🤖 **csbrasil-bot**: portões verdes e diff dentro do que o '
'`check_automerge` aceita — marquei como `pronto-pra-merge`. '
'O merge continua sendo seu.'], check=True)
PY
41 changes: 41 additions & 0 deletions .github/workflows/csbrasil-bot-pr-classify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ on:
types: [submitted, dismissed]
pull_request_review_comment:
types: [created]
# Varredura: em PR vindo de FORK, os dois gatilhos de review acima rodam SEM os
# secrets do repositório (regra do GitHub), e o job abaixo não pode nem se
# identificar. O sweep roda no contexto base, com token, e reclassifica os PRs
# abertos pelo `workflow_dispatch` — é o que devolve a cobertura pós-CodeRabbit
# que os gatilhos de review davam nos PRs de casa.
schedule:
- cron: '7,37 * * * *'
workflow_dispatch:
inputs:
pr_number:
Expand All @@ -31,7 +38,17 @@ concurrency:
cancel-in-progress: true

jobs:
# PORTÃO QUE NÃO PODE MEDIR NÃO VOTA (mesma regra do #402 para o portão de autoria em
# clone raso). `pull_request_review` e `pull_request_review_comment` num PR de FORK não
# recebem secret nenhum: o job não tem como se identificar, falhava no guard e pintava
# de vermelho um PR cujo autor não tem o que consertar. Medido em 11 dos 14 PRs de fork
# dos últimos 29 - e em 0 dos 15 de casa. O run de `pull_request_target` do MESMO commit
# passa e é ele que vale; para o resto, o sweep de meia em meia hora cobre.
classify:
if: >-
github.event_name == 'pull_request_target' ||
github.event_name == 'workflow_dispatch' ||
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 10
env:
Expand Down Expand Up @@ -153,3 +170,27 @@ jobs:
subprocess.run(['gh', 'api', '-X', 'POST', f"repos/${{ github.repository }}/issues/{os.environ['PR_NUMBER']}/comments", '--input', '-'],
input=json.dumps({'body': body}), text=True, check=True)
PY

# Reclassifica os PRs abertos pelo caminho que TEM secret. Idempotente: o classify
# dedupe rótulo e comentário, então rodar de novo não polui o PR.
sweep:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
timeout-minutes: 10
env:
GH_TOKEN: ${{ secrets.CSBRASIL_BOT_TOKEN }}
REPO: ${{ github.repository }}
steps:
- name: Guard de identidade
run: |
if [ -z "$GH_TOKEN" ]; then
echo "::error::CSBRASIL_BOT_TOKEN ausente — o sweep precisa do PAT para disparar o classify."
exit 1
fi
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- name: Dispara o classify de cada PR aberto
run: |
for pr in $(gh pr list --repo "$REPO" --state open --limit 50 --json number --jq '.[].number'); do
gh workflow run csbrasil-bot-pr-classify.yml --repo "$REPO" -f pr_number="$pr" \
|| echo "::warning::não deu para reclassificar o PR #$pr"
done
5 changes: 5 additions & 0 deletions .github/workflows/portao-browser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ jobs:
if: ${{ !cancelled() }}
run: npm run eval:entrada

# A partida carrega as armas DELA e nenhum bot empunha caixa (medido no navegador).
- name: eval:armas (preload de arma por partida)
if: ${{ !cancelled() }}
run: npm run eval:armas

# Número diz que caiu; imagem diz por quê. Só quando algum passo acima reprova.
- name: Fotos das piores células (só em falha)
if: failure()
Expand Down
Loading
Loading