Управление зависимостями

Wippy использует систему зависимостей на основе файла блокировки. Модули публикуются в хаб, объявляются как зависимости в исходном коде и разрешаются в файл wippy.lock, который отслеживает точные версии.

Файлы проекта

wippy.lock

Файл блокировки отслеживает структуру каталогов проекта и закреплённые зависимости:

directories:
  modules: .wippy
  src: ./src
modules:
  - name: acme/http
    version: v1.2.0
    hash: 4ea816fe84ca58a1f0869e5ca6afa93d6ddd72fa09e1162d9e600a7fbf39f0a2
  - name: acme/sql
    version: v2.0.1
    hash: b3f9c8e12a456d7890abcdef1234567890abcdef1234567890abcdef12345678
Поле Описание
directories.modules Где хранятся загруженные модули (по умолчанию: .wippy)
directories.src Где находится исходный код (по умолчанию: ./src)
modules[].name Идентификатор модуля в формате org/module
modules[].version Закреплённая семантическая версия
modules[].hash Дайджест артефакта, которому должен соответствовать загруженный пакет; голое hex-значение читается как sha256
modules[].root Помечает выбранный корень развёртывания; его может нести не более одного модуля
options.unpack_modules Распаковывать пакеты в каталоги вместо загрузки их как файлов .wapp (по умолчанию: false)

wippy.yaml

Метаданные модуля для публикации. Требуется только при публикации собственного модуля:

organization: acme
module: http
version: 1.2.0
description: HTTP utilities for Wippy
license: MIT
repository: https://github.com/acme/wippy-http
keywords:
  - http
  - web
Поле Обязательное Описание
organization Да Строчные буквы, буквенно-цифровые с дефисами
module Да Строчные буквы, буквенно-цифровые с дефисами
version Нет Семантическая версия (устанавливается при публикации)
description Нет Описание модуля
license Нет Идентификатор лицензии SPDX
repository Нет URL репозитория с исходным кодом
homepage Нет Домашняя страница проекта
keywords Нет Ключевые слова для поиска
authors Нет Список авторов

Объявление зависимостей

Добавьте записи ns.dependency в ваш _index.yaml:

version: "1.0"
namespace: app
entries:
  - name: dependency.http
    kind: ns.dependency
    component: acme/http
    version: "^1.0.0"

  - name: dependency.sql
    kind: ns.dependency
    component: acme/sql
    version: ">=2.0.0"

Ограничения версий

Ограничение Пример Совпадает
Точная 1.2.3 Только 1.2.3
Каретка ^1.2.0 >=1.2.0, <2.0.0
Тильда ~1.2.0 >=1.2.0, <1.3.0
Диапазон >=1.0.0 1.0.0 и выше
Подстановочный знак * Любая версия (выбирается наивысшая)
Комбинированное >=1.0.0 <2.0.0 Между 1.0.0 и 2.0.0

Правила разрешения

  • Каждый модуль разрешается по пересечению всех объявленных диапазонов в графе зависимостей. Несовместимые диапазоны (diamond-конфликты) приводят к явной ошибке разрешения, а не к молчаливому выбору одной из сторон.
  • Полный wippy update решает каждый модуль из его объявленных диапазонов; точечное обновление и восстановление при загрузке сохраняют закреплённую версию, если она всё ещё удовлетворяет каждому действующему диапазону.
  • Параметры корня выигрывают у транзитивных: когда ваше приложение и зависимость привязывают одно и то же требование, приоритет у параметров вашей ns.dependency. Диапазоны версий никогда не переопределяются; каждое объявление входит в пересечение.
  • Компонент, объявленный несколькими корневыми записями ns.dependency, управляется одной из них — устоявшиеся объявления идут раньше новых, носители параметров — раньше объявлений без них, при равенстве выигрывает наименьший ID записи — а остальные сворачиваются в ссылки на неё. Дубликат, параметры которого расходятся с управляющим объявлением, отклоняется с ошибкой конфликта; вместо этого обновите существующую зависимость.

Два вида сбоя разрешения сообщаются по-разному. Выражение ограничений, которое не может быть удовлетворено ни одним релизом вообще — пересечение действующих диапазонов пусто — это конфликт, и ошибка называет модуль и каждого запрашивающего, внёсшего диапазон. Корректный набор диапазонов, для которого хаб сейчас не публикует подходящую версию, — это сбой доступности: более поздний релиз может сделать его разрешимым без каких-либо изменений в объявлениях.

