diff --git a/doc/cabal-package.rst b/doc/cabal-package.rst index 2cad579ea04..bc9c23ed798 100644 --- a/doc/cabal-package.rst +++ b/doc/cabal-package.rst @@ -1017,24 +1017,70 @@ disambiguation purposes. Example: $ cabal repl bench:baz Freezing dependency versions -"""""""""""""""""""""""""""" - -If a package is built in several different environments, such as a -development environment, a staging environment and a production -environment, it may be necessary or desirable to ensure that the same -dependency versions are selected in each environment. This can be done -with the ``freeze`` command: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: console $ cabal freeze -The command writes the selected version for all dependencies to the -``cabal.config`` file. All environments which share this file will use -the dependency versions specified in it. +generates ``cabal.project.freeze`` file, which describes the exact dependency tree as it was resolved at that moment by Cabal. +This means it captures an exact version of every dependency, including dependencies of dependencies, recursively all the way. + +Since ``cabal`` reads ``cabal.project.freeze`` when present, and takes into consideration the version constraints in it, +this means that by producing ``cabal.project.freeze`` you are guaranteed that every future ``cabal`` call will use the exact same set of dependencies, +regardless of any updates (even patches) that might get published for these dependencies in the meantime. +Therefore, we have effectively "frozen" the dependencies in place, making our build consistent and reproducible. + +``cabal.project.freeze`` is intended to be committed to the version control. + +Do you need this? +""""""""""""""""" + +Why would you want this? Don't we want to get minor updates of our dependencies, or at least patches, as soon as we can? +Well, although they shouldn't, it is possible that any kind of update introduces new bugs, performance issues, or some other kind of unexpected behaviour. +This is where ``cabal.project.freeze`` comes in, as it ensures that dependencies don't unexpectedly change. +You can still update your dependencies, but you have to do it on purpose, by modifying or by deleting and regenerating ``cabal.project.freeze`` file, +and in the meantime you are guaranteed no surprises will happen. + +This consistency can be valuable as it ensures that all teammates, deployments, and continuous integration are installing the exactly same dependencies. +So if you are running and testing the code on your local machine, you are guaranteed that your teammate and your continuos integration will be running the exact same code, +and that at the end that exact same code will get deployed. + +Usual use-case for using ``cabal freeze`` is when developing end-user code, for example an executable that you will distribute. +On the other hand, if you are developing a library, you will not want to distribute it together with the ``cabal.project.freeze`` file, as it would make it very hard for cabal to resolve dependencies for users of the library, since they would be too strict. + +Common workflow +""""""""""""""" + +Common workflow for using ``cabal freeze``, if you changed any dependencies in ``.cabal`` file or want to update their versions, is to delete ``cabal.project.freeze`` file (if it already exists) and run ``cabal freeze`` to generate fresh version of ``cabal.project.freeze``. + +You might in some cases want to skip deletion of ``cabal.project.freeze``, but keep in mind that in that case ``cabal freeze`` will use existing ``cabal.project.freeze`` when resolving dependencies, therefore not updating any existing dependencies, only adding new ones. +If not sure, best to delete ``cabal.project.freeze`` first and then run ``cabal freeze``. + +Finally, you will always want to commit the new ``cabal.project.freeze`` to the version control. + +Ensuring everything is frozen +""""""""""""""""""""""""""""" + +Since ``cabal`` reads both ``.cabal`` and ``cabal.project.freeze`` files and combines version constraints from them, you can get into a state where not all dependencies are frozen, i.e. if you add a dependency to ``.cabal`` but forget to regenerate ``cabal.project.freeze`` after it -> now this new dependency will not be frozen and might get updated unexpectedly. + +To check if you are in such state, you can just run ``cabal freeze`` and check if ``cabal.project.freeze`` changed its contents -> if so, somebody forgot to regenerate ``cabal.project.freeze`` previously. This will also fix the problem at the same time. + +To automate this check, you can make it a part of your continuous integration, or a part of your pre-commit hook. + +Example of how this can be done via bash script: + +.. code-block:: bash + + [[ -f cabal.project.freeze ]] || exit 1 + OLD_FREEZE_SUM=$(md5sum cabal.project.freeze) + cabal freeze || exit 1 + NEW_FREEZE_SUM=$(md5sum cabal.project.freeze) + exit [[ "$NEW_FREEZE_SUM" == "$OLD_FREEZE_SUM" ]] + Generating dependency version bounds -"""""""""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Cabal also has the ability to suggest dependency version bounds that conform to `Package Versioning Policy`_, which is @@ -1063,7 +1109,7 @@ For example, given the following dependencies specified in bar >= 1.1 && < 1.2 Listing outdated dependency version bounds -"""""""""""""""""""""""""""""""""""""""""" +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Manually updating dependency version bounds in a ``.cabal`` file or a freeze file can be tedious, especially when there's a lot of diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 988572e0a3d..12376ef342d 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -22,7 +22,7 @@ file with ``profiling: True``. The full configuration of a project is determined by combining the following sources (later entries override earlier ones, except for appendable -options): +options, like dependency version constraints): 1. ``~/.cabal/config`` (the user-wide global configuration)