ci: reuso por tree — uma suíte por versão do código no PR para main (TH-68150) #42

Merged
tarcisio merged 1 commit from feat/TH-68150-reuso-por-tree into develop 2026-10-05 06:39:46 -03:00
Owner

O que muda (TH-68150 — uma suíte por versão do código)

Hoje uma release roda a suíte completa no PR feature→develop e de novo no PR develop→main, sobre o mesmo conteúdo. Este PR adiciona o passo "Reuso por tree (PR para main)" no job changes do ci.yml (scripts/reuso_por_tree.py):

  1. calcula git rev-parse HEAD^{tree};
  2. candidatos: os últimos 30 commits de origin/develop (inclui o 2º pai dos merges = head do PR) + o head dos últimos 30 PRs fechados e mergeados com base ≠ main (cobre squash/rebase, via refs/pull/<n>/head);
  3. só os candidatos com o mesmo tree são consultados na API (/commits/<sha>/status, token do job);
  4. serve se: status combinado success, CI / ci (pull_request) success, e CI / changes|gates|backend|frontend (pull_request) presentes, success e sem "skipped" na descrição. Os contextos (push) (smoke da develop) não valem;
  5. se servir: backend e frontend ficam NÃO SE APLICA — reaproveitado do commit (tree idêntico), e o CI / ci fecha verde. Se não servir: roda tudo.
  • gates não muda e roda SEMPRE (Trivy, pip/npm audit, release checks): não existe "verde por ausência" de CVE.
  • Falha segura: API fora, 500, git falhando, status ausente ou qualquer exceção → reuso=false; o script sai 0 sempre, e o passo tem || echo reuso=false. Só reuso=true explícito pula alguma coisa.
  • Ligado por padrão só no PR com base main. PR para develop/epic/**/release/** nunca reaproveita (e nem consulta a API). O push em main já era modo nenhum; o push em develop segue smoke.
  • Desligável: reuso_por_tree: false no ci-config.yml (ou input reuso_por_tree). Schema, ci_config.py, exemplo e README atualizados.

Fato medido que moldou o desenho

Na API do Forgejo 15.0.9, commit.tree.sha (/git/commits/<sha> e /commits) devolve o SHA do commit, não o do tree (conferido em 04/10/2026 contra git log --format=%T no próprio ci-shared: 330d27c… → API 330d27c…, git 50caa07…). Por isso o tree é calculado localmente.

Prova

tests/ci-unico/test_reuso_por_tree.py (19 testes) — ponta a ponta: repo git real (origin bare + clone no commit do PR para main) e API do Forgejo simulada em http.server; as saídas alimentam o run: real dos gates de backend/frontend.

cenário resultado
tree igual + status success reaproveita; backend/frontend NÃO SE APLICA com o sha
tree igual + status failure roda
API indisponível (porta fechada) / API de status 500 roda
PR para develop / epic / release roda, sem nenhuma chamada à API
controle negativo: tree diferente com status verde roda
só contexto (push) (smoke) / job "Has been skipped" / sem status roda
reuso_por_tree: false, modo legado, smoke roda
squash merge (head só em refs/pull/7/head) reaproveita; PR não mergeado ou para main não conta
contrato: gates não referencia reuso; gates só pulam com reuso=true exato ok

Suíte local (python3 -m unittest discover -s tests/<suíte>):

  • ci-config 88 OK · ci-unico 62 OK · hub-issue-sync OK · infisical-secrets 9 OK · nginx-log 6 OK · soberania 13 OK
  • release: 35 falhas + 1 erro idênticos na origin/develop sem esta mudança (rodado num git archive limpo da develop; ambiente macOS — frente INFRA-68242). Este PR não toca tests/release nem os scripts que ela testa.
  • 2 testes existentes ajustados por contrato: test_outputs_do_changes_cobrem_o_schema passa a listar reuso* como saídas do passo reuso (não da config).

Limites

  • Não há job agregador ci no reusável: o CI / ci é o job do chamador; a mensagem "reaproveitado…" sai no log/notice/resumo do changes, backend e frontend.
  • PR docs-only no candidato: o status do Forgejo não distingue backend "não se aplica (docs-only)" de backend que testou. Se o candidato foi um PR docs-only, o reuso se apoia em o código não-docs já ter passado num PR anterior (vale com branch rebaseada, que o preflight exige). Merge não rebaseado gera tree novo → sem match → roda.
  • O contexto esperado é CI / … (pull_request), amarrado ao name: CI do chamador idêntico; produto com chamador diferente (TMF7/TMF9) nunca acha candidato → roda tudo.
  • Depende do token do job ler /pulls e /commits/<sha>/status do próprio repo; se não puder, cai na falha segura (roda tudo).
  • Não validado ainda num run real do Forgejo — o primeiro PR para main de um produto depois do merge é a prova de campo.

Economia estimada

Por release, o PR develop→main deixa de rodar backend (9–17 min segundo o próprio ci.yml, INFRA-68352; 18–19 min medidos no Priceshow) e frontend (~1 min medido no tennis-tracking; teto 20 min): ~10–20 min por release, no runner único. changes (+ alguns segundos do passo novo) e gates continuam.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Uab9wJsQ19HdJHwYi2uVip

## O que muda (TH-68150 — uma suíte por versão do código) Hoje uma release roda a suíte completa no PR feature→develop e de novo no PR develop→main, sobre o **mesmo conteúdo**. Este PR adiciona o passo **"Reuso por tree (PR para main)"** no job `changes` do `ci.yml` (`scripts/reuso_por_tree.py`): 1. calcula `git rev-parse HEAD^{tree}`; 2. candidatos: os últimos 30 commits de `origin/develop` (inclui o 2º pai dos merges = head do PR) **+** o head dos últimos 30 PRs fechados e mergeados com base ≠ main (cobre squash/rebase, via `refs/pull/<n>/head`); 3. só os candidatos com **o mesmo tree** são consultados na API (`/commits/<sha>/status`, token do job); 4. serve se: status combinado `success`, `CI / ci (pull_request)` success, e `CI / changes|gates|backend|frontend (pull_request)` presentes, success e **sem "skipped"** na descrição. Os contextos `(push)` (smoke da develop) não valem; 5. se servir: `backend` e `frontend` ficam **NÃO SE APLICA — reaproveitado do commit <sha> (tree idêntico)**, e o `CI / ci` fecha verde. Se não servir: roda tudo. - **`gates` não muda e roda SEMPRE** (Trivy, pip/npm audit, release checks): não existe "verde por ausência" de CVE. - **Falha segura:** API fora, 500, git falhando, status ausente ou qualquer exceção → `reuso=false`; o script sai 0 sempre, e o passo tem `|| echo reuso=false`. Só `reuso=true` explícito pula alguma coisa. - **Ligado por padrão só no PR com base `main`.** PR para `develop`/`epic/**`/`release/**` nunca reaproveita (e nem consulta a API). O push em `main` já era modo `nenhum`; o push em `develop` segue smoke. - **Desligável:** `reuso_por_tree: false` no `ci-config.yml` (ou input `reuso_por_tree`). Schema, `ci_config.py`, exemplo e README atualizados. ### Fato medido que moldou o desenho Na API do Forgejo 15.0.9, `commit.tree.sha` (`/git/commits/<sha>` e `/commits`) devolve o **SHA do commit**, não o do tree (conferido em 04/10/2026 contra `git log --format=%T` no próprio ci-shared: `330d27c…` → API `330d27c…`, git `50caa07…`). Por isso o tree é calculado **localmente**. ## Prova `tests/ci-unico/test_reuso_por_tree.py` (19 testes) — ponta a ponta: repo git real (origin bare + clone no commit do PR para main) e API do Forgejo simulada em `http.server`; as saídas alimentam o `run:` real dos gates de backend/frontend. | cenário | resultado | |---|---| | tree igual + status success | reaproveita; backend/frontend NÃO SE APLICA com o sha | | tree igual + status failure | roda | | API indisponível (porta fechada) / API de status 500 | roda | | PR para develop / epic / release | roda, sem nenhuma chamada à API | | controle negativo: tree diferente com status verde | roda | | só contexto `(push)` (smoke) / job "Has been skipped" / sem status | roda | | `reuso_por_tree: false`, modo legado, smoke | roda | | squash merge (head só em `refs/pull/7/head`) | reaproveita; PR não mergeado ou para main não conta | | contrato: `gates` não referencia reuso; gates só pulam com `reuso=true` exato | ok | Suíte local (`python3 -m unittest discover -s tests/<suíte>`): - `ci-config` 88 OK · `ci-unico` 62 OK · `hub-issue-sync` OK · `infisical-secrets` 9 OK · `nginx-log` 6 OK · `soberania` 13 OK - `release`: 35 falhas + 1 erro **idênticos na `origin/develop` sem esta mudança** (rodado num `git archive` limpo da develop; ambiente macOS — frente INFRA-68242). Este PR não toca `tests/release` nem os scripts que ela testa. - 2 testes existentes ajustados por contrato: `test_outputs_do_changes_cobrem_o_schema` passa a listar `reuso*` como saídas do passo `reuso` (não da config). ## Limites - **Não há job agregador `ci` no reusável**: o `CI / ci` é o job do chamador; a mensagem "reaproveitado…" sai no log/notice/resumo do `changes`, `backend` e `frontend`. - **PR docs-only no candidato:** o status do Forgejo não distingue backend "não se aplica (docs-only)" de backend que testou. Se o candidato foi um PR docs-only, o reuso se apoia em o código não-docs já ter passado num PR anterior (vale com branch rebaseada, que o preflight exige). Merge não rebaseado gera tree novo → sem match → roda. - O contexto esperado é `CI / … (pull_request)`, amarrado ao `name: CI` do chamador idêntico; produto com chamador diferente (TMF7/TMF9) nunca acha candidato → roda tudo. - Depende do token do job ler `/pulls` e `/commits/<sha>/status` do próprio repo; se não puder, cai na falha segura (roda tudo). - Não validado ainda num run real do Forgejo — o primeiro PR para main de um produto depois do merge é a prova de campo. ## Economia estimada Por release, o PR develop→main deixa de rodar `backend` (9–17 min segundo o próprio `ci.yml`, INFRA-68352; 18–19 min medidos no Priceshow) e `frontend` (~1 min medido no tennis-tracking; teto 20 min): **~10–20 min por release**, no runner único. `changes` (+ alguns segundos do passo novo) e `gates` continuam. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Uab9wJsQ19HdJHwYi2uVip
ci: reuso por tree — uma suíte por versão do código no PR para main (TH-68150)
All checks were successful
PR checks (ci-shared) / testes (pull_request) Successful in 2m34s
d5d3eaedc8
No PR para main, o job changes procura um commit recente (últimos 30 da
develop + head dos PRs mergeados) com o MESMO tree git e o CI completo
verde (CI / ci e os 4 jobs executados, contexto pull_request). Se achar,
backend e frontend ficam NÃO SE APLICA com 'reaproveitado do commit <sha>
(tree idêntico)'. O gates (Trivy, audits, release) roda sempre.

Falha segura: API fora, status ausente, job pulado ou qualquer erro =
roda tudo; o script sai 0 sempre. PR para develop/epic/release nunca
reaproveita. Desliga com reuso_por_tree: false no ci-config.

O tree é calculado localmente: a API do Forgejo 15.0.9 devolve o SHA do
commit em commit.tree.sha (medido em 04/10/2026).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uab9wJsQ19HdJHwYi2uVip
tarcisio merged commit 97f4e665b5 into develop 2026-10-05 06:39:46 -03:00
tarcisio deleted branch feat/TH-68150-reuso-por-tree 2026-10-05 06:39:46 -03:00
Sign in to join this conversation.
No reviewers
No labels
seguranca
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
TMF/ci-shared!42
No description provided.