Skip to content

Migrar tooling de qualidade para Ruff #22

Description

@gitnnolabs

Descrição da tarefa

Substituir a configuração de qualidade de código e testes por um pyproject.toml na raiz do projeto, adotando Ruff como ferramenta única de lint e formatação, e migrando as opções hoje em pytest.ini para [tool.pytest.ini_options].

Estado atual

Arquivos Situação
pytest.ini Configuração pytest na raiz (DJANGO_SETTINGS_MODULE, python_files, addopts = --reuse-db)
pyproject.toml Existe apenas em apps/spsvalidator/ (black, isort, pytest)
requirements/local.txt pytest, pytest-django, pytest-cov pinados
CI (.github/workflows/ci.yml) Job linter vazio (pre-commit comentado); job tests executa manage.py test e pytest
Makefile Targets test, test-fast, test-cov via Docker + pytest
Regra Cursor .cursor/rules/python-black-isort.mdc referencia pyproject.toml na raiz, que ainda não existe

Objetivo

  • pyproject.toml na raiz como fonte única de configuração para Ruff e pytest.
  • Ruff substitui black + isort (lint + format, line-length 88, regras alinhadas ao Django).
  • Ruff / Bandit (S) detecta credenciais e senhas hardcoded no código, SQL injection, pickle inseguro, YAML/load perigoso e outras más práticas de segurança estáticas.
  • pytest permanece como runner de testes (pytest-django, fixtures, pytest.mark.django_db em tests/test_smoke.py e apps/spsvalidator/tests/); apenas a configuração deixa de viver em pytest.ini.
  • CI e Makefile expõem comandos reproduzíveis (ruff check, ruff format --check).

Nota de escopo: Ruff não executa testes. Esta issue trata da infraestrutura de configuração e qualidade estática, não da remoção do pytest como runner. Unificar manage.py test + pytest num único runner fica fora do âmbito (issue separada, se desejado).

Âmbito

Área Acção
Raiz do repo Criar pyproject.toml com [tool.ruff], [tool.ruff.lint], [tool.ruff.lint.flake8-bandit], [tool.ruff.format], [tool.pytest.ini_options]
pytest.ini Remover após migração equivalente em pyproject.toml
Dependências Adicionar ruff a requirements/local.txt (ou grupo dev documentado); avaliar remoção de black/isort onde forem substituídos
apps/spsvalidator/pyproject.toml Alinhar: trocar [tool.black] / [tool.isort] por Ruff; manter [tool.pytest.ini_options] local coerente
CI Activar job linter com ruff check . e ruff format --check . (excluir dist/, build/, migrations geradas, se aplicável)
Makefile Targets lint / format / lint-fix (via container ou documentação para host)
Documentação Atualizar README.md, docs/desenvolvimento-local-make.md
Regras Cursor Substituir python-black-isort.mdc por regra Ruff + pyproject.toml

Fora do âmbito

  • Reescrever testes para deixar de usar pytest.
  • Eliminar o runner legado python manage.py test do CI (mantém-se até decisão explícita).
  • Introduzir pre-commit hooks (pode ser follow-up).
  • Alterar lógica de negócio ou apps Django.

Regras de segurança no Ruff (Bandit)

O conjunto S (flake8-bandit) cobre análise estática de segurança. Não substitui validadores de senha em runtime do Django (AUTH_PASSWORD_VALIDATORS), mas impede que segredos e más práticas entrem no código.

Regra O que detecta
S105 String com senha/token hardcoded (password = "...", api_key = "...")
S106 Argumento de função com senha hardcoded (connect(password="secret"))
S107 Valor default de parâmetro com senha hardcoded (def f(password="x"))
S608 Expressão SQL construída por concatenação/f-string (risco de injection)
S301 / S302 Uso de pickle / cPickle (deserialização insegura)
S506 yaml.load sem Loader seguro
S501 requests/urllib sem verificação de certificado SSL
S113 Chamadas HTTP sem timeout
S104 Bind em 0.0.0.0 (expor serviço a todas as interfaces)
S110 try/except: pass que oculta falhas de segurança

Boas práticas complementares já no Ruff

Conjunto Uso
B (bugbear) Padrões propensos a bugs (B904 raise sem chain, etc.)
UP (pyupgrade) Sintaxe moderna e APIs deprecadas
T20 (opcional) print em código de produção (útil em apps web; ignorar em scripts CLI)

Excepções controladas

  • Testes podem usar strings fictícias (password="pytest-pass"); aplicar per-file-ignores mínimos em tests/** e **/test_*.py apenas para S105–S107, não para o conjunto S inteiro.
  • Fixtures e settings de teste (config/settings/test.py) devem preferir variáveis de ambiente ou valores obviamente fictícios; evitar ignorar regras em config/settings/base.py ou production.py.

Configuração de referência (proposta)

Valores iniciais sugeridos para revisão na implementação:

