<commit_before>[[dependency-locking]]
= Locking dependency versions

Use of dynamic dependency versions (e.g. `1.+` or `[1.0,2.0)`) makes builds non-deterministic.
This causes builds to break without any obvious change, and worse, can be caused by a transitive dependency that the build author has no control over.

To achieve https://reproducible-builds.org/[reproducible builds], it is necessary to _lock_ versions of dependencies and transitive dependencies such that a build with the same inputs will always resolve the same module versions.
This is called _dependency locking_.

It enables, amongst others, the following scenarios:

* Companies dealing with multi repositories no longer need to rely on `-SNAPSHOT` or changing dependencies,
which sometimes result in cascading failures when a dependency introduces a bug or incompatibility.
Now dependencies can be declared against major or minor version range, enabling to test with the latest versions on CI while leveraging locking for stable developer builds.
* Teams that want to always use the latest of their dependencies can use dynamic versions, locking their dependencies only for releases.
The release tag will contain the lock states, allowing that build to be fully reproducible when bug fixes need to be developed.

Combined with <<publishing_maven.adoc#publishing_maven:resolved_dependencies,publishing resolved versions>>, you can also replace the declared dynamic version part at publication time.
Consumers will instead see the versions that your release resolved.

Locking is enabled per <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,dependency configuration>>.
Once enabled, you must create an initial lock state.
It will cause Gradle to verify that resolution results do not change, resulting in the same selected dependencies even if newer versions are produced.
Modifications to your build that would impact the resolved set of dependencies will cause it to fail.
This makes sure that changes, either in published dependencies or build definitions, do not alter resolution without adapting the lock state.

[NOTE]
====
Dependency locking makes sense only with <<dynamic_versions.adoc#sub:declaring_dependency_with_dynamic_version,dynamic versions>>.
It will have no impact on <<dynamic_versions.adoc#sub:declaring_dependency_with_changing_version,changing versions>> (like `-SNAPSHOT`) whose coordinates remain the same, though the content may change.
Gradle will even emit a warning when persisting lock state and changing dependencies are present in the resolution result.
====

== Enabling locking on configurations

Locking of a configuration happens through the link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[ResolutionStrategy]:

.Locking a specific configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingSingleFilePerProject/groovy",files="build.gradle[tags=locking-single]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingSingleFilePerProject/kotlin",files="build.gradle.kts[tags=locking-single]"]
====

[NOTE]
====
Only configurations that can be resolved will have lock state attached to them.
Applying locking on non resolvable-configurations is simply a no-op.
====

Or the following, as a way to lock all configurations:

.Locking all configurations
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingAllConfigurations/groovy",files="build.gradle[tags=locking-all]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingAllConfigurations/kotlin",files="build.gradle.kts[tags=locking-all]"]
====

[NOTE]
====
The above will lock all _project_ configurations, but not the _buildscript_ ones.
====

You can also disable locking on a specific configuration.
This can be useful if a plugin configured locking on all configurations but you happen to add one that should not be locked.

.Unlocking a specific configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-unlockingSingleConfiguration/groovy",files="build.gradle[tags=locking-one]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-unlockingSingleConfiguration/kotlin",files="build.gradle.kts[tags=locking-one]"]
====

=== Locking buildscript classpath configuration

If you apply plugins to your build, you may want to leverage dependency locking there as well.
In order to lock the <<plugins.adoc#sec:applying_plugins_buildscript,`classpath` configuration>> used for script plugins, do the following:

.Locking buildscript classpath configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingClasspathConfiguration/groovy",files="build.gradle[tags=locking-classpath]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingClasspathConfiguration/kotlin",files="build.gradle.kts[tags=locking-classpath]"]
====

== Generating and updating dependency locks

In order to generate or update lock state, you specify the `--write-locks` command line argument in addition to the normal tasks that would trigger configurations to be resolved.
This will cause the creation of lock state for each resolved configuration in that build execution.
Note that if lock state existed previously, it is overwritten.

[NOTE]
====
Gradle will not write lock state to disk if the build fails.
This prevents persisting possibly invalid state.
====

[[lock_all_configurations_in_one_build_execution]]
=== Lock all configurations in one build execution

When locking multiple configurations, you may want to lock them all at once, during a single build execution.

For this, you have two options:

* Run `gradle dependencies --write-locks`.
This will effectively lock all resolvable configurations that have locking enabled.
Note that in a multi project setup, `dependencies` only is executed on _one_ project, the root one in this case.
* Declare a custom task that will resolve all configurations
<commit_msg>Update dependency locking docs<commit_after>[[dependency-locking]]
= Locking dependency versions

Use of dynamic dependency versions (e.g. `1.+` or `[1.0,2.0)`) makes builds non-deterministic.
This causes builds to break without any obvious change, and worse, can be caused by a transitive dependency that the build author has no control over.

To achieve https://reproducible-builds.org/[reproducible builds], it is necessary to _lock_ versions of dependencies and transitive dependencies such that a build with the same inputs will always resolve the same module versions.
This is called _dependency locking_.

It enables, amongst others, the following scenarios:

* Companies dealing with multi repositories no longer need to rely on `-SNAPSHOT` or changing dependencies,
which sometimes result in cascading failures when a dependency introduces a bug or incompatibility.
Now dependencies can be declared against major or minor version range, enabling to test with the latest versions on CI while leveraging locking for stable developer builds.
* Teams that want to always use the latest of their dependencies can use dynamic versions, locking their dependencies only for releases.
The release tag will contain the lock states, allowing that build to be fully reproducible when bug fixes need to be developed.

Combined with <<publishing_maven.adoc#publishing_maven:resolved_dependencies,publishing resolved versions>>, you can also replace the declared dynamic version part at publication time.
Consumers will instead see the versions that your release resolved.

Locking is enabled per <<declaring_dependencies.adoc#sec:what-are-dependency-configurations,dependency configuration>>.
Once enabled, you must create an initial lock state.
It will cause Gradle to verify that resolution results do not change, resulting in the same selected dependencies even if newer versions are produced.
Modifications to your build that would impact the resolved set of dependencies will cause it to fail.
This makes sure that changes, either in published dependencies or build definitions, do not alter resolution without adapting the lock state.

[NOTE]
====
Dependency locking makes sense only with <<dynamic_versions.adoc#sub:declaring_dependency_with_dynamic_version,dynamic versions>>.
It will have no impact on <<dynamic_versions.adoc#sub:declaring_dependency_with_changing_version,changing versions>> (like `-SNAPSHOT`) whose coordinates remain the same, though the content may change.
Gradle will even emit a warning when persisting lock state and changing dependencies are present in the resolution result.
====

== Enabling locking on configurations

Locking of a configuration happens through the link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[ResolutionStrategy]:

.Locking a specific configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingSingleFilePerProject/groovy",files="build.gradle[tags=locking-single]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingSingleFilePerProject/kotlin",files="build.gradle.kts[tags=locking-single]"]
====

[NOTE]
====
Only configurations that can be resolved will have lock state attached to them.
Applying locking on non resolvable-configurations is simply a no-op.
====

Or the following, as a way to lock all configurations:

.Locking all configurations
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingAllConfigurations/groovy",files="build.gradle[tags=locking-all]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingAllConfigurations/kotlin",files="build.gradle.kts[tags=locking-all]"]
====

[NOTE]
====
The above will lock all _project_ configurations, but not the _buildscript_ ones.
====

You can also disable locking on a specific configuration.
This can be useful if a plugin configured locking on all configurations but you happen to add one that should not be locked.

.Unlocking a specific configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-unlockingSingleConfiguration/groovy",files="build.gradle[tags=locking-one]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-unlockingSingleConfiguration/kotlin",files="build.gradle.kts[tags=locking-one]"]
====

=== Locking buildscript classpath configuration

If you apply plugins to your build, you may want to leverage dependency locking there as well.
In order to lock the <<plugins.adoc#sec:applying_plugins_buildscript,`classpath` configuration>> used for script plugins, do the following:

.Locking buildscript classpath configuration
====
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingClasspathConfiguration/groovy",files="build.gradle[tags=locking-classpath]"]
include::sample[dir="snippets/dependencyManagement/dependencyLocking-lockingClasspathConfiguration/kotlin",files="build.gradle.kts[tags=locking-classpath]"]
====

== Generating and updating dependency locks

In order to generate or update lock state, you specify the `--write-locks` command line argument in addition to the normal tasks that would trigger configurations to be resolved.
This will cause the creation of lock state for each resolved configuration in that build execution.
Note that if lock state existed previously, it is overwritten.

[NOTE]
====
Gradle will not write lock state to disk if the build fails.
This prevents persisting possibly invalid state.
====

[[lock_all_configurations_in_one_build_execution]]
=== Lock all configurations in one build execution

When locking multiple configurations, you may want to lock them all at once, during a single build execution.

For this, you have two options:

* Run `gradle dependencies --write-locks`.
This will effectively lock all resolvable configurations that have locking enabled.
Note that in a multi project setup, `dependencies` only is executed on _one_ project, the root one in this case.
* Declare a custom task that resolves all configurations. This does not work for Android projects.

{'fraction_non_alphanumeric': 0.07189596620558271, 'fraction_numerical': 0.0008282945415389713, 'mean_word_length': 6.371184371184371, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6445526', 'n_tokens_mistral': 3047, 'n_tokens_neox': 2826, 'n_words': 1283}