Среда выполнения сохраняет каждый разрешённый граф в истории своего реестра и воспроизводит его при загрузке вместо повторного разрешения, поэтому развёрнутое приложение загружается ровно с теми версиями, которые были разрешены при применении изменения зависимостей. wippy.lock остаётся переносимым снимком для исходных проектов.

Происхождение записей

Происхождение принадлежит реестру, а не метаданным записи. При загрузке записей реестр помечает каждую источником развёртывания, который её поставил:

Поле Описание
registry.owner Имя модуля (org/module), поставившего запись; пусто для исходников приложения
registry.root Устанавливается на записях ns.dependency, поставленных корнем развёртывания, помечая их как корневые объявления

Авторы записей никогда не пишут эти поля; они назначаются при загрузке и не могут быть подделаны из _index.yaml. Просмотреть их можно через wippy registry list --registry-meta --json.

Рабочий процесс

Создание нового проекта

wippy init

Создаёт wippy.lock с каталогами по умолчанию.

Добавление зависимостей

wippy add acme/http               # Последняя версия
wippy add acme/http@1.2.3         # Точная версия
wippy add acme/http@latest         # Метка latest

Это обновляет файл блокировки. Затем установите:

wippy install

Разрешение из исходного кода

Если ваш исходный код уже содержит записи ns.dependency:

wippy update

Эта команда сканирует каталог с исходным кодом, разрешает все ограничения зависимостей, обновляет файл блокировки и устанавливает модули.

Обновление зависимостей

wippy update                       # Переразрешить все зависимости
wippy update acme/http             # Обновить только acme/http
wippy update acme/http acme/sql    # Обновить конкретные модули

При обновлении конкретных модулей остальные модули остаются закреплёнными на текущих версиях. Если обновление потребует изменения незатронутых модулей, запрашивается подтверждение.

Установка из файла блокировки

wippy install                      # Установить всё из файла блокировки
wippy install --refresh            # Повторно загрузить каждый модуль (--force и --repair — алиасы)

Хранение модулей

Загруженные модули хранятся в каталоге .wippy/vendor/:

project/
  wippy.lock
  src/
    _index.yaml
  .wippy/
    vendor/
      acme/
        http-v1.2.0.wapp
        sql-v2.0.1.wapp

По умолчанию модули хранятся как файлы .wapp. Для распаковки в каталоги:

# wippy.lock
options:
  unpack_modules: true

С включённой распаковкой:

.wippy/
  vendor/
    acme/
      http-v1.2.0.wapp
      http/
        wippy.yaml
        src/
          _index.yaml
          ...

Распаковка никогда не отбрасывает пакет. Канонический проверенный .wapp остаётся рядом с распакованным каталогом, потому что это единственное контентно-адресуемое свидетельство для модуля, а материализация артефактов и восстановление читают ресурсы обратно из него. Именно наличие .wapp проверяется при установке: каталог, у которого нет пакета, считается неустановленным, и модуль загружается заново. Каждая установка заново извлекает каталог из проверенного архива, поэтому правки вендорного каталога вручную не сохраняются.

Модули, разрешённые из замены рабочего пространства, никогда не загружаются и не складываются в вендорный каталог; они загружаются из локального пути.

Локальная разработка с заменами

Замените модули из хаба локальными каталогами для разработки. Замены объявляются в секции workspace файла конфигурации среды выполнения — обычно приватного, игнорируемого git и компонуемого поверх .wippy.yaml:

# .wippy.workspace.yaml
version: "1.0"
workspace:
  replacements:
    acme/http: ../local-http
    acme/sql: ../local-sql
wippy run --config .wippy.yaml --config .wippy.workspace.yaml

Ключи — это org/module, значения — каталоги (относительные пути разрешаются относительно каталога первого файла --config). Установка замены в null отключает замену, унаследованную из более раннего слоя конфигурации или профиля. Замены также могут находиться внутри профиля, чтобы активироваться только с --profile workspace.

Требование, чтобы путь существовал и был каталогом, действует только для модуля, который действительно выбран графом lock-файла. Замена, объявленная для модуля, от которого ничто не зависит, — это вход разрешения, а не вход загрузки: она может указывать на каталог, не выгруженный на этой машине, и валидация не упадёт.

Замена меняет то, откуда берутся исходники модуля, а не то, какой релиз был выбран. Путь загрузки сохраняет версию, выбранную lock-файлом для этого модуля, и помечается как замена; записи, загруженные из неё, перекрывают вендорные записи с теми же ID. Когда замена объявлена для модуля, версию которого lock-файл не закрепляет, разрешение запрашивает версию релиза у хаба, а пока более сильное свидетельство не выберет её, удерживает локальную нулевую версию.

При согласовании делается снимок текущего локального дерева, а его дайджест и размер записываются как идентичность замены. Прежний дайджест является контрольной точкой, а не неизменяемой идентичностью артефакта Hub.

Замены рабочего пространства влияют на граф загрузки при старте и никогда не записываются в wippy.lock. Изменения локальных исходников подхватываются напрямую, без обращения к хабу. Глобы exclude: из исходного wippy.yaml модуля применяются и к каталогам замен — как при загрузке записей, так и при хешировании содержимого.

Секция replacements: в wippy.lock устарела: она всё ещё загружается, но выводит предупреждение. Перенесите эти записи в workspace.replacements файла конфигурации.

Порядок загрузки

При запуске Wippy загружает записи из каталогов в следующем порядке:

  1. Каталог с исходным кодом (src)
  2. Каталоги замен
  3. Каталоги вендорных модулей

Модули с активными заменами пропускают свой вендорный путь.

Проверка целостности

Каждый модуль в lock-файле несёт дайджест артефакта. Загрузка среды отказывается загружать модуль, у записи которого в lock-файле дайджеста нет; wippy install такую запись принимает и записывает дайджест, который хаб отдаёт вместе со скачиванием.

При загрузке среды скачивания промежуточные: пакет пишется во временный файл рядом с итоговым расположением, проверяется по дайджесту, закреплённому в wippy.lock, и по дайджесту, который хаб отдал вместе с URL загрузки (плюс отданный размер), и только затем переименовывается на место. Промежуточный файл, не прошедший проверку, удаляется. wippy install переименовывает скачанный файл в его вендорный путь до проверки, сверяет его только с отданными дайджестом и размером, удаляет при неудаче, а дайджест в lock-файле, отличающийся от отданного, заменяет, а не принуждает к нему.

Несовпадение дайджеста — жёсткий, неповторяемый сбой. При загрузке среды это PermissionDenied, "module integrity verification failed", возникающий и для свежего скачивания, и для уже вендорного пакета, который перепроверяется по дайджесту из lock-файла перед загрузкой записей. wippy install сообщает о нём как Internal: "failed to store module", оборачивающий "verify cached WAPP: digest mismatch", для пакета, уже лежащего в вендорном каталоге, и "failed to download module", оборачивающий "verify downloaded WAPP: digest mismatch", для свежего скачивания. Ничто не повторяет попытку, не перезагружает поверх несовпадения и не откатывается на отданное содержимое.

Та же проверка охраняет разрешение. Когда хаб отдаёт манифест, дайджест которого отличается от закреплённого в lock-файле, кэш манифестов однократно обновляется и сравнение повторяется; если расхождение сохраняется, разрешение падает с указанием обоих дайджестов.

Распакованные каталоги несут собственные записанные дайджест, размер и дайджест дерева и перепроверяются по записанным значениям, поэтому изменённое вендорное дерево обнаруживается, а не загружается.

Каждая попытка согласования делает снимок текущего локального дерева и проверяет тот же дайджест и размер до загрузки. Параллельное изменение вызывает ошибку проверки вместо смешивания поколений исходников. При перезапуске неизменяемые исторические артефакты предварительно загружаются отдельно, а исторические локальные замены сопоставляются с окончательными объявлениями зависимостей; удалённой замене не нужен старый каталог, но выбранная итоговым графом замена должна существовать и быть корректной.

Артефакты сборки

Модуль может поставлять файловый ресурс, помеченный meta.artifact.format, который потребители материализуют на диск вместо чтения во время выполнения. Полные и точечные wippy install и wippy update, холодная загрузка и операции с зависимостями во время выполнения согласуют эти выходы в рамках той же транзакции, которая меняет граф модулей; artifact.materialization_root задаёт корень вывода. См. Артефакты сборки.

Смотрите также