Skip to content

[FEAT] Ciclo: descoberta, digest de refs e o workflow_call que a executa #221

Description

@cpenaforte-clickbus

User story

Como operador da coleta, eu quero descobrir quais repositórios mudaram desde a última análise por um
workflow que eu possa despachar, para saber o tamanho do trabalho antes de clonar qualquer coisa.

Acceptance criteria

scripts/iris-cycle.sh — a descoberta

  • Query GraphQL devolve, para 100 repositórios de uma vez, o pushedAt da organização
  • git ls-remote --heads por repositório monta o digest de refs, sem clonar e sem consumir cota de API
  • 16 leituras em paralelo (medido: 29,7 s para 608 repositórios, sem uma rejeição); 8 declarado como fallback
  • O digest segue o contrato, exatamente: v1:$(git ls-remote --heads "$URL" | LC_ALL=C sort | sha256sum | cut -d' ' -f1)
  • Falha ao montar o digest salta o repositório, conta a falha e segue — analisar sem poder gravar
    o watermark deixaria aquele repositório pendente para sempre
  • Ao final, imprime o retrato de refs da organização — repositório, pushedAt e digest — mais
    as falhas. Não clona, não analisa, não faz push, e não compara contra o watermark: dizer o que
    está pendente exige a rota de leitura do watermark, e isso é trabalho da issue de seleção.

.github/workflows/iris-ingestion-worker.yml

  • on: workflow_call como único gatilho — sem schedule, sem push, sem workflow_dispatch
  • Inputs app-id, iris-server-url, organization (opcional, default vazio)
  • Secrets IRIS_WORKER_APP_KEY e IRIS_INGEST_TOKEN declarados obrigatórios, não inherit
  • runs-on: ubuntu-latest, timeout-minutes: 60, permissions: contents: read
  • Checkout do próprio commit por job.workflow_repository + job.workflow_sha
  • Token do App cunhado dentro do job que o usa, com owner: inputs.organization || github.repository_owner
  • Clone via credential.helper lendo do ambiente — nunca token embutido na URL do remote
  • Todo uses: pinado por SHA de commit

O quatro escolhas do digest, cada uma com um motivo

refs/heads/ — tag não traz commit para analisar, e os deploys do DORA vêm do Datadog. LC_ALL=C na
ordenação, para que o locale da imagem do runner não mude o resultado. sha256, que o runner já tem. E
o prefixo v1:, para que uma revisão futura da normalização vire v2: e a reanálise da organização
inteira apareça como decisão, em vez de um dia em que tudo é reanalisado sem explicação.

A definição do digest é contrato, não detalhe de implementação: qualquer mudança nela invalida de
uma vez todos os digests já gravados.

Nada aqui nomeia uma organização

O App, a URL da plataforma e a organização a analisar entram por input, e a organização cai em
github.repository_owner, que numa reusable workflow é o dono de quem chamou. Rodar esta coleta noutra
organização é apontar outro caller para o mesmo arquivo. Regra 6 do CLAUDE.md.

Por que descoberta e workflow na mesma issue

Um workflow_call que referencia um scripts/iris-cycle.sh inexistente falha no step. Juntos, entregam
algo completo e útil desde o primeiro merge: um workflow despachável que reporta o retrato de refs da
organização — que é, também, exatamente o insumo que o censo da Fase 4 publica.

Se encaixa em qual estágio?

Stage 2 — Ingestion: reliability of CLI → /api/ingest pipeline. A leitura centralizada da API do
GitHub reabre o non-goal de integrações externas do CLAUDE.md; a nota de governança da §2.2 do DD
registra que a exceção pedida é a mesma já concedida ao Datadog.

Links

O engine vem do commit do próprio worker

Os steps do job, além do checkout já descrito:

  • actions/setup-python com python-version: "3.11"
  • pip install .sem release, sem wheel e sem resolver versão

Isso amarra engine, script de ciclo e definição do workflow ao mesmo commit por construção: um ciclo
nunca mistura duas versões do engine, e o workflow_dispatch fixa os três juntos ao escolher o ref.
A versão do pacote continua indo para analysis_runs.cli_version, e o commit fica no log do run.

Token fora do log

  • A action create-github-app-token mascara o token e o revoga no post-step
  • O clone usa credential.helper lendo do ambiente — nunca token embutido na URL do remote

O que fica declarado, e não mitigado

checkout, setup-python e create-github-app-token rodam no mesmo job que a chave do App. Pinar
cada uses: por SHA impede que uma tag passe a apontar para outro código, mas não protege de uma
versão que já seja maliciosa
. Fica declarado em vez de mitigado, porque a alternativa seria manter
cópia própria de cada action.

Limitação conhecida do digest: PR vindo de fork

A branch de origem vive no fork, então o ls-remote do repositório não a vê — um PR de fork não muda o
digest. Alcança só os 3 repositórios públicos da organização, e o maior deles recebe push diário, o
que o corrige no ciclo seguinte. Se passar a pesar, a descoberta acrescenta a contagem de PRs abertos,
que cabe na query que já roda.

Duas credenciais, e nenhuma de banco

  • A chave do App fala com o GitHub — clonar e ler PRs
  • O token de ingestão fala com a plataforma, pelas rotas de leitura do watermark e de escrita do censo

O Supabase continua acessível só pela plataforma. Dar credencial de banco ao CI ampliaria o raio sem
necessidade — é por isso que o watermark é lido por rota, e não por query direta.

O token de ingestão é por organização, e o /api/ingest grava tudo sob o organization_id dele —
é também o que isola um tenant do outro.

Um job por ciclo é um token por ciclo

O ciclo é um job só, e isso é restrição de desenho, não acaso: o token do App é gerado dentro do
job que o usa
e não atravessa job nenhum. O Actions não passa token por output, e passar significaria
guardá-lo — que é exatamente o que um token de uma hora existe para evitar.

  • O ciclo roda num único job; qualquer divisão futura em matrix precisa resolver a cunhagem por
    shard antes, não depois

Fallback declarado da leitura de refs: 8 leituras em paralelo, medido em 57 s contra os 29,7 s com 16.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions