Манифест закрепления зависимостей
Политика объявления зависимостей, охватывающая диапазоны среды выполнения Python, инструменты разработки, lock-файлы инструментов релиза и утверждённые нижние границы версий для каждой поверхности сборки.
Утверждённая позиция о том, как Apothem объявляет, закрепляет и блокирует зависимости. Аудитория: участники и нижестоящие потребители, которым нужно понять модель воспроизводимости.
Позиция
Apothem — это Python CLI с небольшим набором зависимостей среды выполнения плюс
более крупная цепочка инструментов для разработки, аудита, релиза и
документации. Зависимости среды выполнения используют консервативные нижние
границы в pyproject.toml; сайт коммитит site/package-lock.json; GitHub
Actions закреплены по неизменяемому SHA с комментариями версий и обновляются
через Dependabot.
| Класс | Объявление | Политика блокировки |
|---|---|---|
| Зависимости среды выполнения Python | [project.dependencies] в pyproject.toml | Диапазоны с нижней границей для пользователей; lock среды выполнения Python в пакет не входит. CI устанавливает их замыкание, заблокированное по хешу (см. Lock-файлы зависимостей CI) |
| Инструменты разработки / безопасности | extras из [project.optional-dependencies] | Диапазоны с нижней границей, кроме ruff и mypy: extra dev точно закрепляет их на версиях, которые запускает CI (для ruff см. Lock-файлы инструментов CI); CI устанавливает extra dev из замыкания, заблокированного по хешу (см. Lock-файлы зависимостей CI) |
| Инструменты релиза | scripts/release/requirements-build-linux.txt для рабочих процессов релиза; extra release в [project.optional-dependencies] для локальных сборок | Заблокированы по хешу для рабочих процессов релиза; сгенерированы из замыкания инструментов релиза. Extra release — диапазон с нижней границей |
| pip для CI | scripts/ci/requirements-ci-pip.txt | Заблокирован по хешу: одно закрепление pip==X.Y.Z с хешами его wheel и sdist; каждый рабочий процесс, обновляющий pip, устанавливает его с --require-hashes -r. Dependabot предлагает обновления и переписывает хеши; scripts/dev/check_pip_pin_drift.py держит закрепление на pip 26.2 или новее |
| Lock-файлы зависимостей CI | scripts/ci/requirements-ci-dev.txt, -runtime.txt, -test.txt и -prereqs.txt | Универсальные замыкания, заблокированные по хешу. Они компилируются с помощью uv pip compile из pyproject.toml (а для -prereqs.txt — из требований движка к хосту) на дату блокировки за вычетом 7-дневного периода ожидания; оба значения записаны в каждом файле. Устанавливаются с --require-hashes -r (сам пакет — с --no-deps -e .). CI тестирует заблокированные версии, пока scripts/dev/refresh_ci_locks.py не сгенерирует их заново; pip-audit.yml еженедельно проверяет новейшие допустимые версии, а также сами lock-файлы. tests/scripts/test_ci_locks_fresh.py падает, когда lock-файл больше не удовлетворяет своим входным данным |
| Lock-файлы инструментов CI | scripts/ci/requirements-ci-ruff.txt, -bandit.txt, -pip-audit.txt, -pip-licenses.txt и -pre-commit.txt | По одному замыканию, заблокированному по хешу, на каждый инструмент, который запускают рабочие процессы. Их компилирует тот же скрипт по точной спецификации из scripts/dev/ci_lock_table.py, с той же датой блокировки и тем же периодом ожидания, а устанавливаются они с --require-hashes -r. Ни один рабочий процесс не указывает версию инструмента. pre-commit требует Python 3.11 или новее, поэтому и его lock-файл тоже, а lock-файл pip-audit тоже компилируется для 3.11, потому что у новейшего filelock с поддержкой 3.10 есть неисправленные уязвимости. Кроме lock-файла pip для CI, ни один lock-файл не закрепляет pip |
| Сайт документации | site/package.json | site/package-lock.json закоммичен и потребляется npm ci |
| Упаковщик VS Code | tools/vsce/package.json | tools/vsce/package-lock.json закоммичен; задания релиза и публикации запускают npm ci --ignore-scripts |
| CLI окружений для channel-smoke | tools/channel-smoke/package.json | tools/channel-smoke/package-lock.json закоммичен и потребляется npm ci |
| GitHub Actions | Закреплённые по SHA ссылки uses: с комментариями версий | Dependabot еженедельно предлагает обновления; исключения несут маркеры action-pinning-exempt: |
Утверждение релиза
| Поверхность | Утверждённое состояние | Обоснование |
|---|---|---|
| Зависимости среды выполнения | Текущие нижние границы находятся в pyproject.toml, в [project.dependencies] | Текущие стабильные нижние границы утверждены до релиза; известных блокеров по CVE среды выполнения нет |
| Инструменты разработки | Текущие нижние границы находятся в extra dev файла pyproject.toml; ruff и mypy закреплены там точно | Последние стабильные нижние границы утверждены до релиза; ruff и mypy закреплены каждый на одной версии для хуков, CI и участников, хук mypy закрепляет перечисленные в нём библиотеки на версиях из lock-файла dev, а tests/scripts/test_ci_tool_locks.py падает при расхождении |
| Инструменты безопасности | Текущие нижние границы находятся в extra security файла pyproject.toml | Текущие нижние границы сканеров утверждены до релиза |
| Инструменты релиза | Замыкание с фиксацией по хешу находится в scripts/release/requirements-build-linux.txt | Рабочий процесс релиза устанавливает с --require-hashes; локальные скрипты требуют, чтобы build уже существовал, вместо тихой установки изменяющихся зависимостей |
| pip для CI | Закрепление, заблокированное по хешу, находится в scripts/ci/requirements-ci-pip.txt | pip 26.2 или новее закрывает CVE-2026-13346; рабочие процессы устанавливают его с --require-hashes, поэтому загрузка сверяется с опубликованным хешем |
| Lock-файлы зависимостей CI | Замыкания с фиксацией по хешу находятся в scripts/ci/requirements-ci-{dev,runtime,test,prereqs}.txt | Рабочие процессы устанавливают их с --require-hashes; каждый файл записывает свою дату блокировки, период ожидания и граничную дату, а тест актуальности держит их синхронными с pyproject.toml |
| Lock-файлы инструментов CI | Замыкания с фиксацией по хешу находятся в scripts/ci/requirements-ci-{ruff,bandit,pip-audit,pip-licenses,pre-commit}.txt | Рабочие процессы устанавливают их с --require-hashes и не указывают версий инструментов; спецификации находятся в scripts/dev/ci_lock_table.py, а дата блокировки, период ожидания и граничная дата совпадают с lock-файлами зависимостей |
| Инструменты документации | Текущие диапазоны находятся в site/package.json; точное разрешение — в site/package-lock.json | npm outdated после обновления lock-файла не возвращает устаревших зависимостей документации |
| GitHub Actions | Существующие закрепления по SHA сохранены для текущего релиза; повторно используемый рабочий процесс SLSA остаётся задокументированным исключением, закреплённым по тегу и отмеченным комментарием action-pinning-exempt: в release.yml | Повторный прогон публичного CI зелёный; широкие обновления мажорных версий Actions намеренно оставлены PR автоматизации зависимостей, чтобы не вносить постоянных изменений в поверхность релиза |
| Упаковка npm | Рабочий процесс publish-npm.yml запускается при push тега v*.*.*, а workflow_dispatch — запасной вариант | Единственный публичный npm-пакет — @ahmed-g-gad/apothem; публикация ожидает одобрения оператором среды release, которое даётся после прохождения scripts/release/check-release-ready.sh |
Блокировка
Собственные lock-файлы Python участники генерируют по требованию, и они не
коммитятся: замыкание среды выполнения Apothem невелико, а CLI поддерживает
несколько минорных версий Python. Исключение — CI. Он устанавливает
заблокированные по хешу файлы из scripts/ci/: четыре lock-файла зависимостей
(requirements-ci-dev.txt, -runtime.txt, -test.txt и -prereqs.txt) и по
одному lock-файлу на каждый инструмент, который запускают рабочие процессы
(-ruff.txt, -bandit.txt, -pip-audit.txt, -pip-licenses.txt и
-pre-commit.txt). python scripts/dev/refresh_ci_locks.py компилирует все
девять на дату блокировки за вычетом 7-дневного периода ожидания, как у
Dependabot. Поэтому CI тестирует релизы, которым на дату блокировки исполнилась
хотя бы неделя, а обновление файлов сдвигает их вперёд. Каждый файл записывает
дату блокировки, период ожидания и итоговую граничную дату, а --check
воспроизводит эту граничную дату. Срочному исправлению безопасности, которое
моложе периода ожидания, нужен запуск обновления с --cooldown-days 0 или
исключение из периода ожидания для его пакета в scripts/dev/ci_lock_table.py;
такое исключение снимает период ожидания только для этого пакета.
Еженедельный запуск pip-audit.yml проверяет новейшие версии, которые
допускает pyproject.toml, а также все девять lock-файлов и запрашивает
у сервиса OSV сведения об уязвимостях. Участники по-прежнему устанавливают
пакет командой 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.lockСайт устроен иначе: site/package-lock.json коммитится, а CI использует
npm ci, поэтому отрендеренный сайт документации имеет воспроизводимое замыкание
Node.
Обоснование
- Почему диапазонные закрепления для Python? CLI поддерживает широкую матрицу
Python, поэтому
pyproject.tomlзадаёт консервативные нижние границы. CI устанавливает заблокированные по хешу замыкания этих диапазонов, которые сопровождающий обновляет с помощьюscripts/dev/refresh_ci_locks.py(задуманный ритм — раз в неделю), а еженедельный запускpip-audit.ymlразрешает новейшие допустимые версии, чтобы выявить проблемы с текущими инструментами. - Почему закоммиченный lock Node? Сайт — это развёрнутый статический артефакт;
lock-файл даёт детерминированные сборки
npm ciдля этой публичной поверхности. - Почему закреплённые по SHA Actions? Теги изменяемы; SHA неизменяемы. Dependabot предлагает повышения SHA и переписывает каждый комментарий версии, а шлюз релиза фиксирует любое намеренное удержание версии.
- Чего бот обновлений не касается. Обновления версий Dependabot
(
.github/dependabot.yml) охватывают закрепления GitHub Actions, lock-файл сайта, lock-файл упаковщика vsce (tools/vsce), lock-файл CLI окружений, который устанавливает channel smoke (tools/channel-smoke), заблокированный по хешу pip, который устанавливает CI (scripts/ci/requirements-ci-pip.txt), и хуки pre-commit. Остальные девять файлов вscripts/ci— это замыкания, которыеscripts/dev/refresh_ci_locks.pyгенерирует заново целиком; ни один из них не закрепляет pip, и обновления версий Dependabot для этой папки ограничены pip. Нижние границы среды выполнения вpyproject.toml, заблокированные по хешу требования релиза, вендорное замыкание вsrc/apothem/_vendor/vendor.txtи хуки pre-commit, закреплённые синхронно сpyproject.toml, обновляются вручную, потому что повышение одного закрепления сломало бы файлы, с которыми они согласованы. Хуки ruff и mypy — два из них:tests/scripts/test_ci_tool_locks.pyпадает, пока точное закрепление ruff вpyproject.toml, rev хука, lock-файл ruff и lock-файл dev не укажут одну и ту же версию и пока точное закрепление mypy, rev хука mypy и библиотеки, которые перечисляет этот хук, не совпадут с lock-файлом dev. Поэтому pull request безопасности от Dependabot для любого из этих инструментов адаптируется вручную. Мажорная версия@types/nodeсайта следует за его нижней границей Node (enginesи.nvmrc, Node 22), поэтому Dependabot предлагает только её минорные и патч-обновления; повышайте эту мажорную версию вместе с нижней границей. Обновления версий не трогают блоки npmoverrides. Обновления безопасности Dependabot по-прежнему охватывают каждый манифест, поэтому pull request безопасности может предложить одно из этих закреплений или сдвинуть override; адаптируйте его вручную или закройте. - Слияние pull request от Dependabot. Squash по умолчанию в репозитории
берёт заголовок PR. Некоторые заголовки Dependabot, в основном групповые,
называющие каталог, превышают 72 символа темы squash-коммита; проверка PR Title
называет их, а смена заголовка их исправляет. Pull request, изменяющий
lock-файл инструмента, не запускает ни одного задания
ci.yml, поэтому рабочий процесс npm audit проверяет и устанавливает его, упаковывает расширение VS Code для lock-файла vsce и запускает тесты релиза vsce, а рабочий процесс channel smoke запускается для lock-файла channel-smoke. Рабочий процесс Markdown Lint запускает хук markdownlint на pull request с хуками pre-commit; остальные хуки в CI не проверяются, поэтому перед слиянием запуститеpre-commit run --all-filesна его ветке.
Заголовок авторства — пофайловая SPDX-строка лицензии
Определение и применение пофайловых маркеров SPDX-идентификатора лицензии с помощью канонических однострочных заголовков в подходящем синтаксисе комментариев для каждого типа файла.
Дисциплина планов — проектно-локальная эфемерная рабочая память
Управление проектно-локальной эфемерной рабочей памятью в .apothem/plans/ (устаревшее дерево .plans/ обновляется через apothem migrate-workspace) со схемой frontmatter, переходами жизненного цикла, повышением до ADR и принудительным применением через хуки и валидаторы.