Manifesto de fixação de dependências
Política de declaração de dependências que cobre as faixas de runtime do Python, as ferramentas de desenvolvimento, os arquivos de bloqueio das ferramentas de lançamento e os pisos de versão ratificados para cada superfície de build.
Postura ratificada de como o Apothem declara, fixa e bloqueia dependências. Público: contribuidores e consumidores a jusante que precisam entender o modelo de reprodutibilidade.
Postura
O Apothem é uma CLI em Python com um pequeno conjunto de dependências de
runtime, além de uma cadeia de ferramentas maior de desenvolvimento, auditoria,
lançamento e documentação. As dependências de runtime usam limites inferiores
conservadores em pyproject.toml; o site faz commit de
site/package-lock.json; as GitHub Actions são fixadas por SHA imutável com
comentários de versão e são atualizadas pelo Dependabot.
| Classe | Declaração | Política de bloqueio |
|---|---|---|
| Dependências Python de runtime | [project.dependencies] em pyproject.toml | Faixas com limite inferior para os usuários; nenhum bloqueio de runtime do Python acompanha o pacote. O CI instala um fechamento dessas dependências bloqueado por hash (veja Bloqueios de dependências do CI) |
| Ferramentas de desenvolvimento / segurança | extras de [project.optional-dependencies] | Faixas com limite inferior, exceto ruff e mypy, que o extra dev fixa exatamente nas versões que o CI executa (para o ruff, veja Bloqueios de ferramentas do CI); o CI instala o extra dev a partir de um fechamento bloqueado por hash (veja Bloqueios de dependências do CI) |
| Ferramentas de lançamento | scripts/release/requirements-build-linux.txt para os fluxos de trabalho de lançamento; o extra release em [project.optional-dependencies] para builds locais | Bloqueado por hash para os fluxos de trabalho de lançamento; gerado a partir do fechamento das ferramentas de lançamento. O extra release é uma faixa com limite inferior |
| pip do CI | scripts/ci/requirements-ci-pip.txt | Bloqueado por hash: uma única fixação pip==X.Y.Z com os hashes do wheel e do sdist, que todo fluxo de trabalho que atualiza o pip instala com --require-hashes -r. O Dependabot propõe as atualizações e reescreve os hashes; scripts/dev/check_pip_pin_drift.py mantém a fixação em pip 26.2 ou posterior |
| Bloqueios de dependências do CI | scripts/ci/requirements-ci-dev.txt, -runtime.txt, -test.txt e -prereqs.txt | Fechamentos universais bloqueados por hash, compilados com uv pip compile a partir de pyproject.toml (ou, para -prereqs.txt, dos pré-requisitos de host do motor) em uma data de bloqueio menos um período de espera de 7 dias, ambos registrados em cada arquivo, e instalados com --require-hashes -r (o próprio pacote com --no-deps -e .). O CI testa as versões bloqueadas até que scripts/dev/refresh_ci_locks.py as regenere; pip-audit.yml audita semanalmente as versões permitidas mais recentes e também os bloqueios. tests/scripts/test_ci_locks_fresh.py falha quando um bloqueio deixa de satisfazer suas entradas |
| Bloqueios de ferramentas do CI | scripts/ci/requirements-ci-ruff.txt, -bandit.txt, -pip-audit.txt, -pip-licenses.txt e -pre-commit.txt | Um fechamento bloqueado por hash para cada ferramenta que os fluxos de trabalho executam, compilado pelo mesmo script a partir de uma especificação exata em scripts/dev/ci_lock_table.py, com a mesma data de bloqueio e o mesmo período de espera, e instalado com --require-hashes -r. Nenhum fluxo de trabalho escreve uma versão de ferramenta. pre-commit precisa do Python 3.11 ou posterior, então o bloqueio dele também, e o bloqueio do pip-audit também é compilado para 3.11 porque o filelock mais recente com suporte a 3.10 tem alertas de segurança sem correção. Nenhum bloqueio além do pip do CI fixa o pip |
| Site de documentação | site/package.json | site/package-lock.json commitado e consumido pelo npm ci |
| Empacotador do VS Code | tools/vsce/package.json | tools/vsce/package-lock.json commitado; os jobs de lançamento e publicação executam npm ci --ignore-scripts |
| CLIs de harness do channel-smoke | tools/channel-smoke/package.json | tools/channel-smoke/package-lock.json commitado e consumido pelo npm ci |
| GitHub Actions | Referências uses: fixadas por SHA com comentários de versão | O Dependabot propõe atualizações semanais; as exceções carregam marcadores action-pinning-exempt: |
Ratificação de lançamento
| Superfície | Estado ratificado | Justificativa |
|---|---|---|
| Dependências de runtime | Os limites inferiores atuais ficam em pyproject.toml [project.dependencies] | Pisos estáveis atuais ratificados antes do lançamento; nenhum bloqueio conhecido por CVE de runtime |
| Ferramentas de desenvolvimento | Os limites inferiores atuais ficam no extra dev de pyproject.toml; ruff e mypy são as fixações exatas ali | Pisos estáveis mais recentes ratificados antes do lançamento; ruff e mypy são fixados cada um em uma única versão para os hooks, o CI e os contribuidores, o hook do mypy fixa as bibliotecas que lista nas versões do bloqueio dev, e tests/scripts/test_ci_tool_locks.py falha quando elas divergem |
| Ferramentas de segurança | Os limites inferiores atuais ficam no extra security de pyproject.toml | Pisos atuais de scanners ratificados antes do lançamento |
| Ferramentas de lançamento | O fechamento bloqueado por hash fica em scripts/release/requirements-build-linux.txt | O fluxo de trabalho de lançamento instala com --require-hashes; os scripts locais exigem que build já exista, em vez de instalar silenciosamente dependências em movimento |
| pip do CI | A fixação bloqueada por hash fica em scripts/ci/requirements-ci-pip.txt | O pip 26.2 ou posterior corrige a CVE-2026-13346; os fluxos de trabalho o instalam com --require-hashes, de modo que o download é verificado contra o hash publicado dele |
| Bloqueios de dependências do CI | Os fechamentos bloqueados por hash ficam em scripts/ci/requirements-ci-{dev,runtime,test,prereqs}.txt | Os fluxos de trabalho os instalam com --require-hashes; cada um registra a data de bloqueio, o período de espera e a data de corte, e o teste de atualidade os mantém em sincronia com pyproject.toml |
| Bloqueios de ferramentas do CI | Os fechamentos bloqueados por hash ficam em scripts/ci/requirements-ci-{ruff,bandit,pip-audit,pip-licenses,pre-commit}.txt | Os fluxos de trabalho os instalam com --require-hashes e não escrevem nenhuma versão de ferramenta; as especificações ficam em scripts/dev/ci_lock_table.py, e a data de bloqueio, o período de espera e a data de corte são os mesmos dos bloqueios de dependências |
| Ferramentas de documentação | As faixas atuais ficam em site/package.json; a resolução exata fica em site/package-lock.json | npm outdated não retorna nenhuma dependência de documentação desatualizada após a atualização do arquivo de bloqueio |
| GitHub Actions | As fixações por SHA existentes são mantidas para o lançamento atual; o fluxo de trabalho reutilizável do SLSA continua sendo a exceção documentada, fixada por tag, marcada com um comentário action-pinning-exempt: em release.yml | A reexecução do CI público está verde; atualizações amplas de versão major das Actions são deixadas de propósito para os pull requests de automação de dependências, para evitar mudanças desnecessárias na superfície de lançamento |
| Empacotamento npm | O fluxo de trabalho publish-npm.yml roda no push da tag v*.*.*, com workflow_dispatch como alternativa | O único pacote npm público é @ahmed-g-gad/apothem; a publicação aguarda a aprovação do operador para o ambiente release, concedida depois que scripts/release/check-release-ready.sh passa |
Bloqueio
Os arquivos de bloqueio do Python dos próprios contribuidores são gerados sob
demanda e não são commitados: o fechamento de runtime do Apothem é pequeno e a
CLI oferece suporte a várias versões menores do Python. O CI é a exceção. Ele
instala os arquivos bloqueados por hash em scripts/ci/: quatro bloqueios de
dependências (requirements-ci-dev.txt, -runtime.txt, -test.txt e
-prereqs.txt) e um bloqueio para cada ferramenta que os fluxos de trabalho
executam (-ruff.txt, -bandit.txt, -pip-audit.txt, -pip-licenses.txt e
-pre-commit.txt). python scripts/dev/refresh_ci_locks.py compila os nove em
uma data de bloqueio menos um período de espera de 7 dias, igual ao do
Dependabot, de modo que o CI testa as versões que tinham pelo menos uma semana
na data de bloqueio, e atualizar os arquivos as faz avançar. Cada arquivo
registra a data de bloqueio, o período de espera e a data de corte resultante,
e --check reproduz essa data de corte. Uma correção de segurança urgente mais
recente que o período de espera exige uma atualização com --cooldown-days 0,
ou uma exceção ao período de espera para o pacote dela em
scripts/dev/ci_lock_table.py, que remove o período de espera só para esse
pacote. A execução semanal de pip-audit.yml audita as versões mais recentes
que pyproject.toml permite, além dos nove bloqueios, consultando o serviço
OSV sobre alertas de segurança. Os contribuidores continuam instalando com
pip install -e ".[dev]".
# Astral uv:
uv pip install -e ".[dev,security,release]"
uv pip freeze > requirements.dev.lock
# pip-tools:
pip-compile --extra=dev --extra=security --extra=release -o requirements.dev.lock pyproject.toml
# Plain pip:
pip install -e ".[dev,security,release]"
pip freeze > requirements.dev.lockO site é diferente: site/package-lock.json é commitado e o CI usa npm ci,
de modo que o site de documentação renderizado tem um fechamento de Node
reprodutível.
Justificativa
- Por que fixações por faixa para o Python? A CLI oferece suporte a uma
ampla matriz de Python, então
pyproject.tomlmantém pisos conservadores. O CI instala fechamentos bloqueados por hash dessas faixas, que o mantenedor atualiza comscripts/dev/refresh_ci_locks.py(a cadência prevista é semanal), e a execução semanal depip-audit.ymlresolve as versões permitidas mais recentes para revelar problemas com as ferramentas atuais. - Por que um bloqueio de Node commitado? O site é um artefato estático
implantado; o arquivo de bloqueio fornece builds
npm cideterministas para essa superfície pública. - Por que Actions fixadas por SHA? As tags são mutáveis; os SHAs são imutáveis. O Dependabot propõe os incrementos de SHA e reescreve cada comentário de versão, enquanto o portão de lançamento registra qualquer retenção intencional.
- O que o bot de atualização não toca. As atualizações de versão do
Dependabot (
.github/dependabot.yml) cobrem as fixações das GitHub Actions, o arquivo de bloqueio do site, o arquivo de bloqueio do empacotador vsce (tools/vsce), o arquivo de bloqueio das CLIs de harness que o channel smoke instala (tools/channel-smoke), o pip bloqueado por hash que o CI instala (scripts/ci/requirements-ci-pip.txt) e os hooks de pre-commit. Os outros nove arquivos emscripts/cisão fechamentos regenerados por inteiro comscripts/dev/refresh_ci_locks.py; nenhum deles fixa o pip, e as atualizações de versão do Dependabot para essa pasta se limitam ao pip. Os pisos de runtime empyproject.toml, os requisitos de lançamento bloqueados por hash, o fechamento vendorizado emsrc/apothem/_vendor/vendor.txte os hooks de pre-commit fixados em sincronia compyproject.tomlmudam à mão, porque o incremento de uma única fixação quebraria os arquivos com os quais eles concordam. Os hooks do ruff e do mypy são dois deles:tests/scripts/test_ci_tool_locks.pyfalha até que a fixação exata do ruff empyproject.toml, a rev do hook, o bloqueio do ruff e o bloqueio dev tenham a mesma versão, e até que a fixação exata do mypy, a rev do hook do mypy e as bibliotecas que esse hook lista correspondam ao bloqueio dev. Por isso, um pull request de segurança do Dependabot para qualquer uma das duas ferramentas é adaptado à mão. A versão major de@types/nodedo site acompanha o piso de Node dele (enginese.nvmrc, Node 22), de modo que o Dependabot propõe apenas as atualizações minor e patch dele; eleve essa versão major junto com o piso. As atualizações de versão deixam intactos os blocosoverridesdo npm. As atualizações de segurança do Dependabot ainda cobrem todos os manifestos, de modo que um pull request de segurança pode propor uma dessas fixações, ou mover um override; adapte-o à mão ou feche-o. - Mesclando um pull request do Dependabot. O padrão de squash do
repositório usa o título do PR. Alguns títulos do Dependabot, principalmente
os agrupados que nomeiam um diretório, ultrapassam o assunto de squash de 72
caracteres; a verificação PR Title os aponta, e uma troca de título os
corrige. Um pull request que altera um arquivo de bloqueio de ferramenta não
executa nenhum job de
ci.yml, então o fluxo de trabalho de auditoria npm o audita e o instala, empacota a extensão do VS Code para o arquivo de bloqueio do vsce e executa os testes de lançamento do vsce, e o fluxo de trabalho do channel smoke é executado para o arquivo de bloqueio do channel-smoke. O fluxo de trabalho Markdown Lint executa o hook markdownlint em um pull request de hooks de pre-commit; os outros hooks não são exercitados no CI, então executepre-commit run --all-filesno branch dele antes de mesclá-lo.
Cabeçalho de autoria — Linha de licença SPDX por arquivo
Define e aplica marcadores de identificador de licença SPDX por arquivo usando cabeçalhos canônicos de uma única linha na sintaxe de comentário adequada para cada tipo de arquivo.
Disciplina de planos — Memória de trabalho efêmera e local ao projeto
Gerencie a memória de trabalho efêmera e local ao projeto em .apothem/plans/ (uma árvore legada .plans/ é atualizada via apothem migrate-workspace) com esquema de frontmatter, transições de ciclo de vida, promoção para ADR e aplicação por meio de hooks e validadores.