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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR

## [Unreleased]

### Adicionado

- Um teste confere as permissões do `docs.yml`, que nenhum check de PR executa: o workflow só concede `contents: read`, o job `deploy` tem exatamente `pages: write` e `id-token: write`, e nenhum outro job escreve. O PR que mexer nelas reprova no job de testes, antes do merge (#268).

### Alterado

- O `ruff check` do CI e do pre-commit confere também `example/`, com as regras de correção listadas em `example/ruff.toml` e sem as de estilo de biblioteca. Na célula que configura a chave, os notebooks de exemplo capturam só o `ImportError` de fora do Colab e as exceções que `userdata.get` levanta, e mostram a mensagem; antes, um `except:` com `pass` engolia também o secret ausente e o Ctrl+C (#211).
Expand Down
48 changes: 48 additions & 0 deletions tests/test_permissoes_do_docs_yml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Permissões do workflow que publica a documentação.

O docs.yml só roda em push na main e em workflow_dispatch, então nenhum check
de PR o executa: sem este teste, um PR que mexa nas permissões passa verde, e o
deploy só falha, ou ganha escrita que não precisa, depois do merge.
"""

from pathlib import Path

import yaml

DOCS_YML = Path(__file__).resolve().parents[1] / ".github" / "workflows" / "docs.yml"


def _workflow():
return yaml.safe_load(DOCS_YML.read_text(encoding="utf-8"))


def _escopos_com_escrita(permissoes):
# O bloco permissions aceita o mapa escopo -> nível ou um atalho em texto
# ("read-all", "write-all"); o atalho de escrita conta como todos os escopos.
if isinstance(permissoes, str):
return ["write-all"] if permissoes == "write-all" else []
return sorted(escopo for escopo, nivel in (permissoes or {}).items() if nivel == "write")


def test_workflow_so_concede_leitura_do_conteudo():
assert _workflow().get("permissions") == {"contents": "read"}


def test_job_deploy_tem_exatamente_escrita_no_pages_e_token_oidc():
deploy = _workflow()["jobs"]["deploy"]

assert deploy.get("permissions") == {"pages": "write", "id-token": "write"}


def test_nenhum_job_alem_do_deploy_escreve():
jobs = _workflow()["jobs"]
# Sem o build na lista, o laço abaixo poderia passar sem conferir nada.
assert "build" in jobs

escritas = {
nome: escopos
for nome, job in jobs.items()
if nome != "deploy" and (escopos := _escopos_com_escrita(job.get("permissions")))
}

assert escritas == {}
Loading