Communitygithub.com

afresto-deploy

Skill para deploy do Afresto Next: backend Go, web React, mobile Expo. Inclui regras rígidas de commit, qualidade de código Go, e procedimentos de implantação automatizados.

O que é afresto-deploy?

Esta skill define o fluxo de deploy completo para o projeto Afresto Next, que possui uma stack composta por backend Go (push para GHCR → Watchtower ~5 min), frontend web React (Vite → Cloudflare Worker via `wrangler deploy` manual) e mobile React Native/Expo (OTA via EAS em repositório separado). A skill impõe regras rigorosas para evitar conflitos entre múltiplos agentes trabalhando no mesmo repositório, incluindo stage seletivo de arquivos (nunca `git add -A`), verificação de golangci-lint local, e exigência de que qualquer trabalho significativo seja registrado em uma issue no GitHub antes de codificar. Também cobre o fluxo de migração de banco de dados (obrigatório estar no commit TIP), recuperação de outages 502 pós-merge, deploy web a partir de worktrees limpos para não misturar trabalho de outros agentes, e deploy mobile OTA com `--environment` obrigatório e verificação de branch. Tudo isso documentado com exemplos de comandos, armadilhas reais, e procedimentos de segurança.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add Afresto-Next/afresto-claude-skills

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Implantar Afresto Next

Stack: backend Go (push→GHCR→Watchtower ~5 min) · web React (Vite→Cloudflare Worker, manual wrangler deploy) · mobile RN/Expo em repositório separado <repo-mobile> (EAS OTA). Deploy de produção = sempre pedir permissão do usuário primeiro (eles aprovam cada vez).

🔴 Regra #1 — este repositório é frequentemente usado por MUITOS AGENTS EM VÁRIAS ABAS

git status frequentemente exibe arquivos modificados de outra sessão. Por isso:

  • NÃO git add -a / git commit -a / git add . — isso vai recolher trabalho inacabado de outra sessão no seu commit.
  • Stage SELETIVO de cada arquivo seu, depois verifique: git diff --cached --name-only — certifique-se de que não há arquivos estranhos (os mais comuns: web/src/pages/ChatPage.tsx, backend/internal/db/dbgen/models.go se não foi sua regeneração).
  • Arquivos compartilhados entre duas sessões (ex. ExamPage) e você só quer seu hunk: não use stash/reset (isso tira o arquivo do outro agente). Edite o index do Git via blob: git show :path > tmp → remova os hunks deles de tmp → SHA=$(git hash-object -w tmp) → git update-index --cacheinfo 100644,$SHA,path. O arquivo no disco não é tocado.
  • 🚨 A ferramenta cd do Bash pode PERSISTIR de uma chamada anterior (ex. de cd .../web ou worktree). Então git add backend/... FALHA com pathspec did not match (ou STAGE a árvore errada). Antes do stage seletivo, cd <repo-root> && primeiro (ou use caminho absoluto). Verifique git diff --cached --name-only exibe caminhos relativos à raiz corretos (não ../backend, src/...).
  • Após o deploy: verifique git status — certifique-se de que os arquivos de trabalho de outra sessão ainda estão intactos.

Portão de qualidade Go (antes do commit backend)

Equivalente ao Pint + PHPStan do Laravel para Go. Execute localmente antes de stagear arquivos backend:

  • Formatação: cd backend && gofmt -l . → deve estar VAZIO. Se houver saída → gofmt -w . (auto-correção), stage o resultado. (Equivalente a pint --test.)
  • Build + vet: go build ./... && go vet ./... verde.
  • Análise estática (target): golangci-lint run ./... (engloba gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell). Equivalente a phpstan analyse.
  • ⚠️ Status: portão CI ainda não ativo. Ainda não há backend/.golangci.yml nem .github/workflows/ci.yml, e staticcheck está desatualizado → por enquanto, format+build+vet manual. Plano de enforce no CI + config completa: docs/plan/00-tooling-quality-gate.md. Código gerado internal/db/dbgen excluído do lint.
  • Testes acompanham as mudanças → skill afresto-testing (go test ./...; integração pulada sem TEST_DATABASE_URL).

🔴 Regra #00 — trabalho SIGNIFICATIVO vai para uma GitHub issue PRIMEIRO (desde 5 Set 2026)

Válida antes da Regra #0 (branch + PR): antes de tocar em qualquer código.

O objetivo não é formalidade. Pedidos que chegam por conversa são conhecidos apenas por duas pessoas — o time não consegue lê-los, o dono não tem onde atribuir, e o módulo não deixa rastro de quem está fazendo o quê.

Quando é OBRIGATÓRIO criar issue

Nova funcionalidade · mudança que será vista pela escola · alteração no schema do BD · qualquer coisa que exija decisão de produto · trabalho que gere mais de um PR.

Quando NÃO é necessário

Bug claro e pontual · correção de texto/rótulo · documentação · acompanhamento direto de comentário de revisão de PR.

Em dúvida → crie a issue. É barato, e pode ser fechada. Esse limite mantém a regra viva: se toda solicitação trivial virar issue, as pessoas vão começar a ignorá-la silenciosamente, e uma vez ignorada sem consequência, a regra morre.

O que o agente faz

  1. Explore o código o suficiente para preencher "descobertas iniciais" e encontrar bloqueadores — não chute.
  2. Crie a issue usando o template .github/ISSUE_TEMPLATE/pekerjaan.md, coloque o rótulo modul:* (é isso que forma o registro por módulo), mais butuh-keputusan se houver algo pendente.
  3. PARE. Não code, não crie branch, não se atribua a tarefa. Atribuição é ação do dono; o time também precisa de espaço para dar feedback/revisar ideias.
  4. Informe o número da issue ao dono, junto com as decisões ainda pendentes (se houver).

🔑 O dono pode ignorar a regra dizendo "faça agora" — a issue ainda é criada como registro, o trabalho segue. Esta regra define o comportamento padrão, não prende ninguém.

Bloqueador descoberto NO MEIO do trabalho

Avise antes de continuar: comente na issue (para o time ver) e comunique ao dono. Se a decisão interromper o trabalho → adicione o rótulo butuh-keputusan. Não escolha silenciosamente sua própria interpretação e siga em frente — isso gera trabalho que "está pronto" mas errado.

Fechando o ciclo

O PR deve obrigatoriamente conter Closes #<número> no corpo. É isso que conecta issue → PR → pessoa, para que gh issue list --label modul:ujian --state all se torne o registro de quem fez o quê naquele módulo. Sem essa linha, o rastro se perde.

🔴 Regra #0 — NÃO faça commit/push direto em main (time de 4 pessoas, desde 25 Jul 2026)

Todo trabalho via branch + Pull Request; apenas revisores fazem merge para main (merge = gatilho de deploy).

  1. Antes de começar: git checkout main && git pull — se houver novas migrações, execute a migração primeiro.
  2. Crie/checkout uma branch temática ANTES de tocar no código:
    • funcionalidade → feat/<nome> · bug → bugfix/<nome> · bug com issue → nova branch + estude a issue.
  3. Commit seletivo → git push -u origin <branch> (NÃO para main).
  4. Abra um PR para main: gh pr create se gh estiver logado; senão, dê ao usuário a URL https://github.com/Afresto-Next/next/compare/main...<branch>?expand=1.
  5. Revisor aprova & faz merge → deploy. NÃO faça merge do seu próprio PR.
  • A seção "Push backend / Deploy WEB" abaixo = mecânica que ocorre APÓS o merge do PR em main (geralmente por quem está encarregado do deploy), não é permissão para push direto em main.
  • Mudanças na skill também passam por PR (CODEOWNERS).

Sequência de etapas