[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "config.settings.test"
pythonpath = ["."]
python_files = ["tests.py", "test_*.py", "*_tests.py"]
addopts = ["--reuse-db"]

[tool.ruff]
line-length = 88
target-version = "py311"
exclude = [".git", ".venv", "dist", "build", "node_modules", "staticfiles"]

[tool.ruff.lint]
select = [
    "E", "F", "I", "UP", "B",
    "S",
]
ignore = [
    "S101",
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S105", "S106", "S107"]
"**/test_*.py" = ["S105", "S106", "S107"]
"apps/spsvalidator/tests/**" = ["S105", "S106", "S107"]

[tool.ruff.lint.flake8-bandit]
check-typed-exception = true

[tool.ruff.lint.isort]
known-first-party = ["config", "manuscripts", "users"]

[tool.ruff.format]
quote-style = "double"

Ajustar known-first-party, exclude, per-file-ignores e regras Django (DJ do ruff-django, se adoptado) conforme estrutura real dos apps. Revisar violações S608 e S501 no código existente antes de activar o CI bloqueante.

Subtarefas

  • Criar pyproject.toml na raiz com secções [tool.pytest.ini_options], [tool.ruff] (lint + format) e [tool.ruff.lint.flake8-bandit].
  • Activar conjunto S (Bandit) com foco em senhas/credenciais (S105, S106, S107) e regras de injection/deserialização (S608, S301, S506).
  • Definir per-file-ignores para testes (apenas S105–S107) e documentar justificativa; não ignorar segurança em config/settings/.
  • Verificar paridade com pytest.ini actual (DJANGO_SETTINGS_MODULE, python_files, --reuse-db) e remover pytest.ini.
  • Adicionar ruff às dependências de desenvolvimento (requirements/local.txt e/ou documentação de instalação).
  • Actualizar apps/spsvalidator/pyproject.toml: Ruff em vez de black/isort; remover dependências dev obsoletas do optional dev.
  • Activar job linter em .github/workflows/ci.yml com ruff check e ruff format --check.
  • Adicionar targets lint, format e lint-fix ao Makefile (ou equivalente documentado para Docker).
  • Correr Ruff no código existente e corrigir violações bloqueantes de segurança (credenciais hardcoded, SQL dinâmico, pickle, YAML inseguro) e estilo.
  • Auditar config/settings/base.py: confirmar que segredos vêm de env (django-environ); nenhuma chave SECRET_KEY, senha de BD ou token em literal no repositório.
  • Actualizar README.md e docs/desenvolvimento-local-make.md (comandos de lint/format; CI passa a incluir Ruff).
  • Actualizar .cursor/rules/python-black-isort.mdc para Ruff + pyproject.toml na raiz.
  • Smoke test local: ruff check ., ruff format --check ., make test / pytest inalterados em comportamento.

Critérios de aceite

  • Existe pyproject.toml na raiz; pytest.ini foi removido.
  • pytest continua a passar com a mesma suíte (raiz + apps/spsvalidator), sem alteração de comportamento dos testes.
  • ruff check . e ruff format --check . passam no CI (job linter), incluindo regras S sem falsos positivos não documentados.
  • Nenhuma credencial real detectável por S105–S107 fora de testes (ou excepções justificadas em per-file-ignores).
  • apps/spsvalidator não depende de black/isort para desenvolvimento quotidiano.
  • Documentação local descreve como correr lint, format e testes antes de abrir PR.
  • Nenhuma referência obsoleta a black/isort como padrão do monorepo (excepto histórico em PRs antigos).

Como testar manualmente

  1. Instalar dependências de desenvolvimento (ambiente local ou container Django).
  2. Na raiz:
    ruff check .
    ruff format --check .
    ruff check --select S105,S106,S107,S608 .
  3. Confirmar que pytest lê config do pyproject.toml:
    docker compose -f local.yml run --rm django pytest --collect-only -q
  4. Executar suíte completa:
    make django_test
    make test
  5. Em apps/spsvalidator:
    cd apps/spsvalidator && pip install -e ".[dev]" && pytest -q

Considerações e notas

  • Ruff vs pytest: Ruff cobre lint e format; pytest continua responsável por descoberta e execução de testes. A “troca” pedida é da infraestrutura de configuração (pytest.ini + black/isort implícitos) para pyproject.toml + Ruff.
  • Monorepo: apps/spsvalidator tem pyproject.toml próprio (app standalone). Manter coerência de line-length e estilo entre raiz e subprojeto.
  • Docker: Decidir se Ruff corre no host (mais rápido no CI linter) ou no container django (paridade com pytest). Recomendação: host no job linter (já tem setup-python); opcional target Make no container para quem só usa Docker.
  • Primeira execução: Esperar diff grande só de formatação Ruff; pode ser commit dedicado style: apply ruff format para facilitar revisão.
  • Ruff vs validadores Django: AUTH_PASSWORD_VALIDATORS em config/settings/base.py (já com os quatro validadores padrão do Django) regula senhas de utilizadores em runtime. O Ruff S105–S107 regula segredos no código-fonte. As duas camadas são complementares; esta issue não altera política de comprimento mínimo de senha de utilizador (follow-up se quiserem MinimumLengthValidator com OPTIONS: {"min_length": 12}).
  • S101 ignorado globalmente: assert é comum em testes; ignorar só S101, não todo o conjunto S.
  • Follow-up opcional: pre-commit com ruff-check + ruff-format; unificar runners de teste; adoptar ruff-django para regras específicas do framework; pip-audit ou dependabot para CVEs em dependências (fora do Ruff).

Referências

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions