diff --git a/CHANGELOG.md b/CHANGELOG.md index c82974d7..47a5dcfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). diff --git a/tests/test_permissoes_do_docs_yml.py b/tests/test_permissoes_do_docs_yml.py new file mode 100644 index 00000000..8f703013 --- /dev/null +++ b/tests/test_permissoes_do_docs_yml.py @@ -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 == {}