Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 114 additions & 20 deletions .github/workflows/doctor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Template drift audit
# Monthly audit — runs `precisa doctor` to detect drift between this
# repo's templated files (workflows, dotfiles, configs) and the current
# `@precisa-saude/cli` templates. When drift shows up, open a PR running
# `pnpm exec precisa sync` to absorb the changes.
# `precisa sync` to absorb the changes.
#
# Runs on the 1st of each month at 06:00 America/Sao_Paulo (09:00 UTC).
# `workflow_dispatch` is wired so you can trigger it ad-hoc.
Expand All @@ -13,6 +13,12 @@ name: Template drift audit
# in each repo. See fhir-brasil#26 (PAT_TOKEN → GitHub App) for the
# concrete failure mode — drift went undetected until a conditional
# job finally fired in production.
#
# Roda via `npx @precisa-saude/cli`, não via `pnpm exec`. O CLI não é
# devDependency de nenhum repo consumidor, então `pnpm exec precisa` saía
# "command not found" — não-zero, indistinguível de drift real. Todo repo
# com este workflow abriu uma issue de "drift" por mês sem nunca ter
# inspecionado um template. Ver a separação de exit codes abaixo.

on:
schedule:
Expand All @@ -30,42 +36,130 @@ jobs:
timeout-minutes: 10
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22
cache: 'pnpm'
- run: pnpm install --frozen-lockfile

# `npx --yes` dispensa o CLI como devDependency (nenhum repo o tem) e
# dispensa `pnpm install` — o doctor só lê `.precisa.json` e os templates
# que vêm dentro do próprio pacote.
#
# O exit code é capturado em vez de virar falha do step, para separar os
# dois casos que antes colidiam:
# 0 — sem drift
# 1 — drift encontrado (o doctor rodou e comparou)
# 2 — `.precisa.json` ausente ou inválido (nada comparado)
# qualquer outro — o audit não rodou (rede, versão quebrada, etc.)
# `PIPESTATUS[0]` pega o código do npx, não do `tee`.
- name: Audit templates
id: doctor
run: pnpm exec precisa doctor
continue-on-error: true
- name: Open issue on drift
if: steps.doctor.outcome == 'failure'
# `shell: bash` explícito: `PIPESTATUS` é bashismo. É o default em
# runners Linux, mas depender do default deixa o passo frágil se o
# workflow for reaproveitado em outro runner.
shell: bash
env:
NO_COLOR: '1'
run: |
set +e
npx --yes @precisa-saude/cli@latest doctor 2>&1 | tee "$RUNNER_TEMP/precisa-doctor.log"
echo "exit_code=${PIPESTATUS[0]}" >> "$GITHUB_OUTPUT"

# Falha barulhenta e visível quando a própria auditoria quebra. Antes
# isso virava uma issue mensal de "drift" — o modo de falha que fez o
# sinal perder credibilidade.
- name: Fail when the manifest is invalid
if: steps.doctor.outputs.exit_code == '2'
run: |
echo "::error title=.precisa.json inválido::O manifesto deste repo não valida contra o schema atual do CLI, então nenhum template foi comparado. Isto NÃO é drift — rodar 'sync' não resolve. Corrija os campos listados no log acima."
exit 1

- name: Fail when the audit could not run
if: steps.doctor.outputs.exit_code != '0' && steps.doctor.outputs.exit_code != '1' && steps.doctor.outputs.exit_code != '2'
run: |
echo "::error title=precisa doctor não rodou::exit ${{ steps.doctor.outputs.exit_code }} — isto NÃO é drift; a auditoria em si falhou."
exit 1

# Uma issue por repo, atualizada a cada rodada. A versão anterior criava
# uma issue nova todo mês, acumulando duplicatas até ninguém mais ler.
- name: Report drift
if: steps.doctor.outputs.exit_code == '1'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const fs = require('node:fs');
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const MARKER = '<!-- precisa-doctor-drift -->';

let log = '';
try {
// `RUNNER_TEMP` em vez de `/tmp`: é limpo por job e não colide
// com outro job no mesmo runner (relevante em self-hosted).
log = fs.readFileSync(`${process.env.RUNNER_TEMP}/precisa-doctor.log`, 'utf8').trim();
} catch {
log = '(log indisponível)';
}
// Cabe folgado no limite de 65536 chars do corpo de uma issue.
// Corta em fronteira de linha: cada linha do doctor é um arquivo,
// e cortar no meio produziria um nome de arquivo pela metade.
const LIMITE = 20000;
if (log.length > LIMITE) {
const corte = log.lastIndexOf('\n', LIMITE);
log = `${log.slice(0, corte > 0 ? corte : LIMITE)}\n… (truncado — ver o log completo do run)`;
}

