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

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions