Почему у Gradle или Maven нет файла блокировки версии зависимости?
Недавно я познакомился с концепцией файла блокировки версии зависимостей, когда читал о менеджерах пакетов, таких как NPM, Yarn, Paket, Cargo и т. Д. Насколько я понимаю, это файл, в котором перечислены все прямые и транзитивные зависимости вместе с их точными номер версии, поэтому последующие сборки гарантированно будут использовать эквивалентный набор зависимостей. Это представляется желательной функцией, так как многие менеджеры пакетов имеют или принимают концепцию.
Мои вопросы тогда:
Почему Maven или Gradle не используют файл блокировки? Или, если они делают, почему я не видел это?
Каковы преимущества и недостатки разрешения диапазонов версий в стратегии разрешения зависимостей менеджера пакетов по сравнению с разрешением только точных версий?
1 ответ
И Maven, SBT и Gradle имеют то, что вы описываете. Это называется "использование выпущенных (или исправленных) версий". Выпущенная версия выглядит как 1.2.3
по сравнению с диапазоном версий [1.2.3,)
или снимок (1.2.3-SNAPSHOT
).
Если все ваши зависимости используют выпущенные версии, вы добьетесь того, что описываете.
Диапазоны версий также являются допустимой формой версий, в зависимости от вашего варианта использования, но я бы обычно советовал против них, если они не используются для родительского POM
или просто во время активной разработки. Диапазоны версий могут пригодиться, если вам не нужно постоянно обновлять фиксированную версию стороннего или родительского POM, если вы уверены, что соответствующий артефакт никоим образом не может сломать вас (и доверять мне, это часто случается с диапазонами версий). Фиксированные версии следует использовать, когда вы хотите гарантировать, что код будет создаваться и работать в соответствии с тем, что вы изначально разработали и протестировали.
Там нет необходимости иметь такую функцию, как "файл блокировки", или что-то подобное, если ваш pom.xml
строго определяет версии ваших зависимостей.
Если вы прочтете документацию по управлению зависимостями, вы увидите, что это действительно так:
1.
У Maven нет способа достичь того, о чем вы просите. Даже если вы устанавливаете конкретные версии для ваших прямых зависимостей, что вам и следует, ваши переходные зависимости могут быть легко непреднамеренно изменены из-за, казалось бы, несвязанных изменений. Например, добавление зависимости к новой библиотеке может дать вам более старую версию существующей переходной зависимости.
То, что вам нужно, это иметь раздел dependencyManagement, в котором перечислены все ваши прямые и переходные зависимости. Вы по-прежнему не сможете определить, была ли удалена или добавлена транзитивная зависимость, что является функцией, предоставляемой, например, NPM. Проблема с отсутствием в том, что все ваши зависимости больше не находятся в разделе dependencyManagement. Чтобы обнаружить эти изменения, вы можете использовать что-то вроде https://github.com/vandmo/dependency-lock-maven-plugin которое я написал. Использование этого также сделает менее важным иметь все в разделе dependencyManagement, так как будут обнаружены изменения в транзитивных зависимостях.
Я также рекомендовал бы иметь https://maven.apache.org/enforcer/enforcer-rules/requireUpperBoundDeps.html в вашей сборке, так как Maven выбирает версии транзитивных зависимостей, которые закрываются в дереве, а не, как вы ожидаете, самая высокая версия.
Я видел много проблем во время выполнения, вызванных разработчиками, случайно меняющими переходные зависимости.
TL; DR: Вам нужно что-то вроде файла блокировки в Maven, но его нет по историческим идеологическим причинам.
2.
Я бы не рекомендовал использовать диапазоны версий, поскольку они делают вашу сборку невоспроизводимой. Он также не ведет себя так, как вы думаете, когда речь идет о переходных зависимостях.
Блокировка зависимостей была функцией, которая достигла некоторой зрелости в Gradle 5.0:https://docs.gradle.org/current/userguide/dependency_locking.html
Реализация Gradle была вдохновлена плагином Nebula: https://github.com/nebula-plugins/gradle-dependency-lock-plugin
Диапазоны версий работают хорошо, когда используются в качестве входных данных для любых обновлений вашего механизма блокировки. Итак, для Gradle вы можете просто нацелить определенные зависимости, которые будут стремиться разрешить диапазоны версий, которые вы указали для:
gradle classes --update-locks org.apache.commons:commons-lang3,org.slf4j:slf4j-api
Или вы можете просто сказать "иди обнови все мои данные":
gradle dependencies --write-locks
Если вы интересуетесь автоматизацией, также стоит рассмотреть определение стратегий разрешения: https://docs.gradle.org/current/userguide/dependency_resolution.html