Skip to content

feat: ingestão de contribuições do GitHub e retrospectiva da comunidade#304

Merged
danielhe4rt merged 38 commits into
4.xfrom
feat/community-retrospective
Jun 7, 2026
Merged

feat: ingestão de contribuições do GitHub e retrospectiva da comunidade#304
danielhe4rt merged 38 commits into
4.xfrom
feat/community-retrospective

Conversation

@Clintonrocha98
Copy link
Copy Markdown
Member

@Clintonrocha98 Clintonrocha98 commented Jun 3, 2026

O que este PR faz

image image

Hoje a comunidade não tem um jeito automático de saber quem está contribuindo nos repositórios públicos. Este PR cria essa base: o sistema passa a guardar as contribuições do GitHub (pull requests, reviews, issues, comentários e commits) e a mostrar tudo numa apresentação de retrospectiva — no espírito do "Quem fez a He4rt bater".

Como funciona, em linguagem simples

1. De onde vêm os dados

  • Histórico: com um clique no painel (ou um comando), o sistema busca tudo o que já aconteceu num repositório e guarda — desde o primeiro registro do projeto.
  • Tempo real: quando alguém abre um PR, comenta ou faz um commit, o GitHub avisa o sistema na hora e a contribuição é registrada sozinha.

2. Quais repositórios contam

  • Um administrador escolhe, pelo painel, quais repositórios entram na conta. Adicionar um novo é só cadastrar — sem mexer em código.

3. Quem aparece

  • Todo mundo que contribui é contado, seja membro cadastrado ou não.
  • Robôs (bots) ficam de fora.
  • PRs que não foram aceitos ainda contam como participação, mas aparecem separados dos que foram aceitos — pra dar crédito ao esforço sem confundir com o que de fato entrou.

A apresentação (deck)

A retrospectiva é uma apresentação em tela cheia, navegável por teclado (← →) ou pelas setas/bolinhas do rodapé, sem o "chrome" do portal. A sequência conta uma história: capa → panorama → núcleo → comunidade → repositórios → destaques → encerramento.

  • Capa animada: coração da He4rt pulsando, o logo da He4rt ao fundo com um LED percorrendo as linhas e a linha do batimento (ECG) se desenhando na entrada do slide.
  • Cards de contribuidor: cada pessoa tem uma barra de composição (o "DNA" da participação — a proporção de PRs, reviews, issues, comentários e commits por cor) e os tipos de atividade com ícone ("Abriu PR", "Revisou", "Abriu issue", "Comentou", "Commitou").

Filtros (aplicados ao vivo)

Um painel de filtros controla o que a apresentação mostra, e tudo fica refletido na URL — dá pra compartilhar um recorte já pronto:

  • Período: datas livres ou presets — semana, mês ou tudo (que cobre desde a primeira contribuição real, respeitando os repositórios selecionados).
  • Tipo de contribuição: ligar/desligar PR, review, issue, comentário e commit.
  • Repositórios: filtrar por um ou vários repos.
  • Desfecho de PR: todos, só aceitos (merged), só abertos ou só fechados sem merge.
  • Pessoa: focar numa pessoa específica.
  • Bots: mostrar ou ocultar (ocultos por padrão).
  • Ordenação: por total de interações, por PRs ou por linhas de código.

O que muda pra quem usa

  • Para o admin: uma tela nova ("Repositórios") pra escolher o que entra e um botão "Backfill agora" pra puxar o histórico. Se o GitHub limitar as requisições, aparece um aviso claro pedindo pra tentar de novo depois (e nada é perdido — dá pra continuar de onde parou).
  • Para a comunidade: a apresentação de retrospectiva, filtrável por período e escopo.

Substitui a versão provisória

A primeira tentativa era um comando solto, rodado na mão, que jogava um JSON num link temporário. Isso foi removido e trocado por essa base que vive dentro da arquitetura do projeto, guarda os dados de verdade e se atualiza sozinha.

Para funcionar em produção (configuração)

Precisa configurar duas chaves (GITHUB_API_TOKEN e GITHUB_WEBHOOK_SECRET) e registrar o webhook na organização do GitHub. Sem isso, o histórico pelo painel ainda funciona; só o tempo real fica desligado.

Nota: ainda em draft — falta a configuração das chaves/webhook no ambiente e uma passada final de revisão.

@Clintonrocha98 Clintonrocha98 requested a review from a team June 3, 2026 17:56
Copy link
Copy Markdown

@fernanduandrade fernanduandrade left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(comentário do contexto provisório anterior — removido)

Substitui a retrospectiva provisória (comando que rodava `gh` na mão e subia
um JSON para um link temporário) por uma ingestão persistente que respeita a
arquitetura modular do projeto.

- integration-github: modelo próprio de contribuições (desacoplado de
  Interaction/gamification), lake bruto de eventos espelhando o do Discord,
  backfill REST resiliente e ciente de rate limit (helper RateLimit
  compartilhado), e webhook ao vivo com verificação de assinatura HMAC.
- panel-admin: allowlist de repositórios gerenciável + ação "Backfill agora"
  com notificações amigáveis (rate limit / falha) sem marcar sucesso falso.
- portal: página pública de retrospectiva por período (Livewire), contando
  todos os contribuidores, excluindo bots e distinguindo PRs mergeados dos
  fechados sem merge.

A gamificação fica como seam (evento GithubContributionRecorded), sem
dependência direta entre os módulos.
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@he4rt he4rt deleted a comment from coderabbitai Bot Jun 5, 2026
@Clintonrocha98 Clintonrocha98 changed the title feat: comando de retrospectiva semanal da comunidade feat: ingestão de contribuições do GitHub e retrospectiva da comunidade Jun 5, 2026
@Clintonrocha98 Clintonrocha98 marked this pull request as draft June 5, 2026 13:19
O painel admin é multi-tenant, então tratar a allowlist e as contribuições
como globais quebrava ao paginar (o model não tinha relação `tenant`). Agora
cada comunidade tem o seu recorte, consistente com o resto do sistema.

- github_repositories e github_contributions carregam tenant_id; as uniques
  passam a (tenant_id, full_name) e (tenant_id, repo, type, external_ref).
- ProjectGithubEvent faz fan-out: uma entrega do webhook vira uma contribuição
  por tenant que acompanha o repo; o lake github_event_logs segue global.
- BackfillRepository::execute recebe o GithubRepository e carimba o tenant_id
  do repo de origem.
- A retrospectiva do portal filtra por tenant, resolvido por slug de rota
  (/comunidade/{tenant}/retrospectiva) com default em config('he4rt.main_tenant').
- GithubRepositoryResource volta a ser tenant-scoped; a validação de unicidade
  do form é escopada ao tenant atual.

Contribuição de repo compartilhado é duplicada por tenant — trade-off aceito em
favor do isolamento total entre comunidades.
@Clintonrocha98 Clintonrocha98 force-pushed the feat/community-retrospective branch from dda959b to fb4ee60 Compare June 6, 2026 00:59
@danielhe4rt
Copy link
Copy Markdown
Contributor

🔐 Configuração de produção — token, scopes e ENVs

Pra ligar a ingestão do GitHub em produção precisa de 2 segredos (+1 opcional) e registrar o webhook na org. Sem isso, o backfill pelo painel ainda funciona (autenticado pelo token); só o tempo real (webhook) fica desligado.

1. GITHUB_API_TOKEN — backfill (REST)

Usado pelo GitHubApiConnector como Authorization: Bearer <token> para puxar PRs, reviews, issues, comentários e commits dos repos da allowlist. Sem ele as requisições vão sem auth e o GitHub limita a 60 req/h (estoura rate limit rápido); com ele, 5.000 req/h.

Onde gerar:

  • Fine-grained (recomendado — read-only): Settings → Developer settings → Personal access tokens → Fine-grained tokenshttps://github.com/settings/tokens?type=beta
    • Resource owner: a org he4rt (pode exigir aprovação da org).
    • Repository access: os repositórios públicos que entram na allowlist (ou "All repositories").
    • Permissions (todas Read-only):
      • Metadata: Read (obrigatório)
      • Pull requests: Read
      • Issues: Read
      • Contents: Read (endpoint de commits)
  • Classic (alternativa): https://github.com/settings/tokens → escopo public_repo. Use repo apenas se algum dia rastrear repositório privado.

