Automatic Buildout Updates
==========================

NOTE: buildout 1.4.4 is a version that has been hacked to prefer itself, and
not upgrade.  It is intended as a way for people who have trouble with the new
1.5 line to easily keep from upgrading until they are ready.  In the future,
we suggest that you specify the versions of your dependencies using the
standard buildout mechanism
(http://pypi.python.org/pypi/zc.buildout#repeatable-buildouts-controlling-eggs-used).
However, for now, you can use 1.4.4 to easily control your dependencies.
When this file changes because of the hack, the documentation indicates this
with a "HACK" label.

When a buildout is run, one of the first steps performed is to check for
updates to either zc.buildout or setuptools (HACK: not zc.buildout).  To
demonstrate this, we've created some "new releases" of buildout and
setuptools in a new_releases folder:

    >>> ls(new_releases)
    d  setuptools
    -  setuptools-99.99-py2.4.egg
    d  zc.buildout
    -  zc.buildout-99.99-py2.4.egg

Let's update the sample buildout.cfg to look in this area:

    >>> write(sample_buildout, 'buildout.cfg',
    ... """
    ... [buildout]
    ... find-links = %(new_releases)s
    ... index = %(new_releases)s
    ... parts = show-versions
    ... develop = showversions
    ...
    ... [show-versions]
    ... recipe = showversions
    ... """ % dict(new_releases=new_releases))

We'll also include a recipe that echos the versions of setuptools and
zc.buildout used:

    >>> mkdir(sample_buildout, 'showversions')

    >>> write(sample_buildout, 'showversions', 'showversions.py',
    ... """
    ... import pkg_resources
    ...
    ... class Recipe:
    ...
    ...     def __init__(self, buildout, name, options):
    ...         pass
    ...
    ...     def install(self):
    ...         for project in 'zc.buildout', 'setuptools':
    ...             req = pkg_resources.Requirement.parse(project)
    ...             print project, pkg_resources.working_set.find(req).version
    ...         return ()
    ...     update = install
    ... """)


    >>> write(sample_buildout, 'showversions', 'setup.py',
    ... """
    ... from setuptools import setup
    ...
    ... setup(
    ...     name = "showversions",
    ...     entry_points = {'zc.buildout': ['default = showversions:Recipe']},
    ...     )
    ... """)


Now if we run the buildout, the buildout will upgrade itself to the
new versions found in new releases (HACK: only setuptools):

    >>> print system(buildout),
    Getting distribution for 'setuptools'.
    Got setuptools 99.99.
    Upgraded:
      setuptools version 99.99;
    restarting.
    Generated script '/sample-buildout/bin/buildout'.
    Develop: '/sample-buildout/showversions'
    Installing show-versions.
    zc.buildout 1.4.4
    setuptools 99.99

Our buildout script has been updated to use the new eggs (HACK: only for
setuptools):

    >>> cat(sample_buildout, 'bin', 'buildout')
    #!/usr/local/bin/python2.4
    <BLANKLINE>
    import sys
    sys.path[0:0] = [
      '/sample-buildout/eggs/zc.buildout-1.4.4-py2.4.egg',
      '/sample-buildout/eggs/setuptools-99.99-py2.4.egg',
      ]
    <BLANKLINE>
    import zc.buildout.buildout
    <BLANKLINE>
    if __name__ == '__main__':
        zc.buildout.buildout.main()

Now, let's recreate the sample buildout. If we specify constraints on
the versions of zc.buildout and setuptools to use, running the buildout
will install earlier versions of these packages (HACK: only setuptools):

    >>> write(sample_buildout, 'buildout.cfg',
    ... """
    ... [buildout]
    ... find-links = %(new_releases)s
    ... index = %(new_releases)s
    ... parts = show-versions
    ... develop = showversions
    ... zc.buildout-version = < 99
    ... setuptools-version = < 99
    ...
    ... [show-versions]
    ... recipe = showversions
    ... """ % dict(new_releases=new_releases))

Now we can see that we actually "upgrade" to an earlier version.

    >>> print system(buildout),
    Upgraded:
      setuptools version 0.6;
    restarting.
    Generated script '/sample-buildout/bin/buildout'.
    Develop: '/sample-buildout/showversions'
    Updating show-versions.
    zc.buildout 1.0.0
    setuptools 0.6

There are a number of cases, described below, in which the updates
don't happen.

We won't upgrade in offline mode:

    >>> write(sample_buildout, 'buildout.cfg',
    ... """
    ... [buildout]
    ... find-links = %(new_releases)s
    ... index = %(new_releases)s
    ... parts = show-versions
    ... develop = showversions
    ...
    ... [show-versions]
    ... recipe = showversions
    ... """ % dict(new_releases=new_releases))

    >>> print system(buildout+' -o'),
    Develop: '/sample-buildout/showversions'
    Updating show-versions.
    zc.buildout 1.0.0
    setuptools 0.6

Or in non-newest mode:

    >>> print system(buildout+' -N'),
    Develop: '/sample-buildout/showversions'
    Updating show-versions.
    zc.buildout 1.0.0
    setuptools 0.6

We also won't upgrade if the buildout script being run isn't in the
buildouts bin directory.  To see this we'll create a new buildout
directory:

    >>> sample_buildout2 = tmpdir('sample_buildout2')
    >>> write(sample_buildout2, 'buildout.cfg',
    ... """
    ... [buildout]
    ... find-links = %(new_releases)s
    ... index = %(new_releases)s
    ... parts =
    ... """ % dict(new_releases=new_releases))

    >>> cd(sample_buildout2)
    >>> print system(buildout),
    Creating directory '/sample_buildout2/bin'.
    Creating directory '/sample_buildout2/parts'.
    Creating directory '/sample_buildout2/eggs'.
    Creating directory '/sample_buildout2/develop-eggs'.
    Getting distribution for 'setuptools'.
    Got setuptools 99.99.
    Not upgrading because not running a local buildout command.

    >>> ls('bin')