1. Commit (Ferramenta Bash = Git Bash / sh, NÃO PowerShell)

  • Mensagem multilinha: heredoc git commit -q -F - <<'EOF' … EOF. NÃO use @'…'@ (isso é here-string do PowerShell → o assunto fica poluído com @ ...).
  • Finalize a mensagem: Co-Authored-By: Claude Opus 4.8 <[email protected]>.
  • Aviso "CRLF will be replaced by LF" = normal, ignore.
  • Commit na sua branch de trabalho (feat/* · bugfix/*), não em main (veja Regra #0). O usuário commita quando solicitado.

🔴 NÃO use git add -A / git add <dir>/ — já removeu arquivos do usuário 3 vezes

Este repositório armazena muitos arquivos de trabalho do usuário não rastreados: docs/bskap/ (imagens+PDF), docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (cache de conta). Uma vez com -A, tudo vai junto.

Casos reais: git add docs/ arrastou PDF/xls/imagens do BSKAP (25 Jul & 2 Ago); git add -A arrastou 446 arquivos incluindo wrangler-account.json (9 Ago).

git add path/para/arquivo1 path/para/arquivo2        # UM POR UM, sempre
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l   # confere com o que você pretendia?

Se a quantidade estiver errada: git reset --soft HEAD~1 && git reset, refaça o stage corretamente. Se já tiver dado push para sua própria branch, git push --force-with-lease é seguro.

2. Migração do BD — OBRIGATÓRIO estar no commit TOPO

.github/workflows/migrate.yml detecta migrações via git diff --name-only HEAD~1 HEAD (apenas o commit mais recente), executado após "Build & Push Docker Images" com sucesso (workflow_run).

  • Se o push contiver migração → a migração deve estar no commit TOPO (um commit com migração+código = seguro).
  • A migração é executada automaticamente no VM runner self-hosted após o build. run --rm migrate sincroniza → o job FALHA GRAVEMENTE se houver erro (não silencioso).
  • Verifique a migração: veja a aba Actions → "Migrate DB" verde para aquele commit (CLI gh está disponível: gh run list --workflow="Migrate DB (self-hosted)" --limit 1 — o agente pode verificar sozinho, sem pedir ao usuário). Watchtower troca a imagem api na próxima verificação (~5 min), quase sempre após a migração terminar.
  • ANTES de push de migração, verifique o SQL via psql BEGIN; … ROLLBACK; no BD local (v102 = produção). Veja a skill afresto-db-change / armadilha-engenharia §4.

🔴 Número da migração: verifique novamente IMEDIATAMENTE ANTES DO MERGE, não ao criar a branch

Colegas de time podem ter feito merge de outra série de migrações enquanto sua branch estava aberta. Números duplicados fazem goose ENTRAR EM PÂNICO em sortAndConnectMigrations — não é um erro SQL, então é fácil de interpretar mal. ✅ Sem danos aos dados: o pânico ocorre ao coletar a lista, antes de qualquer uma ser executada.

Caso real (9 Ago 2026): escolheu 00138 ao criar a branch; na hora do merge, o time já estava usando 00138–00140. A primeira correção nomeou como 00140 e ainda colidiu — porque chutou novamente. Conte, não chute:

git pull                                        # OBRIGATÓRIO primeiro, para ver as migrações dos colegas
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d   # DEVE ESTAR VAZIO

Se já tiver acontecido: git mv para o novo número, PR pequeno, merge, e execute novamente o workflow "Migrate DB (self-hosted)" da aba Actions.

3. Push do backend (APÓS o merge do PR em main — veja Regra #0)

Merge do PR → main atualizado → GHCR → Watchtower ~5 min troca a imagem. Sem alterações no backend = não precisa esperar o Watchtower. (Ao desenvolver: git push -u origin <branch> depois PR — não push em main.)

⚠️ APÓS o merge do backend: VERIFIQUE 502 (já ocorreu 6x)

Recriar o container api/worker frequentemente causa uma paralisação total — todos os containers Up mas todas as rotas 502. Na prática, todo merge de backend é um jogo de dados.

curl -s -o /dev/null -w "%{http_code}
" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = SAUDÁVEL (pede autenticação) · 502 = paralisação

Se 502 → Actions → "Ops — pulihkan API" → restart-docker (sem SSH, recupera em <1 minuto). Não analise a funcionalidade recém-implantada: 502 + container Up = reiniciar o daemon Docker. Detalhes: docs/ops-auto-pulih.md.

4. Deploy do WEB — a partir de WORKTREE LIMPO (por causa de muitas abas de agentes)

npm run build lê TODO o working tree → trabalho inacabado de outra sessão é publicado junto. Portanto, construa e faça deploy a partir de um worktree no seu commit:

rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-do-seu-commit>
cd /c/wtd && git status --short   # deve estar VAZIO (limpo)
cd /c/wtd/web && npm ci && npm run build
cd /c/wtd/cloudflare/web-worker && npx wrangler deploy
cd <repo-projectTwo> && git worktree remove --force /c/wtd && git worktree prune
  • 🔥 O caminho do worktree DEVE SER CURTO (/c/wtd) — espaço temporário + nome longo do PDF kaldik DKI → erro no Windows "Filename too long" durante checkout.
  • Verifique o bundle correto, se necessário: grep -rl "<marcador da sua funcionalidade>" /c/wtd/web/dist/assets/*.js — mas cuidado com falso positivo (marcadores comuns podem vir de outras funcionalidades).
  • Cloudflare cacheia assets por ~20 s → teste com Ctrl+F5. (200 mas tamanho ~397 bytes = fallback, não o asset real.)

5. Mobile OTA — repositório separado, --environment OBRIGATÓRIO

cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
  • 🔥 --environment OBRIGATÓRIO (preview/production) — sem ele, a atualização vai para o environment errado / falha.
  • O repositório mobile tem remote GitHub (Afresto-Next/afresto-next-mobile) → aplica-se a Regra #0: branch → PR → merge, não faça push direto em main.
  • OTA é apenas JS/assets. Mudanças em módulo nativo → CRASH se for via OTA → obrigatório recompilar.

🔥 eas update envia o WORKING TREE, não o commit

Exatamente como npm run build no passo 4 — todo o diretório de trabalho é empacotado, incluindo trabalho inacabado de outra sessão. O caractere * na linha Commit <hash>* no resultado da publicação = árvore suja.

OBRIGATÓRIO imediatamente antes de publicar — imprima e depois confira com a linha Commit no resultado:

echo "publicar de: $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main   # VAZIO = nenhum trabalho de outra pessoa ficou para trás
  • Publique a partir de main, não de uma branch de funcionalidade. De uma branch, o trabalho de outras pessoas que já foi mergeado DESAPARECE do canal — OTA substitui todo o bundle, não aplica patches.
  • Produção: isole primeiro as alterações não-commitadas de outra sessão (git stash push -- <arquivos> … git stash pop). Produção não pode levar código não revisado.

🚨 A branch pode MUDAR no meio do trabalho (muitas abas de agentes, um clone)

Caso 20 Ago 2026: publicação preview claramente de main @ d0938fa; o comando seguinte constatou que HEAD já estava em outra branch @ 8126a50 — outra sessão mudou de branch no mesmo clone. Produção chegou a executar código de 3 PRs a mais, sem nenhum erro.

➡️ Não presuma que a branch continua a mesma entre comandos. Imprima novamente a cada vez (comando acima). ➡️ Prevenção real: OTA a partir de WORKTREE, igual ao deploy web no passo 4 — outra sessão não pode mudar sua branch:

rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-do-seu-commit>
cd /c/wtm && git status --short          # deve estar VAZIO
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune

🪤 Caminho do worktree curto (/c/wtm) — mesma razão do passo 4. 🪤 npm ci no worktree leva tempo; para OTA pequeno, pode ficar no clone principal desde que a verificação de branch+commit acima seja executada e o resultado coincida.

🏢 GHCR & organização GitHub — repositório agora na org Afresto-Next

Os repositórios (next, afresto-next-mobile, afresto-claude-skills) foram movidos de ristology/* → org Afresto-Next (20 Jul 2026). Runbook de transferência completo: docs/runbook-transfer-next-org.md.

  • 🚨 Transferir repositório para org = caminho do GHCR muda de dono → Watchtower CONGELA SEM AVISO. CI tagueia imagem como ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate} — o dono segue o proprietário do repositório. Se

...[truncated for translation]

Habilidades Relacionadas