2. GITHUB_WEBHOOK_SECRET — tempo real (webhook)

HMAC X-Hub-Signature-256 verificado pelo middleware VerifyGithubSignature. Fail-safe: se estiver vazio, o webhook é rejeitado com HTTP 500 e nada é gravado.

Gerar um segredo forte:

openssl rand -hex 32

Use o mesmo valor no .env e na config do webhook (passo 4).

3. HE4RT_MAIN_TENANT (opcional, default he4rt)

Tenant padrão da rota pública /comunidade/retrospectiva quando nenhum slug é informado. Só preencha se o slug do tenant principal for diferente de he4rt.

ENVs (já presentes no .env.example, linhas 109–110)

GITHUB_API_TOKEN=github_pat_xxx     # fine-grained (ou ghp_xxx classic)
GITHUB_WEBHOOK_SECRET=xxx           # mesmo valor configurado no webhook
# HE4RT_MAIN_TENANT=he4rt           # opcional

⚠️ Não confundir com o OAuth de login (GITHUB_OAUTH_CLIENT_ID / GITHUB_OAUTH_CLIENT_SECRET) — é outra configuração e não é necessária para esta feature.

4. Registrar o webhook (org ou repositório)

Org he4rt → Settings → Webhooks → Add webhook:

  • Payload URL: https://<dominio>/api/webhooks/github
  • Content type: application/json
  • Secret: o valor de GITHUB_WEBHOOK_SECRET
  • Events: "Let me select individual events"
    pull_request, pull_request_review, issues, issue_comment, pull_request_review_comment, push
  • Active:

Depois de configurar

  1. Cadastrar os repos no painel (admin → cluster GitHub → Repositórios) e clicar "Backfill agora" (ou php artisan github:backfill owner/repo).
  2. Rodar npm run build (entrypoint retrospective.css do deck).

Reviews PENDING (rascunho não enviado) não têm submitted_at — a API do GitHub
lista o rascunho do próprio usuário do token. Gravar occurred_at = "" estourava
um QueryException (invalid input syntax for type timestamp) e abortava o backfill.
Agora o backfill e o webhook pulam esses reviews. Bug latente, exposto por dados reais.
O backfill faz centenas de requests REST e estourava o timeout no request HTTP
do Livewire (cURL error 28). Agora roda em segundo plano.

- BackfillGithubRepository (ShouldQueue, ShouldBeUniqueUntilProcessing): executa o
  backfill no worker; resumível por idempotência; em rate limit faz release() até o
  reset (não conta como falha); retryUntil 6h + maxExceptions 3 + timeout 600s.
- painel "Backfill agora" agora ENFILEIRA o job e volta na hora (notifica "enfileirado").
- RateLimit::secondsUntilReset() para re-agendar o job até o reset.
- comando github:backfill segue síncrono (CLI não tem timeout).

Requer fila assíncrona + worker (prod=redis). Testes: job (release/stamp) + painel (dispatch).
…CLI)

- BackfillRepository::execute ganha um callback opcional de progresso, chamado por
  contribuição gravada com o ContributionType (seam agnóstico de UI). Job e painel
  passam null → comportamento idêntico.
- github:backfill refinado com Laravel Prompts (intro/table/outro/warning) + barra
  de progresso ao vivo por repo, com contador por tipo (PRs · reviews · issues · …).
- testes: callback chamado por contribuição (na ordem); comando segue cobrindo
  rate-limit (FAILURE + sem stamp).
Banco zerado / nenhuma contribuição ingerida mostrava 3 slides com tudo "0".
Agora, quando não há contribuição alguma para o tenant (count(repoOptions) === 0),
o deck renderiza um único slide: coração + "Métricas indisponíveis" + CTA pra
reunião semanal (discord.gg/he4rt). O deck entra em modo `bare` (sem progress,
FAB de filtros nem navbar). Gatilho é "sem dado nenhum" (não a janela), então uma
semana parada com histórico segue caindo no deck normal com filtros.
Por padrão o backfill agora é incremental: com last_backfilled_at preenchido, só
busca o que mudou desde (last_backfilled_at − 1 dia). Sem ele (1ª run) ou com
--full, varre o histórico inteiro. Mata a varredura completa toda vez.

- /issues, /issues/comments, /pulls/comments, /commits → query `since` (server-side)
- /pulls (a API não tem `since`) → sort=updated&direction=desc + early-stop no
  paginate quando updated_at < corte; só os PRs recentes fazem GetPullRequest+reviews
- execute(repository, onProgress, full=false); comando ganha --full; painel/job
  ficam incrementais por padrão (1º backfill é completo, cliques seguintes rápidos)
- params `since` validados na doc oficial do GitHub via context7
gvieira18
gvieira18 previously approved these changes Jun 6, 2026
gvieira18
gvieira18 previously approved these changes Jun 7, 2026
danielhe4rt
danielhe4rt previously approved these changes Jun 7, 2026
Copy link
Copy Markdown
Contributor

@danielhe4rt danielhe4rt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Falta uns toques de UX mas por agora tá ótimo!

Copy link
Copy Markdown

@sirelves sirelves left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

davicbtoliveira
davicbtoliveira previously approved these changes Jun 7, 2026
Copy link
Copy Markdown
Member

@davicbtoliveira davicbtoliveira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown

@erikmazzelli erikmazzelli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

YuriSouzaDev
YuriSouzaDev previously approved these changes Jun 7, 2026
Copy link
Copy Markdown
Contributor

@YuriSouzaDev YuriSouzaDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

GabrielFVDev
GabrielFVDev previously approved these changes Jun 7, 2026
@WesleiRamos
Copy link
Copy Markdown

LGTM

…s no backfill

O contador "X ingeridas" contava todo updateOrCreate (re-upserts inclusos),
então re-rodar o backfill reportava as mesmas ~81 "contribuições" mesmo sem
nada novo. Agora record() propaga wasRecentlyCreated e o comando separa
"novas" de "atualizadas" — re-run mostra "0 novas · N atualizadas".

Feedback ao vivo: cada item ingerido vira uma linha (Item · Autor · Quando ·
Status) rolando acima de uma barra fixa, via ConsoleSectionOutput. describe()
resolve o alvo (target_ref) pra comentário/review mostrarem "→ PR #312" em vez
do id cru. humanTime() dá o tempo relativo ("há 3 horas") pra noção de quão
recente o backfill alcançou.
…ectiva

Os cards de contribuidor quebravam quando uma pessoa tinha muito mais
contribuições que a outra: o grid 2-col deixava gaps enormes. Reorganiza os
dois slides de pessoas:

- "O núcleo" (top 5): carrossel horizontal de cards ricos, sem o #1 destacado.
  Arrasto com o mouse (drag-to-scroll) com inércia, igual no celular; scroll
  nativo no touch. Setas e fade só aparecem quando há overflow. Sobrescreve o
  scroll-behavior:smooth global no trilho (deixava o arrasto borrachudo) e
  desliga seleção de texto / drag-fantasma da imagem.
- "E toda a comunidade" (cauda): cards compactos e uniformes (header + barra +
  ícones com somatória por tipo, sem títulos) empilhados em grid de 3-4 colunas
  com altura natural — dispensa o carrossel.
- Cap nos refs de PR/issue: mostra os 3 mais recentes + "mais X…"; o agregador
  ordena os refs por occurred_at desc.

Testes: ordenação dos refs, cap "mais X…" na view e card compacto (ícones +
somatória, sem títulos).
@danielhe4rt danielhe4rt dismissed stale reviews from GabrielFVDev, YuriSouzaDev, davicbtoliveira, gvieira18, and themself via 6f1f76e June 7, 2026 20:08
@danielhe4rt danielhe4rt merged commit 6b75912 into 4.x Jun 7, 2026
6 checks passed
@danielhe4rt danielhe4rt deleted the feat/community-retrospective branch June 7, 2026 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants