Repeatable buildouts: controlling eggs used
===========================================

One of the goals of zc.buildout is to provide enough control to make
buildouts repeatable.  It should be possible to check the buildout
configuration files for a project into a version control system and
later use the checked in files to get the same buildout, subject to
changes in the environment outside the buildout.

An advantage of using Python eggs is that depenencies of eggs used are
automatically determined and used.  The automatic inclusion of
depenent distributions is at odds with the goal of repeatable
buildouts.

To support repeatable buildouts, a versions section can be created
with options for each distribution name whos version is to be fixed.
The section can then be specified via the buildout versions option.

To see how this works, we'll create two versions of a recipe egg:

    >>> mkdir('recipe')
    >>> write('recipe', 'recipe.py',
    ... '''
    ... class Recipe:
    ...     def __init__(*a): pass
    ...     def install(self):
    ...         print 'recipe v1'
    ...         return ()
    ...     update = install
    ... ''')

    >>> write('recipe', 'setup.py',
    ... '''
    ... from setuptools import setup
    ... setup(name='spam', version='1', py_modules=['recipe'],
    ...       entry_points={'zc.buildout': ['default = recipe:Recipe']},
    ...       )
    ... ''')

    >>> write('recipe', 'README', '')

    >>> print system(buildout+' setup recipe bdist_egg'), # doctest: +ELLIPSIS
    Running setup script 'recipe/setup.py'.
    ...

    >>> rmdir('recipe', 'build')

    >>> write('recipe', 'recipe.py',
    ... '''
    ... class Recipe:
    ...     def __init__(*a): pass
    ...     def install(self):
    ...         print 'recipe v2'
    ...         return ()
    ...     update = install
    ... ''')

    >>> write('recipe', 'setup.py',
    ... '''
    ... from setuptools import setup
    ... setup(name='spam', version='2', py_modules=['recipe'],
    ...       entry_points={'zc.buildout': ['default = recipe:Recipe']},
    ...       )
    ... ''')


    >>> print system(buildout+' setup recipe bdist_egg'), # doctest: +ELLIPSIS
    Running setup script 'recipe/setup.py'.
    ...

and we'll configure a buildout to use it:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ... find-links = %s
    ...
    ... [foo]
    ... recipe = spam
    ... ''' % join('recipe', 'dist'))

If we run the buildout, it will use version 2:

    >>> print system(buildout),
    Getting distribution for 'spam'.
    Got spam 2.
    Installing foo.
    recipe v2

We can specify a versions section that lists our recipe and name it in
the buildout section:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ... find-links = %s
    ... versions = release-1
    ...
    ... [release-1]
    ... spam = 1
    ... eggs = 2.2
    ...
    ... [foo]
    ... recipe = spam
    ... ''' % join('recipe', 'dist'))

Here we created a release-1 section listing the version 1 for the spam
distribution.  We told the buildout to use it by specifying release-1
as in the versions option.

Now, if we run the buildout, we'll use version 1 of the spam recipe:

    >>> print system(buildout),
    Getting distribution for 'spam==1'.
    Got spam 1.
    Uninstalling foo.
    Installing foo.
    recipe v1

Running the buildout in verbose mode will help us get information
about versions used. If we run the buildout in verbose mode without
specifying a versions section:

    >>> print system(buildout+' buildout:versions= -v'), # doctest: +ELLIPSIS
    Installing 'zc.buildout', 'setuptools'.
    We have a develop egg: zc.buildout 1.0.0.
    We have the best distribution that satisfies 'setuptools'.
    Picked: setuptools = 0.6
    Installing 'spam'.
    We have the best distribution that satisfies 'spam'.
    Picked: spam = 2.
    Uninstalling foo.
    Installing foo.
    recipe v2

We'll get output that includes lines that tell us what versions
buildout chose a for us, like::

    zc.buildout.easy_install.picked: spam = 2

This allows us to discover versions that are picked dynamically, so
that we can fix them in a versions section.

If we run the buildout with the versions section:

    >>> print system(buildout+' -v'), # doctest: +ELLIPSIS
    Installing 'zc.buildout', 'setuptools'.
    We have a develop egg: zc.buildout 1.0.0.
    We have the best distribution that satisfies 'setuptools'.
    Picked: setuptools = 0.6
    Installing 'spam'.
    We have the distribution that satisfies 'spam==1'.
    Uninstalling foo.
    Installing foo.
    recipe v1

We won't get output for the spam distribution, which we didn't pick,
but we will get output for setuptools, which we didn't specify
versions for.

You can request buildout to generate an error if it picks any
versions:

    >>> write('buildout.cfg',
    ... '''
    ... [buildout]
    ... parts = foo
    ... find-links = %s
    ... versions = release-1
    ... allow-picked-versions = false
    ...
    ... [release-1]
    ... spam = 1
    ... eggs = 2.2
    ...
    ... [foo]
    ... recipe = spam
    ... ''' % join('recipe', 'dist'))