const body = [
'`precisa doctor` detected drift between this repo and the canonical templates.',
MARKER,
'`precisa doctor` encontrou divergência entre este repo e os templates canônicos.',
'',
`Full log: ${runUrl}`,
'### Saída do doctor',
'',
'To fix:',
'```',
log,
'```',
'',
`Log completo do run: ${runUrl}`,
'',
'### Como resolver',
'',
'```bash',
'pnpm exec precisa sync --dry-run # review the diff',
'pnpm exec precisa sync # apply',
'npx @precisa-saude/cli@latest sync --dry-run # revisar o diff',
'npx @precisa-saude/cli@latest sync # aplicar',
'```',
'',
'Drift typically shows up when `@precisa-saude/cli` publishes',
'new templates (workflow changes, dotfile updates) and this',
'repo has not run `precisa sync` since.',
'> **Revise o `--dry-run` antes de aplicar.** Templates marcados',
'> `overwrite` são reescritos sem backup nem confirmação — se este',
'> repo tem customização deliberada num arquivo templatizado, o',
'> sync a descarta. Nesse caso, leve a mudança para o `tooling`',
'> em vez de rodar o sync.',
'',
`Atualizado em ${new Date().toISOString()} pelo run ${context.runId}.`,
].join('\n');
await github.rest.issues.create({

const title = 'precisa doctor: template drift detected';
const existing = await github.paginate(github.rest.issues.listForRepo, {
owner: context.repo.owner,
repo: context.repo.repo,
title: 'precisa doctor: template drift detected',
body,
labels: ['dependencies', 'tooling-drift'],
state: 'open',
labels: 'tooling-drift',
per_page: 100,
});
const open = existing.find((i) => !i.pull_request && (i.body ?? '').includes(MARKER));

if (open) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: open.number,
body,
});
core.notice(`Drift persiste — issue #${open.number} atualizada.`);
} else {
const { data } = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body,
labels: ['dependencies', 'tooling-drift'],
});
core.notice(`Drift detectado — issue #${data.number} aberta.`);
}
7 changes: 6 additions & 1 deletion .precisa.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@
"conduct": "conduct@precisa-saude.com.br"
},
"nodeVersion": "22",
"pnpmVersion": "9.15.9"
"pnpmVersion": "9.15.9",
"ignoreTemplates": [
".commitlintrc.cjs",
".github/workflows/_checks.yml",
".github/workflows/ci.yml"
]
}
48 changes: 5 additions & 43 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,46 +1,8 @@
cff-version: 1.2.0
title: 'datasus-parquet'
message: >-
Se você utilizar este arquivo em pesquisa, publicação ou software,
cite conforme abaixo. Cada release do dataset recebe um DOI
individual via Zenodo — troque o campo `doi` pelo DOI emitido para
a versão específica utilizada (visível em
https://zenodo.org/search?q=Precisa-Saude/datasus-parquet).
type: dataset
message: 'Se você utilizar este software, por favor cite-o conforme abaixo.'
type: software
authors:
- name: Precisa Saúde
website: https://precisa-saude.com.br
repository-code: https://github.com/Precisa-Saude/datasus-parquet
url: https://github.com/Precisa-Saude/datasus-parquet
license: CC-BY-4.0
keywords:
- datasus
- sus
- sia-sus
- brazil
- microdados
- open-data
- epidemiology
- parquet
- public-health
identifiers:
- type: doi
value: 10.5281/zenodo.PENDING
description: >-
Concept DOI (Zenodo) — placeholder. Preenchido após habilitar o
webhook GitHub ↔ Zenodo e publicar a primeira release.
abstract: >-
Arquivo público de microdados do DATASUS em formato Parquet, com
conversão 1:1 a partir dos DBC/DBF originais do FTP oficial.
Provenance com SHA256 permite validação byte-a-byte contra a fonte.
Atualmente cobre SIA-PA (Produção Ambulatorial, 2008+);
contribuições para outros subdatasets são bem-vindas.
references:
- type: data
title: SIA-SUS — Produção Ambulatorial (fonte original)
authors:
- name: 'Ministério da Saúde / DATASUS'
url: ftp://ftp.datasus.gov.br/dissemin/publicos/SIASUS/200801_/Dados/
notes: >-
Fonte oficial, distribuída sob regime de dados abertos
(Lei 12.527/2011 e Decreto 8.777/2016).
- name: 'Precisa-Saude'
repository-code: 'https://github.com/Precisa-Saude/datasus-parquet'
license: Apache-2.0
12 changes: 8 additions & 4 deletions renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@
"labels": ["dependencies"],
"prConcurrentLimit": 5,
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch"],
"matchDepTypes": ["devDependencies"],
"automerge": true,
"automergeType": "pr"
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"labels": ["dependencies", "major-update"]
},
{
"matchPackagePatterns": ["^@precisa-saude/"],
"groupName": "precisa-saude packages",
"automerge": true,
"automergeType": "pr"
"groupName": "precisa-saude packages"
},
{
"matchManagers": ["github-actions"],
Expand All @@ -30,6 +34,6 @@
],
"vulnerabilityAlerts": {
"labels": ["security"],
"automerge": false
"automerge": true
}
}
15 changes: 2 additions & 13 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2022"],
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["scripts/**/*.ts"]
"extends": "@precisa-saude/tsconfig/library",
"exclude": ["node_modules", "dist"]
}