feat(project): auto-add issues + reconcile workflows para nfe/5#20
feat(project): auto-add issues + reconcile workflows para nfe/5#20
Conversation
- templates/auto-add-to-project.yml: workflow syncado para cada repo (dispara em issues.opened/reopened/transferred e adiciona a issue ao Project Work - Product & Engineering / nfe/5) - .github/workflows/sync-auto-add-workflow.yml: provisioner diario que abre PR em cada repo da org pra instalar/atualizar o workflow - .github/workflows/reconcile-project-items.yml: reconciler horario que encontra issues orfas (abertas na org e ausentes do Project #5) e as adiciona automaticamente Requer secret organization-level WORK_PROJECT_TOKEN (PAT com scopes project + repo + workflow) visivel para todos os repos alvo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds organization-wide automation to ensure newly created/reopened/transferred GitHub Issues are always included in the central Project Work - Product & Engineering (nfe/5), plus a scheduled reconciler and a daily workflow provisioner to keep the per-repo workflow synced.
Changes:
- Introduces a reusable workflow template to auto-add issues to Project nfe/5 on
issuesevents. - Adds a daily provisioner workflow that opens PRs across all non-archived org repos to sync the template into
.github/workflows/auto-add-to-project.yml. - Adds an hourly reconciler workflow that backfills “orphan” open issues missing from Project nfe/5.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
templates/auto-add-to-project.yml |
Template workflow to add issues to org Project nfe/5 on issue lifecycle events. |
.github/workflows/sync-auto-add-workflow.yml |
Scheduled/manual provisioner that syncs the template into each repo by opening PRs. |
.github/workflows/reconcile-project-items.yml |
Scheduled/manual reconciler that finds open issues not in the Project and adds them. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| on: | ||
| schedule: | ||
| - cron: '0 * * * *' | ||
| workflow_dispatch: |
There was a problem hiding this comment.
Sem concurrency, execuções sobrepostas (cron + manual) podem processar o mesmo diff ao mesmo tempo e duplicar trabalho/consumir rate limit. Sugestão: adicionar concurrency.group para serializar execuções e, opcionalmente, timeout-minutes para limitar o impacto operacional em caso de lentidão de API.
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| add-to-project: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
O template define permissions no nível do workflow. Como este arquivo será copiado para muitos repositórios, vale manter o padrão do repo (permissões no nível do job) para facilitar futuras extensões/ajustes sem afetar outros jobs. Sugestão: mover permissions: contents: read para dentro de jobs.add-to-project.permissions.
| permissions: | |
| contents: read | |
| jobs: | |
| add-to-project: | |
| runs-on: ubuntu-latest | |
| jobs: | |
| add-to-project: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read |
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| sync: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
Este workflow define permissions no nível do workflow. No restante do repositório, as permissões parecem ser definidas no nível do job (ex.: .github/workflows/validate-node.yml), o que facilita ajustar permissões por job quando o arquivo crescer. Sugestão: mover permissions: contents: read para dentro de jobs.sync.permissions para manter consistência.
| permissions: | |
| contents: read | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read |
| Ao merge, toda nova issue aberta neste repositorio sera automaticamente adicionada ao Project [Work - Product & Engineering (nfe/5)](https://github.com/orgs/nfe/projects/5). | ||
|
|
||
| **Requisitos:** | ||
| - Secret organization-level `WORK_PROJECT_TOKEN` (PAT com scope `project`) visivel para este repositorio |
There was a problem hiding this comment.
O corpo do PR gerado indica que o secret WORK_PROJECT_TOKEN precisa apenas do scope project, mas o token descrito no PR precisa também de repo (para issues em repositórios privados) e de workflow/repo para o provisioner conseguir commitar em .github/workflows. Sugestão: alinhar o texto de requisitos no pr_body para evitar configurar um PAT insuficiente e o auto-add falhar.
| - Secret organization-level `WORK_PROJECT_TOKEN` (PAT com scope `project`) visivel para este repositorio | |
| - Secret organization-level `WORK_PROJECT_TOKEN` visivel para este repositorio, com scopes `project`, `repo` (necessario para issues em repositorios privados) e `workflow`/`repo` para o provisioner conseguir commitar em `.github/workflows` |
| on: | ||
| schedule: | ||
| - cron: '0 6 * * *' | ||
| workflow_dispatch: |
There was a problem hiding this comment.
Sem concurrency, uma execução manual pode sobrepor a execução agendada (ou duas execuções manuais), e ambas podem tentar resetar o branch chore/auto-add-to-project-sync e abrir PRs em paralelo. Sugestão: definir concurrency.group (ex.: por workflow) para serializar as execuções e evitar corridas/duplicidade.
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| reconcile: |
There was a problem hiding this comment.
Este workflow também define permissions no nível do workflow. Para manter consistência com os demais workflows do repo, considere mover para o nível do job (ex.: jobs.reconcile.permissions).
| permissions: | |
| contents: read | |
| jobs: | |
| reconcile: | |
| jobs: | |
| reconcile: | |
| permissions: | |
| contents: read |
| PROJECT_ID: PVT_kwDOAID6As4BUp17 | ||
| DRY_RUN: ${{ inputs.dry_run || 'false' }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
There was a problem hiding this comment.
PROJECT_ID está hardcoded como um Node ID. Se o Project for recriado/migrado, esse ID muda e o reconciler quebra silenciosamente até alguém atualizar o YAML. Sugestão: buscar o project ID via GraphQL a partir de org=nfe + project number=5 (ou ler de uma org/repo var) para reduzir manutenção.
| PROJECT_ID: PVT_kwDOAID6As4BUp17 | |
| DRY_RUN: ${{ inputs.dry_run || 'false' }} | |
| run: | | |
| set -euo pipefail | |
| PROJECT_ORG: nfe | |
| PROJECT_NUMBER: 5 | |
| DRY_RUN: ${{ inputs.dry_run || 'false' }} | |
| run: | | |
| set -euo pipefail | |
| PROJECT_ID=$(gh api graphql \ | |
| -f query=' | |
| query($org: String!, $number: Int!) { | |
| organization(login: $org) { | |
| projectV2(number: $number) { | |
| id | |
| } | |
| } | |
| }' \ | |
| -F org="$PROJECT_ORG" \ | |
| -F number="$PROJECT_NUMBER" \ | |
| --jq '.data.organization.projectV2.id') | |
| if [ -z "$PROJECT_ID" ] || [ "$PROJECT_ID" = "null" ]; then | |
| echo "::error::Nao foi possivel resolver o ProjectV2 ID para ${PROJECT_ORG}/${PROJECT_NUMBER}" | |
| exit 1 | |
| fi |
| mapfile -t repos < <(gh api 'orgs/nfe/repos?per_page=100&type=all' --paginate --jq '.[] | select(.archived == false) | .name') | ||
| echo "Repos nao-arquivados: ${#repos[@]}" | ||
| for repo in "${repos[@]}"; do | ||
| gh api --paginate "repos/nfe/$repo/issues?state=open&per_page=100" \ | ||
| --jq '.[] | select(.pull_request == null) | .node_id' >> "$all_open" 2>/dev/null || true | ||
| done |
There was a problem hiding this comment.
O reconciler faz pelo menos 1 chamada REST por repositório (com paginação) a cada hora (repos/.../issues dentro do loop). Isso pode estourar rate limit/tempo de execução conforme o número de repos/issues cresce. Sugestão: substituir por uma única busca paginada via search/issues (org-wide) ou GraphQL search para coletar issues abertas, reduzindo chamadas por execução.
| mapfile -t repos < <(gh api 'orgs/nfe/repos?per_page=100&type=all' --paginate --jq '.[] | select(.archived == false) | .name') | |
| echo "Repos nao-arquivados: ${#repos[@]}" | |
| for repo in "${repos[@]}"; do | |
| gh api --paginate "repos/nfe/$repo/issues?state=open&per_page=100" \ | |
| --jq '.[] | select(.pull_request == null) | .node_id' >> "$all_open" 2>/dev/null || true | |
| done | |
| gh api --paginate "search/issues?q=org:nfe+is:issue+is:open+archived:false&per_page=100" \ | |
| --jq '.items[].node_id' > "$all_open" |
Contexto
Issues criadas pela UI do GitHub hoje não entram automaticamente no Project Work - Product & Engineering (nfe/5) — só issues criadas via skill
/create-issue(que fazaddProjectV2ItemByIdexplicitamente). Isso gera issues órfãs, que ficam rastreadas no repo mas fora do Project central.Required Workflows (feature original de 2023) foi descontinuada. Rulesets (seu sucessor) só cobre eventos de PR/push — não cobre
issues.opened. Logo, não há mecanismo GitHub nativo que force um workflow a rodar org-wide no evento de issue. A solução é sincronizar o workflow pra cada repo + reconciler como safety-net.O que este PR adiciona
Três arquivos, nenhuma mudança em código existente:
1.
templates/auto-add-to-project.yml(template de sync)Workflow que será copiado pra cada repo. Dispara em
issues: [opened, reopened, transferred]e usaactions/add-to-project@v1pra adicionar a issue ao Project #5. Delay típico <30s.2.
.github/workflows/sync-auto-add-workflow.yml(provisioner)Roda diariamente 06:00 UTC + manual via
workflow_dispatch. Pra cada repo não-arquivado da org:chore/auto-add-to-project-sync, commita o template, abre PR contramain/default branch do repoInputs do dispatch manual:
dry_run(default false): lista o que faria sem abrir PRlimit(default 0): max repos a processar por execução (0 = todos)3.
.github/workflows/reconcile-project-items.yml(reconciler)Roda a cada hora + manual. Encontra issues abertas na org ausentes do Project #5 e adiciona. Safety-net pra: (a) repos ainda sem workflow syncado, (b) falhas transitórias do workflow primary, (c) issues transferidas / reabertas fora de evento.
Apenas adiciona ao Project — não seta fields (Priority, Status, etc. ficam vazios, pra preenchimento manual/Triage).
Arquitetura
Criar e expor secret organization-level
WORK_PROJECT_TOKEN:Criar PAT em https://github.com/settings/tokens com scopes:
repo— pra commit e abrir PR nos repos alvo (provisioner)workflow— pra commitar arquivos em.github/workflows/(provisioner)project— praaddProjectV2ItemById(runtime e reconciler)Adicionar como secret org-level em https://github.com/organizations/nfe/settings/secrets/actions:
WORK_PROJECT_TOKENnfe/.github)Sem esse secret, o workflow
actions/add-to-project@v1falha silenciosamente e os PRs do provisioner não abrem.Plano de rollout recomendado
Actions → Sync auto-add-to-project workflow → Run workflow → dry_run=true— valida a lista de repos sem efeito colaterallimit=3, dry_run=false— valida o fluxo de PR em 3 reposNotas operacionais
.github/workflows/auto-add-to-project.yml. É o trade-off da Arquitetura B (sem Workers hospedado).Test plan
WORK_PROJECT_TOKENcriado e visível org-widedry_run=true— lista de repos confere comgh api orgs/nfe/reposlimit=3— 3 PRs abertos corretamente em 3 reposdry_run=true— contagem de órfãs bate com query manual🤖 Generated with Claude Code